aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/common/logging/text_formatter.cpp
blob: 45be6d0a1b62f26c243a34868171f7295b99bc40 (plain)
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
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#include <array>
#include <cstdio>

#ifdef _WIN32
#   define WIN32_LEAN_AND_MEAN
#   include <Windows.h>
#endif

#include "common/logging/backend.h"
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"

#include "common/common_funcs.h"
#include "common/string_util.h"

namespace Log {

// TODO(bunnei): This should be moved to a generic path manipulation library
const char* TrimSourcePath(const char* path, const char* root) {
    const char* p = path;

    while (*p != '\0') {
        const char* next_slash = p;
        while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') {
            ++next_slash;
        }

        bool is_src = Common::ComparePartialString(p, next_slash, root);
        p = next_slash;

        if (*p != '\0') {
            ++p;
        }
        if (is_src) {
            path = p;
        }
    }
    return path;
}

void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len) {
    unsigned int time_seconds    = static_cast<unsigned int>(entry.timestamp.count() / 1000000);
    unsigned int time_fractional = static_cast<unsigned int>(entry.timestamp.count() % 1000000);

    const char* class_name = Logger::GetLogClassName(entry.log_class);
    const char* level_name = Logger::GetLevelName(entry.log_level);

    snprintf(out_text, text_len, "[%4u.%06u] %s <%s> %s: %s",
        time_seconds, time_fractional, class_name, level_name,
        TrimSourcePath(entry.location.c_str()), entry.message.c_str());
}

void PrintMessage(const Entry& entry) {
    std::array<char, 4 * 1024> format_buffer;
    FormatLogMessage(entry, format_buffer.data(), format_buffer.size());
    fputs(format_buffer.data(), stderr);
    fputc('\n', stderr);
}

void PrintColoredMessage(const Entry& entry) {
#ifdef _WIN32
    static HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE);

    CONSOLE_SCREEN_BUFFER_INFO original_info = {0};
    GetConsoleScreenBufferInfo(console_handle, &original_info);

    WORD color = 0;
    switch (entry.log_level) {
    case Level::Trace: // Grey
        color = FOREGROUND_INTENSITY; break;
    case Level::Debug: // Cyan
        color = FOREGROUND_GREEN | FOREGROUND_BLUE; break;
    case Level::Info: // Bright gray
        color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; break;
    case Level::Warning: // Bright yellow
        color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; break;
    case Level::Error: // Bright red
        color = FOREGROUND_RED | FOREGROUND_INTENSITY; break;
    case Level::Critical: // Bright magenta
        color = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY; break;
    }

    SetConsoleTextAttribute(console_handle, color);
#else
#   define ESC "\x1b"
    const char* color = "";
    switch (entry.log_level) {
    case Level::Trace: // Grey
        color = ESC "[1;30m"; break;
    case Level::Debug: // Cyan
        color = ESC "[0;36m"; break;
    case Level::Info: // Bright gray
        color = ESC "[0;37m"; break;
    case Level::Warning: // Bright yellow
        color = ESC "[1;33m"; break;
    case Level::Error: // Bright red
        color = ESC "[1;31m"; break;
    case Level::Critical: // Bright magenta
        color = ESC "[1;35m"; break;
    }

    fputs(color, stderr);
#endif

    PrintMessage(entry);

#ifdef _WIN32
    SetConsoleTextAttribute(console_handle, original_info.wAttributes);
#else
    fputs(ESC "[0m", stderr);
#   undef ESC
#endif
}

void TextLoggingLoop(std::shared_ptr<Logger> logger) {
    std::array<Entry, 256> entry_buffer;

    while (true) {
        size_t num_entries = logger->GetEntries(entry_buffer.data(), entry_buffer.size());
        if (num_entries == Logger::QUEUE_CLOSED) {
            break;
        }
        for (size_t i = 0; i < num_entries; ++i) {
            const Entry& entry = entry_buffer[i];
            PrintColoredMessage(entry);
        }
    }
}

}