summaryrefslogtreecommitdiff
path: root/matching/include/spdlog/sinks/wincolor_sink.h
blob: 1fdf8c566f25ecfccec71a91f2c015f7a3454a10 (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
//
// Copyright(c) 2016 spdlog
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//

#pragma once

#ifndef SPDLOG_H
#include "spdlog/spdlog.h"
#endif

#include "spdlog/common.h"
#include "spdlog/details/console_globals.h"
#include "spdlog/details/null_mutex.h"
#include "spdlog/sinks/sink.h"

#include <memory>
#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<typename OutHandle, typename ConsoleMutex>
class wincolor_sink : public sink
{
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()
        : out_handle_(OutHandle::handle())
        , mutex_(ConsoleMutex::mutex())
    {
        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_t> lock(mutex_);
        colors_[level] = color;
    }

    void log(const details::log_msg &msg) final override
    {
        std::lock_guard<mutex_t> lock(mutex_);
        fmt::memory_buffer formatted;
        formatter_->format(msg, formatted);
        if (msg.color_range_end > msg.color_range_start)
        {
            // before color range
            print_range_(formatted, 0, msg.color_range_start);

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

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

    void set_pattern(const std::string &pattern) override final
    {
        std::lock_guard<mutex_t> lock(mutex_);
        formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
    }

    void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override final
    {
        std::lock_guard<mutex_t> lock(mutex_);
        formatter_ = std::move(sink_formatter);
    }

private:
    using mutex_t = typename ConsoleMutex::mutex_t;
    // 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 fmt::memory_buffer &formatted, size_t start, size_t end)
    {
        auto size = static_cast<DWORD>(end - start);
        ::WriteConsoleA(out_handle_, formatted.data() + start, size, nullptr, nullptr);
    }

    HANDLE out_handle_;
    mutex_t &mutex_;
    std::unordered_map<level::level_enum, WORD, level::level_hasher> colors_;
};

using wincolor_stdout_sink_mt = wincolor_sink<details::console_stdout, details::console_mutex>;
using wincolor_stdout_sink_st = wincolor_sink<details::console_stdout, details::console_nullmutex>;

using wincolor_stderr_sink_mt = wincolor_sink<details::console_stderr, details::console_mutex>;
using wincolor_stderr_sink_st = wincolor_sink<details::console_stderr, details::console_nullmutex>;

} // namespace sinks
} // namespace spdlog