IRSOL
C++ code implementing socket server for interacting with Baumer camera.
logging.cpp
Go to the documentation of this file.
1#include "irsol/logging.hpp"
2
3#include <spdlog/sinks/basic_file_sink.h>
4#include <spdlog/sinks/rotating_file_sink.h>
5#include <spdlog/sinks/stdout_color_sinks.h>
6
7namespace irsol {
8namespace internal {
9std::unordered_map<std::string, LoggerInfo> NamedLoggerRegistry::m_loggers;
10
11spdlog::logger*
12NamedLoggerRegistry::getLogger(const std::string& name)
13{
14 spdlog::logger* result;
15 auto it = m_loggers.find(name);
16 if(it != m_loggers.end()) {
17 result = it->second.logger.get();
18 it->second.lastRetrieved = irsol::types::clock_t::now();
19 } else {
20 auto newLogger = spdlog::default_logger()->clone(name);
21
22#ifdef DEBUG
23 // Extract the current logFilePath from the default logger
24 auto& all_sinks = newLogger->sinks();
25 // Find a logger of type rotating_file_sink_mt and determine the logFilePath
26 // for the named logger.
27 bool fileSinkFound = false;
28 std::string newLogFilePath;
29 for(auto& existing_sink : all_sinks) {
30 if(
31 spdlog::sinks::rotating_file_sink_mt* rotating_sink =
32 dynamic_cast<spdlog::sinks::rotating_file_sink_mt*>(existing_sink.get())) {
33 std::string logFilePath = rotating_sink->filename();
34 // strip the suffix from the logFilePath
35 size_t pos = logFilePath.rfind('.');
36 if(pos != std::string::npos) {
37 logFilePath = logFilePath.substr(0, pos);
38 }
39 newLogFilePath = logFilePath + "_" + name + ".log";
40 fileSinkFound = true;
41 break;
42 }
43 }
44 if(!fileSinkFound) {
45 newLogFilePath = name + ".log";
46 }
47
48 // Also create a new named sink for debug mode
49 const auto maxFileSize = 1024 * 1024 * 5; // 5 MB
50 const auto maxFiles = 24; // Keep 24 rotated files
51 auto fileSink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
52 newLogFilePath, maxFileSize, maxFiles, false);
53
54 // Add the dedicated file sink to the named logger
55 newLogger->sinks().push_back(fileSink);
56#endif
57 LoggerInfo info = {newLogger, irsol::types::clock_t::now()};
58 m_loggers[name] = info;
59 result = newLogger.get();
60 }
61
62 // If there's more than 256 loggers, delete the oldest one
63 if(m_loggers.size() > 256) {
64 IRSOL_LOG_INFO("Automatic deletion of old named loggers.");
65 auto oldestLogger =
66 std::min_element(m_loggers.begin(), m_loggers.end(), [](const auto& a, const auto& b) {
67 return a.second.lastRetrieved < b.second.lastRetrieved;
68 });
69 m_loggers.erase(oldestLogger);
70 }
71 return result;
72}
73
74} // namespace internal
75
76void
77setLoggerName(const char* name)
78{
79 auto logger = spdlog::default_logger()->clone(name);
80 spdlog::set_default_logger(logger);
81}
82
83void
84setLoggingFormat(LoggingFormat format, std::optional<std::shared_ptr<spdlog::logger>> logger)
85{
86 auto thisLogger = logger.value_or(spdlog::default_logger());
87 for(auto& sink : thisLogger->sinks()) {
88 setSinkLoggingFormat(format, sink);
89 }
90}
91void
92setSinkLoggingFormat(LoggingFormat format, std::shared_ptr<spdlog::sinks::sink> sink)
93{
94 switch(format) {
96 sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%^%l%$][%n][pid %P][tid %t][%s:%!():%#] %v");
97 break;
99 sink->set_pattern("[%H:%M:%S.%e][%^%l%$][%n][%s:%!():%#] %v");
100 break;
102 sink->set_pattern("[%^%l%$][%s:%!():%#] %v");
103 break;
104 }
105}
106
107void
108initLogging(const char* logFilePath, std::optional<spdlog::level::level_enum> minLogLevel)
109{
110#ifdef DEBUG
111 // Set the logging level to debug if in debug mode
112 const auto defaultConsoleLevel = spdlog::level::trace;
113 const auto defaultFileSinkLevel = spdlog::level::trace;
114#else
115 // Set the logging level to info if not in debug mode
116 const auto defaultConsoleLevel = spdlog::level::info;
117 const auto defaultFileSinkLevel = spdlog::level::info;
118#endif
119
120 auto consoleSink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
122
123 const auto maxFileSize = 1024 * 1024 * 5; // 5 MB
124 const auto maxFiles = 24; // Keep 24 rotated files
125 auto fileSink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
126 logFilePath, maxFileSize, maxFiles, false);
128
129 // Set the loggers as default loggers
130 auto logger =
131 std::make_shared<spdlog::logger>("irsol", spdlog::sinks_init_list{consoleSink, fileSink});
132 spdlog::set_default_logger(logger);
133
134#ifdef DEBUG
135 // Force flush on trace level and above
136 spdlog::flush_on(spdlog::level::trace);
137#else
138 // Force flush on error level and above
139 spdlog::flush_on(spdlog::level::err);
140#endif
141
142 // Force flush every N seconds
143 spdlog::flush_every(std::chrono::seconds(2));
144
145 // Configure the levels for logging on the different sinks
146 auto consoleLevel = defaultConsoleLevel;
147 auto fileSinkLevel = defaultFileSinkLevel;
148 if(minLogLevel.has_value()) {
149 // Override the console and file levels if needed
150 consoleLevel = std::max({defaultConsoleLevel, *minLogLevel});
151 fileSinkLevel = std::max({defaultFileSinkLevel, *minLogLevel});
152 }
153 auto globalLevel = std::min({consoleLevel, fileSinkLevel});
154
155 consoleSink->set_level(consoleLevel);
156 fileSink->set_level(fileSinkLevel);
157 logger->set_level(globalLevel);
158
159#ifdef DEBUG
160 spdlog::info("Logging initialized with sync levels");
161 spdlog::info("Console {}", spdlog::level::to_string_view(consoleLevel));
162 spdlog::info("File: {}", spdlog::level::to_string_view(fileSinkLevel));
163 if(minLogLevel.has_value()) {
164 spdlog::info("Global: {} (overridden)", spdlog::level::to_string_view(minLogLevel.value()));
165 } else {
166 spdlog::info("Global: {}", spdlog::level::to_string_view(globalLevel));
167 }
168#endif
169}
170} // namespace irsol
static std::unordered_map< std::string, LoggerInfo > m_loggers
Internal logger registry.
Definition logging.hpp:130
static spdlog::logger * getLogger(const std::string &name)
Retrieves a logger by name from the registry.
Definition logging.cpp:12
#define IRSOL_LOG_INFO(...)
Logs an info-level message using the default logger.
Definition logging.hpp:92
LoggingFormat
Enum representing supported logging output formats.
Definition logging.hpp:192
void setSinkLoggingFormat(LoggingFormat format, std::shared_ptr< spdlog::sinks::sink > sink)
Sets the logging format for a specific sink.
Definition logging.cpp:92
void setLoggerName(const char *name)
Sets the name of the default logger.
Definition logging.cpp:77
void initLogging(const char *fileSinkFilename="logs/irsol.log", std::optional< spdlog::level::level_enum > minLogLevel=std::nullopt)
Initializes the irsol logging system.
Definition logging.cpp:108
void setLoggingFormat(LoggingFormat format=LoggingFormat::FILE, std::optional< std::shared_ptr< spdlog::logger > > logger=std::nullopt)
Sets the global logging format.
Definition logging.cpp:84
@ CONSOLE
Human-readable format for terminal output.
@ UNIT_TESTS
Format suitable for unit test frameworks.
@ FILE
Persistent file logging format.
Logging utilities and configuration for the irsol library.
Metadata for a named logger instance.
Definition logging.hpp:105
auto result