File: logger.cpp

package info (click to toggle)
satdump 1.2.2%2Bgb79af48-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 81,648 kB
  • sloc: cpp: 276,768; ansic: 164,598; lisp: 1,219; sh: 283; xml: 106; makefile: 7
file content (348 lines) | stat: -rw-r--r-- 11,222 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
#define SATDUMP_DLL_EXPORT 1
#include "logger.h"
#include <iostream>
#include <ctime>
#include <algorithm>
#include <filesystem>
#ifdef __ANDROID__
#include <android/log.h>
static char ag_LogTag[] = "SatDump";
#endif
#if defined(_WIN32)
#include <windows.h>
#include <wincon.h>
#endif

#include "init.h"

// Logger and sinks. We got a console sink and file sink
#ifdef __ANDROID__
std::shared_ptr<slog::AndroidSink> console_sink;
#elif defined(_WIN32)
std::shared_ptr<slog::WinOutSink> console_sink;
#else
std::shared_ptr<slog::StdOutSink> console_sink;
#endif
std::shared_ptr<slog::FileSink> file_sink;
SATDUMP_DLL std::shared_ptr<slog::Logger> logger;

// We need a place to buffer log messages during initialization
// to make sure the messages end up in log sinks added later in
// the initialization process
std::vector<slog::LogMsg> init_log_buffer;
bool init_buffer_active = true;

namespace slog
{
    const std::string log_schar[] = {"T", "D", "I", "W", "E", "C"};
    const std::string colors[] = {"\033[37m",
                                  "\033[36m",
                                  "\033[32m",
                                  "\033[33m\033[1m",
                                  "\033[31m\033[1m",
                                  "\033[1m\033[41m"};

    template <typename... T>
    std::string vformat(const char *fmt, T &&...args)
    {
        // Allocate a buffer on the stack that's big enough for us almost
        // all the time.
        size_t size = 1024;
        std::vector<char> buf;
        buf.resize(size);

        // Try to vsnprintf into our buffer.
        size_t needed = snprintf((char *)&buf[0], size, fmt, args...);
        // NB. On Windows, vsnprintf returns -1 if the string didn't fit the
        // buffer.  On Linux & OSX, it returns the length it would have needed.

        if (needed <= size)
        {
            // It fit fine the first time, we're done.
            return std::string(&buf[0]);
        }
        else
        {
            // vsnprintf reported that it wanted to write more characters
            // than we allotted.  So do a malloc of the right size and try again.
            // This doesn't happen very often if we chose our initial size
            // well.
            size = needed;
            buf.resize(size);
            needed = snprintf((char *)&buf[0], size, fmt, args...);
            return std::string(&buf[0]);
        }
    }

    std::string LoggerSink::format_log(LogMsg m, bool color, int *cpos)
    {
        time_t ct = time(0);
        std::tm *tmr = gmtime(&ct);

        std::string timestamp =
            (tmr->tm_hour < 10 ? "0" : "") + std::to_string(tmr->tm_hour) + ":" +       // Hour
            (tmr->tm_min < 10 ? "0" : "") + std::to_string(tmr->tm_min) + ":" +         // Min
            (tmr->tm_sec < 10 ? "0" : "") + std::to_string(tmr->tm_sec) + " - " +       // Sec
            (tmr->tm_mday < 10 ? "0" : "") + std::to_string(tmr->tm_mday) + "/" +       // Day
            (tmr->tm_mon + 1 < 10 ? "0" : "") + std::to_string(tmr->tm_mon + 1) + "/" + // Mon
            (tmr->tm_year < 10 ? "0" : "") + std::to_string(tmr->tm_year + 1900);       // Year

        if (cpos != nullptr)
            *cpos = timestamp.size() + 3;

        if (color)
            return vformat("[%s] %s(%s) %s\033[m\n",
                           timestamp.c_str(),
                           colors[m.lvl].c_str(), log_schar[m.lvl].c_str(), m.str.c_str());
        else
            return vformat("[%s] (%s) %s\n",
                           timestamp.c_str(),
                           log_schar[m.lvl].c_str(), m.str.c_str());
    }

