summaryrefslogtreecommitdiff
path: root/lib/spdlog/sinks/wincolor_sink.h
blob: 402fa1217c97bf81b2d91e132697cba3074e5488 (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
135
136
137
138
139
140
141
142
143
144
//
// Copyright(c) 2016 spdlog
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//

#pragma once

#include "../common.h"
#include "../details/null_mutex.h"
#include "base_sink.h"

#include <mutex>
#include <string>
#include <unordered_map>
#include <wincon.h>

namespace spdlog {
namespace sinks {
/*
 * Windows color console sink. Uses WriteConsoleA to write to the console with colors
 */
template<class Mutex>
class wincolor_sink : public base_sink<Mutex>
{
public:
    const WORD BOLD = FOREGROUND_INTENSITY;
    const WORD RED = FOREGROUND_RED;
    const WORD GREEN = FOREGROUND_GREEN;
    const WORD CYAN = FOREGROUND_GREEN | FOREGROUND_BLUE;
    const WORD WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
    const WORD YELLOW = FOREGROUND_RED | FOREGROUND_GREEN;

    wincolor_sink(HANDLE std_handle)
        : out_handle_(std_handle)
    {
        colors_[level::trace] = WHITE;
        colors_[level::debug] = CYAN;
        colors_[level::info] = GREEN;
        colors_[level::warn] = YELLOW | BOLD;
        colors_[level::err] = RED | BOLD;                         // red bold
        colors_[level::critical] = BACKGROUND_RED | WHITE | BOLD; // white bold on red background
        colors_[level::off] = 0;
    }

    ~wincolor_sink() override
    {
        this->flush();
    }

    wincolor_sink(const wincolor_sink &other) = delete;
    wincolor_sink &operator=(const wincolor_sink &other) = delete;

    // change the color for the given level
    void set_color(level::level_enum level, WORD color)
    {
        std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
        colors_[level] = color;
    }

protected:
    void _sink_it(const details::log_msg &msg) override
    {
        if (msg.color_range_end > msg.color_range_start)
        {
            // before color range
            _print_range(msg, 0, msg.color_range_start);

            // in color range
            auto orig_attribs = set_console_attribs(colors_[msg.level]);
            _print_range(msg, msg.color_range_start, msg.color_range_end);
            ::SetConsoleTextAttribute(out_handle_, orig_attribs); // reset to orig colors
            // after color range
            _print_range(msg, msg.color_range_end, msg.formatted.size());
        }
        else // print without colors if color range is invalid
        {
            _print_range(msg, 0, msg.formatted.size());
        }
    }

    void _flush() override
    {
        // windows console always flushed?
    }

private:
    HANDLE out_handle_;
    std::unordered_map<level::level_enum, WORD, level::level_hasher> colors_;

    // set color and return the orig console attributes (for resetting later)
    WORD set_console_attribs(WORD attribs)
    {
        CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info;
        GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info);
        WORD back_color = orig_buffer_info.wAttributes;
        // retrieve the current background color
        back_color &= static_cast<WORD>(~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY));
        // keep the background color unchanged
        SetConsoleTextAttribute(out_handle_, attribs | back_color);
        return orig_buffer_info.wAttributes; // return orig attribs
    }

    // print a range of formatted message to console
    void _print_range(const details::log_msg &msg, size_t start, size_t end)
    {
        DWORD size = static_cast<DWORD>(end - start);
        WriteConsoleA(out_handle_, msg.formatted.data() + start, size, nullptr, nullptr);
    }
};

//
// windows color console to stdout
//
template<class Mutex>
class wincolor_stdout_sink : public wincolor_sink<Mutex>
{
public:
    wincolor_stdout_sink()
        : wincolor_sink<Mutex>(GetStdHandle(STD_OUTPUT_HANDLE))
    {
    }
};

using wincolor_stdout_sink_mt = wincolor_stdout_sink<std::mutex>;
using wincolor_stdout_sink_st = wincolor_stdout_sink<details::null_mutex>;

//
// windows color console to stderr
//
template<class Mutex>
class wincolor_stderr_sink : public wincolor_sink<Mutex>
{
public:
    wincolor_stderr_sink()
        : wincolor_sink<Mutex>(GetStdHandle(STD_ERROR_HANDLE))
    {
    }
};

using wincolor_stderr_sink_mt = wincolor_stderr_sink<std::mutex>;
using wincolor_stderr_sink_st = wincolor_stderr_sink<details::null_mutex>;

} // namespace sinks
} // namespace spdlog