16#define CPPXDIC_ISATTY(fd) _isatty(fd)
17#define CPPXDIC_FILENO(f) _fileno(f)
20#define CPPXDIC_ISATTY(fd) ::isatty(fd)
21#define CPPXDIC_FILENO(f) ::fileno(f)
29std::string toLower(std::string s) {
30 std::transform(s.begin(), s.end(), s.begin(),
31 [](
unsigned char c) { return static_cast<char>(std::tolower(c)); });
35const char* baseName(
const char* path) {
37 const char* base = path;
38 for (
const char* p = path; *p; ++p) {
39 if (*p ==
'/' || *p ==
'\\') base = p + 1;
44const char* colorFor(
Level l) {
60const char* colorReset() {
64std::string timestamp() {
65 using namespace std::chrono;
66 const auto now = system_clock::now();
67 const auto t = system_clock::to_time_t(now);
68 const auto ms = duration_cast<milliseconds>(now.time_since_epoch()).count() % 1000;
71 localtime_s(&tm_buf, &t);
73 localtime_r(&t, &tm_buf);
76 std::strftime(buf,
sizeof(buf),
"%Y-%m-%d %H:%M:%S", &tm_buf);
78 std::snprintf(out,
sizeof(out),
"%s.%03d", buf,
static_cast<int>(ms));
82std::string rtrim(
const std::string& s) {
83 std::size_t end = s.size();
85 const char c = s[end - 1];
86 if (c ==
'\n' || c ==
'\r' || c ==
' ' || c ==
'\t')
91 return s.substr(0, end);
97 const std::string v = toLower(s);
101 if (v ==
"warn" || v ==
"warning")
return Level::Warn;
103 if (v ==
"off" || v ==
"none" || v ==
"silent")
return Level::Off;
134void Logger::ensureEnv() {
142 if (
const char* lvl = std::getenv(
"CPPXDIC_LOG_LEVEL")) {
145 if (
const char* con = std::getenv(
"CPPXDIC_LOG_CONSOLE")) {
146 const std::string v = toLower(con);
147 console_enabled_ = !(v ==
"0" || v ==
"false" || v ==
"off" || v ==
"no");
149 if (
const char* path = std::getenv(
"CPPXDIC_LOG_FILE")) {
150 if (path[0] !=
'\0') file_.open(path, std::ios::out | std::ios::app);
155 std::lock_guard<std::mutex> lk(mtx_);
160 std::lock_guard<std::mutex> lk(mtx_);
165 std::lock_guard<std::mutex> lk(mtx_);
166 return console_level_;
170 std::lock_guard<std::mutex> lk(mtx_);
175 std::lock_guard<std::mutex> lk(mtx_);
176 if (file_.is_open()) file_.close();
177 if (path.empty())
return true;
178 file_.open(path, std::ios::out | std::ios::app);
179 return file_.is_open();
183 std::lock_guard<std::mutex> lk(mtx_);
184 console_enabled_ = on;
188 std::lock_guard<std::mutex> lk(mtx_);
189 verbose_format_ = on;
193 std::lock_guard<std::mutex> lk(mtx_);
196 const bool to_console = console_enabled_ && l >= console_level_;
197 const bool to_file = file_.is_open() && l >= file_level_;
198 return to_console || to_file;
202 std::lock_guard<std::mutex> lk(mtx_);
206 const std::string text = rtrim(msg);
207 const char* fname = baseName(file);
209 if (console_enabled_ && l >= console_level_) {
210 std::ostream& os = (l >=
Level::Warn) ? std::cerr : std::cout;
211 if (color_) os << colorFor(l);
213 if (color_) os << colorReset();
214 if (verbose_format_) os <<
" " << timestamp() <<
" " << fname <<
":" << line;
215 os <<
" " << text <<
"\n";
218 if (file_.is_open() && l >= file_level_) {
219 file_ << timestamp() <<
" [" <<
levelName(l) <<
"] " << fname <<
":" << line <<
" " << text
244 auto step_down = [](
Level x,
int n) {
245 int v =
static_cast<int>(x) - n;
247 return static_cast<Level>(v);
264 std::string lname(name);
266 while (!lname.empty() && lname.back() ==
' ') lname.pop_back();
267 std::transform(lname.begin(), lname.end(), lname.begin(),
268 [](
unsigned char c) { return static_cast<char>(std::tolower(c)); });
270 setenv(
"NCORR_LOG_LEVEL", lname.c_str(), 1);
271 if (!opts.
log_file.empty()) setenv(
"NCORR_LOG_FILE", opts.
log_file.c_str(), 1);
Process-wide singleton logger.
void setVerboseFormat(bool on)
Include timestamp + source location in console output too (the file sink always includes them).
void setFileLevel(Level l)
void setConsoleLevel(Level l)
bool setFile(const std::string &path)
Open (or replace) the file sink.
bool enabled(Level l)
True if a message at l would reach any sink.
Level consoleLevel() const
void configureFromEnv()
Apply CPPXDIC_LOG_LEVEL / CPPXDIC_LOG_FILE / CPPXDIC_LOG_CONSOLE.
static Logger & instance()
void write(Level l, const char *file, int line, const std::string &msg)
Emit a fully-formed message (trailing newlines trimmed) at l.
void setConsoleEnabled(bool on)
Enable or disable console output entirely.
#define CPPXDIC_ISATTY(fd)
#define CPPXDIC_FILENO(f)
Lightweight, dependency-free logging facility for CPPxDIC.
void configureFromOptions(const Options &opts)
Apply env vars, then the supplied options (CLI/config), to the singleton.
void setLevel(Level l)
Convenience accessors.
Level
Severity levels, ordered from most to least verbose.
bool setFile(const std::string &path)
Level levelFromString(const std::string &s, Level fallback=Level::Info)
Parse a level name (case-insensitive): trace, debug, info, warn|warning, error, off.
const char * levelName(Level l)
Short, fixed-width display name for a level (e.g. "INFO ").
DataSerializer - Abstract base class for format-agnostic DIC data I/O.
Aggregated options resolved from env/config/CLI, applied in one call.
bool console_level_set
true if an explicit level was chosen
std::string log_file
empty = no file sink
int verbose
-V count: lowers threshold per step
bool debug
debug mode (Debug level + verbose format)
bool quiet
force console threshold up to Warn