    void StdOutSink::receive(LogMsg log)
    {
        if (log.lvl >= sink_lvl)
        {
            std::string s = format_log(log, true);
            fwrite(s.c_str(), sizeof(char), s.size(), stderr);
        }
    }

#if defined(_WIN32)
    const int colors_win[] = {FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
                              FOREGROUND_GREEN | FOREGROUND_BLUE,
                              FOREGROUND_GREEN,
                              FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY,
                              FOREGROUND_RED | FOREGROUND_INTENSITY,
                              BACKGROUND_RED | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY};

    int win_set_foreground_color(HANDLE &out_handle_, int attribs)
    {
        CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info;
        if (!::GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info))
        {
            // just return white if failed getting console info
            return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
        }

        // change only the foreground bits (lowest 4 bits)
        auto new_attribs = static_cast<int>(attribs) | (orig_buffer_info.wAttributes & 0xfff0);
        auto ignored = ::SetConsoleTextAttribute(out_handle_, static_cast<WORD>(new_attribs));
        (void)(ignored);
        return orig_buffer_info.wAttributes; // return orig attribs
    }

    void WinOutSink::receive(LogMsg log)
    {
        if (log.lvl >= sink_lvl)
        {
            int color_pos = 0;
            std::string s = format_log(log, false, &color_pos);
            std::string s1 = s.substr(0, color_pos);
            std::string s2 = s.substr(color_pos, s.size() - color_pos - 1);
            std::string s3 = s.substr(s.size() - 1, 1);
            HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
            fwrite(s1.c_str(), sizeof(char), s1.size(), stderr);
            int rst = win_set_foreground_color(hConsole, colors_win[log.lvl]);
            fwrite(s2.c_str(), sizeof(char), s2.size(), stderr);
            SetConsoleTextAttribute(hConsole, rst);
            fwrite(s3.c_str(), sizeof(char), s3.size(), stderr);
        }
    }
#endif

    FileSink::FileSink(std::string path)
    {
        int suffix = 0;
        std::string log_path = path + ".log";
        while(std::filesystem::exists(log_path))
        {
            suffix++;
            log_path = path + "-" + std::to_string(suffix) + ".log";
        }

        outf = std::ofstream(log_path);
    }
    FileSink::~FileSink()
    {
        outf.close();
    }

    void FileSink::receive(LogMsg log)
    {
        if (log.lvl >= sink_lvl)
        {
            std::string s = format_log(log, false);
            outf.write(s.c_str(), s.size());
            outf.flush(); // Too annoying when there's a segault not letting it flush
        }
    }

    void Logger::log(LogLevel lvl, std::string v)
    {
        sink_mtx.lock();
        LogMsg m;
        m.str = v;
        m.lvl = lvl;

        // Push to init buffer to dump to sinks added later
        if (init_buffer_active)
        {
            // Once 1000 messages have been logged, assume all sinks have
            // been attached and we were not notified. Stop buffering
            if (init_log_buffer.size() >= 1000)
                completeLoggerInit();
            else
                init_log_buffer.push_back(m);
        }

        // Push to current sinks
        if (m.lvl >= logger_lvl)
            for (auto &l : sinks)
                l->receive(m);
        sink_mtx.unlock();
    }

    void Logger::logf(LogLevel lvl, std::string fmt, va_list args)
    {
        std::string output;
        output.resize(1024);

        va_list argscopy;
        va_copy(argscopy, args);

        size_t size = vsnprintf((char *)output.c_str(), output.size(), fmt.c_str(), args);

        if (output.size() <= size)
        {
            log(lvl, output);
        }
        else
        {
            output.resize(size + 1);
            size = vsnprintf((char *)output.c_str(), output.size(), fmt.c_str(), argscopy);
            log(lvl, output);
        }
    }

