SNode.C
Loading...
Searching...
No Matches
Logger.cpp
Go to the documentation of this file.
1/*
2 * SNode.C - A Slim Toolkit for Network Communication
3 * Copyright (C) Volker Christian <me@vchrist.at>
4 * 2020, 2021, 2022, 2023, 2024, 2025, 2026
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20/*
21 * MIT License
22 *
23 * Permission is hereby granted, free of charge, to any person obtaining a copy
24 * of this software and associated documentation files (the "Software"), to deal
25 * in the Software without restriction, including without limitation the rights
26 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27 * copies of the Software, and to permit persons to whom the Software is
28 * furnished to do so, subject to the following conditions:
29 *
30 * The above copyright notice and this permission notice shall be included in
31 * all copies or substantial portions of the Software.
32 *
33 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
39 * THE SOFTWARE.
40 */
41
42#ifndef DOXYGEN_SHOULD_SKIP_THIS
43
44#include "log/Logger.h"
45
46#include <algorithm>
47#include <cerrno>
48#include <chrono>
49#include <cstring>
50#include <spdlog/logger.h>
51#include <spdlog/pattern_formatter.h>
52#include <spdlog/sinks/basic_file_sink.h>
53#include <spdlog/sinks/rotating_file_sink.h>
54#include <spdlog/sinks/stdout_color_sinks.h>
55#include <unistd.h>
56
57#endif /* DOXYGEN_SHOULD_SKIP_THIS */
58
59namespace {
64
67 bool quietMode = false;
69
70 using Clock = std::chrono::steady_clock;
71 Clock::time_point startTime = Clock::now();
72
73 std::string defaultTick();
74
75 class TickFlagFormatter final : public spdlog::custom_flag_formatter {
76 public:
77 void format(const spdlog::details::log_msg&, const std::tm&, spdlog::memory_buf_t& dest) override {
78 std::string tick = tickResolver ? tickResolver() : defaultTick();
79 dest.append(tick.data(), tick.data() + static_cast<std::ptrdiff_t>(tick.size()));
80 }
81
83 return spdlog::details::make_unique<TickFlagFormatter>();
84 }
85 };
86
87 std::string defaultTick() {
88 const auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(Clock::now() - startTime).count();
89 std::string tick = std::to_string(elapsed);
90 if (tick.size() < 13) {
91 tick.insert(0, 13 - tick.size(), '0');
92 }
93 return tick;
94 }
95
96 std::string levelName(const logger::Level level) {
97 std::string result;
98
99 switch (level) {
101 result = "TRACE ";
102 break;
104 result = "DEBUG ";
105 break;
107 result = "INFO ";
108 break;
110 result = "WARNING";
111 break;
113 result = "ERROR ";
114 break;
116 result = "FATAL ";
117 break;
119 result = "VERBOSE";
120 break;
121 }
122
123 return result;
124 }
125
128
129 switch (level) {
131 color = Color::Code::FG_MAGENTA; // +
132 break;
134 color = Color::Code::FG_LIGHT_GREEN; // +
135 break;
137 color = Color::Code::FG_LIGHT_YELLOW; // +
138 break;
140 color = Color::Code::FG_YELLOW; // +
141 break;
143 color = Color::Code::FG_RED; // +
144 break;
146 color = Color::Code::FG_LIGHT_RED; // +
147 break;
149 color = Color::Code::FG_WHITE;
150 break;
151 }
152
153 return color;
154 }
155
156 std::string colorizeLevel(const logger::Level level) {
157 std::string label = levelName(level);
158
161 }
162
163 return label;
164 }
165
166 bool shouldEmit(const logger::Level level) {
167 if (level == logger::Level::VERBOSE) {
168 return true;
169 }
170
171 switch (configuredLogLevel) {
172 case 6:
173 return true;
174 case 5:
175 return level != logger::Level::TRACE;
176 case 4:
177 return level == logger::Level::INFO || level == logger::Level::WARNING || level == logger::Level::ERROR ||
178 level == logger::Level::FATAL;
179 case 3:
180 return level == logger::Level::WARNING || level == logger::Level::ERROR || level == logger::Level::FATAL;
181 case 2:
182 return level == logger::Level::ERROR || level == logger::Level::FATAL;
183 case 1:
184 return level == logger::Level::FATAL;
185 default:
186 return false;
187 }
188 }
189} // namespace
190
191namespace logger {
192
193 void Logger::init() {
194 startTime = Clock::now();
195 stdoutSink = std::make_shared<spdlog::sinks::stdout_color_sink_st>();
196 stdoutLogger = std::make_shared<spdlog::logger>("snodec-stdout", stdoutSink);
197 auto stdoutPattern = std::make_unique<spdlog::pattern_formatter>();
198 stdoutPattern->add_flag<TickFlagFormatter>('*').set_pattern("%Y-%m-%d %H:%M:%S %* %v");
199 stdoutLogger->set_formatter(std::move(stdoutPattern));
200
201 fileSink.reset();
202 fileLogger.reset();
203 quietMode = false;
204 disableColorLog = ::isatty(::fileno(stdout)) == 0;
207 }
208
210 tickResolver = std::move(resolver);
211 }
212
213 void Logger::setLogLevel(int level) {
214 configuredLogLevel = level;
215 }
216
217 void Logger::setVerboseLevel(int level) {
218 configuredVerboseLevel = std::max(0, level);
219 }
220
221 void Logger::logToFile(const std::string& logFile) {
222 constexpr std::size_t maxSize = 2 * 1024 * 1024; // 2 MiB
223 constexpr std::size_t maxFiles = 3; // keep log, log.1, log.2, log.3
224 fileSink = std::make_shared<spdlog::sinks::rotating_file_sink_st>(logFile, maxSize, maxFiles);
225 fileLogger = std::make_shared<spdlog::logger>("snodec-file", fileSink);
226 auto filePattern = std::make_unique<spdlog::pattern_formatter>();
227 filePattern->add_flag<TickFlagFormatter>('*').set_pattern("%Y-%m-%d %H:%M:%S %* %v");
228 fileLogger->set_formatter(std::move(filePattern));
229 }
230
232 fileLogger.reset();
233 fileSink.reset();
234 }
235
236 void Logger::setQuiet(bool quiet) {
237 quietMode = quiet;
238 }
239
240 void Logger::setDisableColor(bool disableColor) {
241 disableColorLog = disableColor;
242 }
243
245 return disableColorLog;
246 }
247
248 bool Logger::shouldLog(Level level) {
249 return shouldEmit(level);
250 }
251
252 bool Logger::shouldVerbose(int verboseLevel) {
253 return verboseLevel >= 0 && verboseLevel <= configuredVerboseLevel;
254 }
255
256 static void emitLine(Level level, std::string message, const bool withErrno, const int errnoValue) {
257 if (!shouldEmit(level)) {
258 return;
259 }
260
261 if (withErrno) {
262 message += ": ";
263 message += std::strerror(errnoValue);
264 }
265
266 if (level != Level::VERBOSE) {
267 message = colorizeLevel(level) + " " + message;
268 }
269
270 if (!quietMode && stdoutLogger) {
271 stdoutLogger->log(spdlog::level::info, message);
272 }
273 if (fileLogger) {
274 fileLogger->log(spdlog::level::info, message);
275 }
276 }
277
278 bool Logger::disableColorLog = false;
279
280 LogMessage::LogMessage(const Level level, const int verboseLevel, const bool withErrno)
281 : level(level)
282 , verboseLevel(verboseLevel)
283 , withErrno(withErrno)
284 , enabled(true)
285 , errnoValue(errno) {
286 }
287
293
294 std::ostringstream& LogMessage::stream() {
295 return message;
296 }
297
298} // namespace logger
299
300std::ostream& Color::operator<<(std::ostream& os, const Code& code) {
301 return os << (!logger::Logger::disableColorLog ? ("\033[" + std::to_string(static_cast<int>(code)) + "m") : "");
302}
303
304std::string Color::operator+(const std::string& string, const Code& code) {
305 return string + (!logger::Logger::disableColorLog ? ("\033[" + std::to_string(static_cast<int>(code)) + "m") : "");
306}
307
308std::string Color::operator+(const Code& code, const std::string& string) {
309 return (!logger::Logger::disableColorLog ? ("\033[" + std::to_string(static_cast<int>(code)) + "m") : "") + string;
310}
void format(const spdlog::details::log_msg &, const std::tm &, spdlog::memory_buf_t &dest) override
Definition Logger.cpp:77
std::unique_ptr< custom_flag_formatter > clone() const override
Definition Logger.cpp:82
LogMessage(Level level, int verboseLevel=-1, bool withErrno=false)
Definition Logger.cpp:280
std::ostringstream message
Definition Logger.h:133
std::ostringstream & stream()
Definition Logger.cpp:294
static bool disableColorLog
Definition Logger.h:113
static void setTickResolver(TickResolver resolver)
Definition Logger.cpp:209
static void setDisableColor(bool disableColorLog=true)
Definition Logger.cpp:240
static void logToFile(const std::string &logFile)
Definition Logger.cpp:221
static void setVerboseLevel(int level)
Definition Logger.cpp:217
static void setLogLevel(int level)
Definition Logger.cpp:213
std::function< std::string()> TickResolver
Definition Logger.h:94
static void init()
Definition Logger.cpp:193
static void setQuiet(bool quiet=true)
Definition Logger.cpp:236
static bool shouldVerbose(int verboseLevel)
Definition Logger.cpp:252
static bool getDisableColor()
Definition Logger.cpp:244
static bool shouldLog(Level level)
Definition Logger.cpp:248
static void disableLogToFile()
Definition Logger.cpp:231
Definition Logger.h:55
Code
Definition Logger.h:57
@ FG_LIGHT_YELLOW
Definition Logger.h:70
@ FG_LIGHT_GREEN
Definition Logger.h:69
@ FG_LIGHT_RED
Definition Logger.h:68
@ FG_YELLOW
Definition Logger.h:62
@ FG_DEFAULT
Definition Logger.h:58
@ FG_MAGENTA
Definition Logger.h:64
std::string operator+(const std::string &string, const Code &code)
Definition Logger.cpp:304
std::string operator+(const Code &code, const std::string &string)
Definition Logger.cpp:308
std::shared_ptr< spdlog::sinks::rotating_file_sink_st > fileSink
Definition Logger.cpp:61
std::string colorizeLevel(const logger::Level level)
Definition Logger.cpp:156
bool shouldEmit(const logger::Level level)
Definition Logger.cpp:166
std::string levelName(const logger::Level level)
Definition Logger.cpp:96
Clock::time_point startTime
Definition Logger.cpp:71
std::chrono::steady_clock Clock
Definition Logger.cpp:70
std::shared_ptr< spdlog::logger > stdoutLogger
Definition Logger.cpp:62
std::shared_ptr< spdlog::sinks::stdout_color_sink_st > stdoutSink
Definition Logger.cpp:60
logger::Logger::TickResolver tickResolver
Definition Logger.cpp:68
std::shared_ptr< spdlog::logger > fileLogger
Definition Logger.cpp:63
Color::Code levelColor(const logger::Level level)
Definition Logger.cpp:126
Level
Definition Logger.h:90
static void emitLine(Level level, std::string message, const bool withErrno, const int errnoValue)
Definition Logger.cpp:256