CppNCorr
C++ ncorr Digital Image Correlation engine
Loading...
Searching...
No Matches
log.cpp
Go to the documentation of this file.
1
6#include "ncorr/log.h"
7
8#include <algorithm>
9#include <chrono>
10#include <cstdlib>
11#include <cstring>
12#include <ctime>
13#include <iostream>
14
15#if defined(_WIN32)
16#include <io.h>
17#define NCORR_ISATTY(fd) _isatty(fd)
18#define NCORR_FILENO(f) _fileno(f)
19#else
20#include <unistd.h>
21#define NCORR_ISATTY(fd) ::isatty(fd)
22#define NCORR_FILENO(f) ::fileno(f)
23#endif
24
25namespace ncorr {
26namespace log {
27
28namespace {
29
31std::string to_lower(std::string s) {
32 std::transform(s.begin(), s.end(), s.begin(),
33 [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
34 return s;
35}
36
38const char* base_name(const char* path) {
39 if (!path) return "";
40 const char* base = path;
41 for (const char* p = path; *p; ++p) {
42 if (*p == '/' || *p == '\\') base = p + 1;
43 }
44 return base;
45}
46
48const char* color_for(Level l) {
49 switch (l) {
50 case Level::Trace:
51 return "\033[37m"; // grey
52 case Level::Debug:
53 return "\033[36m"; // cyan
54 case Level::Info:
55 return "\033[32m"; // green
56 case Level::Warn:
57 return "\033[33m"; // yellow
58 case Level::Error:
59 return "\033[31m"; // red
60 default:
61 return "";
62 }
63}
64const char* color_reset() {
65 return "\033[0m";
66}
67
69std::string timestamp() {
70 using namespace std::chrono;
71 const auto now = system_clock::now();
72 const auto t = system_clock::to_time_t(now);
73 const auto ms = duration_cast<milliseconds>(now.time_since_epoch()).count() % 1000;
74 std::tm tm_buf{};
75#if defined(_WIN32)
76 localtime_s(&tm_buf, &t);
77#else
78 localtime_r(&t, &tm_buf);
79#endif
80 char buf[32];
81 std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm_buf);
82 char out[40];
83 std::snprintf(out, sizeof(out), "%s.%03d", buf, static_cast<int>(ms));
84 return out;
85}
86
88std::string rtrim(const std::string& s) {
89 std::size_t end = s.size();
90 while (end > 0) {
91 const char c = s[end - 1];
92 if (c == '\n' || c == '\r' || c == ' ' || c == '\t')
93 --end;
94 else
95 break;
96 }
97 return s.substr(0, end);
98}
99
100} // namespace
101
102Level level_from_string(const std::string& s, Level fallback) {
103 const std::string v = to_lower(s);
104 if (v == "trace") return Level::Trace;
105 if (v == "debug") return Level::Debug;
106 if (v == "info") return Level::Info;
107 if (v == "warn" || v == "warning") return Level::Warn;
108 if (v == "error" || v == "err") return Level::Error;
109 if (v == "off" || v == "none" || v == "silent") return Level::Off;
110 return fallback;
111}
112
113const char* level_name(Level l) {
114 switch (l) {
115 case Level::Trace:
116 return "TRACE";
117 case Level::Debug:
118 return "DEBUG";
119 case Level::Info:
120 return "INFO ";
121 case Level::Warn:
122 return "WARN ";
123 case Level::Error:
124 return "ERROR";
125 case Level::Off:
126 return "OFF ";
127 }
128 return "?????";
129}
130
132 static Logger logger;
133 return logger;
134}
135
136Logger::Logger() {
137 color_ = NCORR_ISATTY(NCORR_FILENO(stderr)) != 0;
138}
139
140void Logger::ensure_env() {
141 if (!env_done_) {
142 env_done_ = true;
144 }
145}
146
148 if (const char* lvl = std::getenv("NCORR_LOG_LEVEL")) {
149 console_level_ = level_from_string(lvl, console_level_);
150 }
151 if (const char* con = std::getenv("NCORR_LOG_CONSOLE")) {
152 const std::string v = to_lower(con);
153 console_enabled_ = !(v == "0" || v == "false" || v == "off" || v == "no");
154 }
155 if (const char* path = std::getenv("NCORR_LOG_FILE")) {
156 if (path[0] != '\0') {
157 file_.open(path, std::ios::out | std::ios::app);
158 }
159 }
160}
161
163 std::lock_guard<std::mutex> lk(mtx_);
164 console_level_ = l;
165}
166
168 std::lock_guard<std::mutex> lk(mtx_);
169 file_level_ = l;
170}
171
173 std::lock_guard<std::mutex> lk(mtx_);
174 return console_level_;
175}
176
178 std::lock_guard<std::mutex> lk(mtx_);
179 return file_level_;
180}
181
182bool Logger::set_file(const std::string& path) {
183 std::lock_guard<std::mutex> lk(mtx_);
184 if (file_.is_open()) file_.close();
185 if (path.empty()) return true;
186 file_.open(path, std::ios::out | std::ios::app);
187 return file_.is_open();
188}
189
191 std::lock_guard<std::mutex> lk(mtx_);
192 console_enabled_ = on;
193}
194
196 std::lock_guard<std::mutex> lk(mtx_);
197 verbose_format_ = on;
198}
199
201 std::lock_guard<std::mutex> lk(mtx_);
202 ensure_env();
203 if (l == Level::Off) return false;
204 const bool to_console = console_enabled_ && l >= console_level_;
205 const bool to_file = file_.is_open() && l >= file_level_;
206 return to_console || to_file;
207}
208
209void Logger::write(Level l, const char* file, int line, const std::string& msg) {
210 std::lock_guard<std::mutex> lk(mtx_);
211 ensure_env();
212 if (l == Level::Off) return;
213
214 const std::string text = rtrim(msg);
215 const char* fname = base_name(file);
216
217 if (console_enabled_ && l >= console_level_) {
218 std::ostream& os = (l >= Level::Warn) ? std::cerr : std::cout;
219 if (color_) os << color_for(l);
220 os << "[" << level_name(l) << "]";
221 if (color_) os << color_reset();
222 if (verbose_format_) {
223 os << " " << timestamp() << " " << fname << ":" << line;
224 }
225 os << " " << text << "\n";
226 }
227
228 if (file_.is_open() && l >= file_level_) {
229 file_ << timestamp() << " [" << level_name(l) << "] " << fname << ":" << line << " " << text
230 << "\n";
231 file_.flush();
232 }
233}
234
238
239void set_debug(bool on) {
240 if (on && Logger::instance().console_level() > Level::Debug) {
242 }
243}
244
245bool set_file(const std::string& path) {
246 return Logger::instance().set_file(path);
247}
248
249} // namespace log
250} // namespace ncorr
Process-wide singleton logger.
Definition log.h:51
void configure_from_env()
Apply NCORR_LOG_LEVEL / NCORR_LOG_FILE / NCORR_LOG_CONSOLE.
Definition log.cpp:147
void set_file_level(Level l)
Definition log.cpp:167
void write(Level l, const char *file, int line, const std::string &msg)
Emit a fully-formed message (trailing newlines are trimmed) at l.
Definition log.cpp:209
Level console_level() const
Definition log.cpp:172
static Logger & instance()
Definition log.cpp:131
bool enabled(Level l)
True if a message at l would reach any sink.
Definition log.cpp:200
void set_console_enabled(bool on)
Enable or disable console output entirely.
Definition log.cpp:190
void set_console_level(Level l)
Definition log.cpp:162
Level file_level() const
Definition log.cpp:177
bool set_file(const std::string &path)
Open (or replace) the file sink.
Definition log.cpp:182
void set_verbose_format(bool on)
Include timestamp + source location in console output too (the file sink always includes them).
Definition log.cpp:195
#define NCORR_ISATTY(fd)
Definition log.cpp:21
#define NCORR_FILENO(f)
Definition log.cpp:22
Lightweight, dependency-free logging facility for CppNCorr.
void set_level(Level l)
Set the console threshold (convenience wrapper).
Definition log.cpp:235
void set_debug(bool on)
Lower the console threshold to Debug when on is true (used to honour the engine's existing debug flag...
Definition log.cpp:239
Level level_from_string(const std::string &s, Level fallback=Level::Info)
Parse a level name (case-insensitive): trace, debug, info, warn|warning, error, off.
Definition log.cpp:102
Level
Severity levels, ordered from most to least verbose.
Definition log.h:39
const char * level_name(Level l)
Short, fixed-width display name for a level (e.g. "INFO ").
Definition log.cpp:113
bool set_file(const std::string &path)
Convenience: open a log file sink (full Debug detail).
Definition log.cpp:245