#ifdef __ANDROID__
    const android_LogPriority log_lvls_a[] = {
        ANDROID_LOG_VERBOSE,
        ANDROID_LOG_DEBUG,
        ANDROID_LOG_INFO,
        ANDROID_LOG_WARN,
        ANDROID_LOG_ERROR,
        ANDROID_LOG_FATAL,
    };

    void AndroidSink::receive(LogMsg log)
    {
        if (log.lvl >= sink_lvl)
        {
            std::string s = format_log(log, false);
            __android_log_print(log_lvls_a[log.lvl], ag_LogTag, "%s", log.str.c_str());
        }
    }
#endif

    void Logger::set_level(LogLevel lvl)
    {
        logger_lvl = lvl;
    }

    void Logger::add_sink(std::shared_ptr<LoggerSink> sink)
    {
        sink_mtx.lock();
        for (LogMsg& msg : init_log_buffer)
            sink->receive(msg);
        sinks.push_back(sink);
        sink_mtx.unlock();
    }

    void Logger::del_sink(std::shared_ptr<LoggerSink> sink)
    {
        sink_mtx.lock();
        auto it = std::find_if(sinks.begin(), sinks.end(), [&sink](std::shared_ptr<LoggerSink>& c)
            { return sink.get() == c.get(); });
        if (it != sinks.end())
            sinks.erase(it);
        sink_mtx.unlock();
    }
}

void initLogger()
{
    try
    {
// Initialize everything
#ifdef __ANDROID__
        console_sink = std::make_shared<slog::AndroidSink>();
#elif defined(_WIN32)
        console_sink = std::make_shared<slog::WinOutSink>();
#else
        console_sink = std::make_shared<slog::StdOutSink>();
#endif

        logger = std::make_shared<slog::Logger>();
        logger->set_level(slog::LOG_TRACE);
        console_sink->set_level(slog::LOG_TRACE);
        logger->add_sink(console_sink);
    }
    catch (std::exception &e)
    {
        std::cout << e.what() << std::endl;
        exit(1);
    }
}

void initFileSink()
{
    try
    {
        //Make sure the user folder is created
        if (!std::filesystem::exists(std::filesystem::path(satdump::user_path + "/")))
            std::filesystem::create_directories(std::filesystem::path(satdump::user_path + "/"));

        //Clear old logs
        char timebuffer[16];
        struct tm *timeinfo;
        time_t current_time = time(NULL);
        timeinfo = localtime(&current_time);
        strftime(timebuffer, sizeof(timebuffer), "%Y%m%d", timeinfo);
        int delete_before = atoi(timebuffer) - 2;
        for (const auto& user_file : std::filesystem::directory_iterator(satdump::user_path + "/"))
        {
            std::string file_name = user_file.path().filename().string();
            if (file_name.size() < 26)
                continue;
            int log_date = atoi(file_name.substr(8, 8).c_str());
            if (log_date != 0 && log_date < delete_before)
                std::filesystem::remove(user_file);
        }

        //Create new log
        strftime(timebuffer, sizeof(timebuffer), "%Y%m%dT%H%M%S", timeinfo);
        file_sink = std::make_shared<slog::FileSink>(satdump::user_path + "/satdump-" + std::string(timebuffer));
        logger->add_sink(file_sink);
        // file_sink->set_pattern("[%D - %T] (%L) %v");
        file_sink->set_level(slog::LOG_TRACE);
    }
    catch (std::exception &e)
    {
        logger->error("Error initializing log file: %s", e.what());
        return;
    }
}

void completeLoggerInit()
{
    init_log_buffer.clear();
    init_buffer_active = false;
}

void setConsoleLevel(slog::LogLevel level)
{
    // Just change our log level
    console_sink->set_level(level);
}