Icarus
Vehicle Simulation as a Transformable Computational Graph, built on Vulcan and Janus
Loading...
Searching...
No Matches
LogService.hpp
Go to the documentation of this file.
1#pragma once
2
12
15
16#include <algorithm>
17#include <chrono>
18#include <functional>
19#include <iomanip>
20#include <mutex>
21#include <sstream>
22#include <string>
23#include <string_view>
24#include <vector>
25
26namespace icarus {
27
28// =============================================================================
29// LogConfig
30// =============================================================================
31
35struct LogConfig {
36 // Console output
38 bool progress_enabled = true;
39
40 // File output
41 bool file_enabled = false;
42 std::string file_path;
44 bool file_rotate = false;
45 std::size_t max_file_size_mb = 100;
46
47 // Features
48 bool profiling_enabled = false;
49 bool quiet_mode = false;
50
51 // Telemetry recording
52 bool telemetry_enabled = false;
53 std::string telemetry_path;
54 std::vector<std::string> telemetry_signals;
55
57 [[nodiscard]] static LogConfig Default() {
58 LogConfig config;
59 return config;
60 }
61
63 [[nodiscard]] static LogConfig Quiet() {
64 LogConfig config;
66 config.progress_enabled = false;
67 config.quiet_mode = true;
68 return config;
69 }
70
72 [[nodiscard]] static LogConfig Verbose() {
73 LogConfig config;
75 config.file_enabled = true;
77 return config;
78 }
79
81 [[nodiscard]] static LogConfig WithProfiling() {
82 LogConfig config;
83 config.profiling_enabled = true;
84 return config;
85 }
86};
87
88// =============================================================================
89// LogContext
90// =============================================================================
91
98struct LogContext {
99 std::string entity;
100 std::string component;
101 std::string type;
102
104 [[nodiscard]] std::string FullPath() const { return MakeFullPath(entity, component); }
105
107 [[nodiscard]] bool IsSet() const { return !component.empty(); }
108};
109
110// =============================================================================
111// LogEntry
112// =============================================================================
113
119struct LogEntry {
121 double sim_time;
122 std::string message;
124
126 std::chrono::steady_clock::time_point wall_time;
127
129 static LogEntry Create(LogLevel level, double sim_time, std::string_view message,
130 const LogContext &ctx) {
131 LogEntry entry;
132 entry.level = level;
133 entry.sim_time = sim_time;
134 entry.message = std::string(message);
135 entry.context = ctx;
136 entry.wall_time = std::chrono::steady_clock::now();
137 return entry;
138 }
139
141 [[nodiscard]] std::string Format(bool include_context = true) const {
142 std::ostringstream oss;
143 oss << "[" << std::fixed << std::setprecision(3) << sim_time << "] ";
144 oss << "[" << GetLevelString(level) << "] ";
145
146 if (include_context && context.IsSet()) {
147 oss << "[" << context.FullPath() << "] ";
148 }
149
150 oss << message;
151 return oss.str();
152 }
153
155 [[nodiscard]] std::string FormatColored(const Console &console) const {
156 std::ostringstream oss;
157
158 // Time in dim
159 oss << console.Colorize("[", AnsiColor::Dim);
160 oss << std::fixed << std::setprecision(3) << sim_time;
161 oss << console.Colorize("]", AnsiColor::Dim) << " ";
162
163 // Level with appropriate color
164 oss << console.Colorize("[" + std::string(GetLevelString(level)) + "]",
165 GetLevelColor(level))
166 << " ";
167
168 // Context in cyan
169 if (context.IsSet()) {
170 oss << console.Colorize("[" + context.FullPath() + "]", AnsiColor::Cyan) << " ";
171 }
172
173 oss << message;
174 return oss.str();
175 }
176
177 private:
178 [[nodiscard]] static const char *GetLevelString(LogLevel level) {
179 switch (level) {
180 case LogLevel::Trace:
181 return "TRC";
182 case LogLevel::Debug:
183 return "DBG";
184 case LogLevel::Info:
185 return "INF";
186 case LogLevel::Event:
187 return "EVT";
189 return "WRN";
190 case LogLevel::Error:
191 return "ERR";
192 case LogLevel::Fatal:
193 return "FTL";
194 }
195 return "???";
196 }
197
198 [[nodiscard]] static const char *GetLevelColor(LogLevel level) {
199 switch (level) {
200 case LogLevel::Trace:
201 return AnsiColor::Gray;
202 case LogLevel::Debug:
203 return AnsiColor::Cyan;
204 case LogLevel::Info:
205 return AnsiColor::White;
206 case LogLevel::Event:
207 return AnsiColor::Green;
209 return AnsiColor::Yellow;
210 case LogLevel::Error:
211 return AnsiColor::Red;
212 case LogLevel::Fatal:
213 return AnsiColor::BgRed;
214 }
215 return AnsiColor::White;
216 }
217};
218
219// =============================================================================
220// LogContextManager
221// =============================================================================
222
230 public:
232 static void SetContext(const LogContext &ctx) { current_context_ = ctx; }
233
235 static void ClearContext() { current_context_ = LogContext{}; }
236
238 [[nodiscard]] static const LogContext &GetContext() { return current_context_; }
239
244 public:
245 ScopedContext(const std::string &entity, const std::string &component,
246 const std::string &type = "")
247 : previous_(current_context_) {
248 current_context_.entity = entity;
249 current_context_.component = component;
250 current_context_.type = type;
251 }
252
253 ~ScopedContext() { current_context_ = previous_; }
254
255 // Non-copyable, non-movable
256 ScopedContext(const ScopedContext &) = delete;
260
261 private:
262 LogContext previous_;
263 };
264
265 private:
266 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
267 static inline thread_local LogContext current_context_;
268};
269
270// =============================================================================
271// LogService
272// =============================================================================
273
290 public:
292 using Sink = std::function<void(const std::vector<LogEntry> &)>;
293
294 LogService() = default;
295
296 // === Mode Control ===
297
299 void SetImmediateMode(bool immediate) { immediate_mode_ = immediate; }
300 [[nodiscard]] bool IsImmediateMode() const { return immediate_mode_; }
301
309 public:
310 explicit BufferedScope(LogService &service)
311 : service_(service), previous_mode_(service.immediate_mode_) {
312 service_.SetImmediateMode(false);
313 }
314
316 service_.FlushAndClear();
317 service_.SetImmediateMode(previous_mode_);
318 }
319
320 // Non-copyable, non-movable
321 BufferedScope(const BufferedScope &) = delete;
325
326 private:
327 LogService &service_;
328 bool previous_mode_;
329 };
330
331 // === Configuration ===
332
334 void SetMinLevel(LogLevel level) { min_level_ = level; }
335 [[nodiscard]] LogLevel GetMinLevel() const { return min_level_; }
336
338 void Reserve(std::size_t capacity) { entries_.reserve(capacity); }
339
341 void AddSink(Sink sink) { sinks_.emplace_back(std::move(sink), LogLevel::Trace); }
342
344 void AddSink(Sink sink, LogLevel min_level) { sinks_.emplace_back(std::move(sink), min_level); }
345
347 void ClearSinks() { sinks_.clear(); }
348
349 // === Logging API ===
350
352 void Log(LogLevel level, double sim_time, std::string_view message) {
353 Log(level, sim_time, message, LogContextManager::GetContext());
354 }
355
357 void Log(LogLevel level, double sim_time, std::string_view message, const LogContext &ctx) {
358 if (level < min_level_) {
359 return;
360 }
361
362 auto entry = LogEntry::Create(level, sim_time, message, ctx);
363
364 std::lock_guard<std::mutex> lock(mutex_);
365
366 // Track error counts (inside lock for thread safety)
367 if (level == LogLevel::Error) {
368 ++error_count_;
369 } else if (level == LogLevel::Fatal) {
370 ++fatal_count_;
371 }
372
373 entries_.push_back(std::move(entry));
374
375 if (immediate_mode_) {
376 FlushEntry(entries_.back());
377 }
378 }
379
380 // Convenience methods (use thread-local context)
381 void Trace(double t, std::string_view msg) { Log(LogLevel::Trace, t, msg); }
382 void Debug(double t, std::string_view msg) { Log(LogLevel::Debug, t, msg); }
383 void Info(double t, std::string_view msg) { Log(LogLevel::Info, t, msg); }
384 void Event(double t, std::string_view msg) { Log(LogLevel::Event, t, msg); }
385 void Warning(double t, std::string_view msg) { Log(LogLevel::Warning, t, msg); }
386 void Error(double t, std::string_view msg) { Log(LogLevel::Error, t, msg); }
387 void Fatal(double t, std::string_view msg) { Log(LogLevel::Fatal, t, msg); }
388
389 // === Flush Control ===
390
392 void Flush(bool sort_by_time = false) {
393 std::lock_guard<std::mutex> lock(mutex_);
394
395 if (entries_.empty()) {
396 return;
397 }
398
399 if (sort_by_time) {
400 std::sort(entries_.begin(), entries_.end(), [](const LogEntry &a, const LogEntry &b) {
401 return a.wall_time < b.wall_time;
402 });
403 }
404
405 for (const auto &[sink, min_level] : sinks_) {
406 // Filter entries by sink's minimum level
407 std::vector<LogEntry> filtered;
408 filtered.reserve(entries_.size());
409 for (const auto &entry : entries_) {
410 if (entry.level >= min_level) {
411 filtered.push_back(entry);
412 }
413 }
414 if (!filtered.empty()) {
415 sink(filtered);
416 }
417 }
418 }
419
421 void FlushAndClear(bool sort_by_time = false) {
422 Flush(sort_by_time);
423 std::lock_guard<std::mutex> lock(mutex_);
424 entries_.clear();
425 }
426
428 void Clear() {
429 std::lock_guard<std::mutex> lock(mutex_);
430 entries_.clear();
431 }
432
433 // === Query API ===
434
435 [[nodiscard]] std::size_t PendingCount() const {
436 std::lock_guard<std::mutex> lock(mutex_);
437 return entries_.size();
438 }
439
440 [[nodiscard]] bool HasPending() const {
441 std::lock_guard<std::mutex> lock(mutex_);
442 return !entries_.empty();
443 }
444
445 [[nodiscard]] std::vector<LogEntry> GetPending() const {
446 std::lock_guard<std::mutex> lock(mutex_);
447 return entries_; // Return copy for thread safety
448 }
449
451 [[nodiscard]] bool HasErrors() const {
452 std::lock_guard<std::mutex> lock(mutex_);
453 return error_count_ > 0;
454 }
455
456 [[nodiscard]] bool HasFatalErrors() const {
457 std::lock_guard<std::mutex> lock(mutex_);
458 return fatal_count_ > 0;
459 }
460
461 [[nodiscard]] std::size_t ErrorCount() const {
462 std::lock_guard<std::mutex> lock(mutex_);
463 return error_count_;
464 }
465
466 [[nodiscard]] std::size_t FatalCount() const {
467 std::lock_guard<std::mutex> lock(mutex_);
468 return fatal_count_;
469 }
470
473 std::lock_guard<std::mutex> lock(mutex_);
474 error_count_ = 0;
475 fatal_count_ = 0;
476 }
477
478 // === Filtering (for query, not output) ===
479
480 [[nodiscard]] std::vector<LogEntry> GetEntriesAtLevel(LogLevel level) const {
481 std::lock_guard<std::mutex> lock(mutex_);
482 std::vector<LogEntry> result;
483 for (const auto &entry : entries_) {
484 if (entry.level == level) {
485 result.push_back(entry);
486 }
487 }
488 return result;
489 }
490
491 [[nodiscard]] std::vector<LogEntry> GetEntriesForEntity(std::string_view entity) const {
492 std::lock_guard<std::mutex> lock(mutex_);
493 std::vector<LogEntry> result;
494 for (const auto &entry : entries_) {
495 if (entry.context.entity == entity) {
496 result.push_back(entry);
497 }
498 }
499 return result;
500 }
501
502 [[nodiscard]] std::vector<LogEntry> GetEntriesForComponent(std::string_view path) const {
503 std::lock_guard<std::mutex> lock(mutex_);
504 std::vector<LogEntry> result;
505 for (const auto &entry : entries_) {
506 if (entry.context.FullPath() == path) {
507 result.push_back(entry);
508 }
509 }
510 return result;
511 }
512
513 private:
514 std::vector<LogEntry> entries_;
515 std::vector<std::pair<Sink, LogLevel>> sinks_;
516 LogLevel min_level_ = LogLevel::Info;
517 bool immediate_mode_ = true;
518
519 std::size_t error_count_ = 0;
520 std::size_t fatal_count_ = 0;
521
522 mutable std::mutex mutex_;
523
524 void FlushEntry(const LogEntry &entry) {
525 for (const auto &[sink, min_level] : sinks_) {
526 if (entry.level >= min_level) {
527 sink({entry});
528 }
529 }
530 }
531};
532
537 static LogService instance;
538 return instance;
539}
540
541} // namespace icarus
542
543// =============================================================================
544// Logging Macros
545// =============================================================================
546
547// NOLINTBEGIN(cppcoreguidelines-macro-usage)
548#define ICARUS_LOG_TRACE(sim_time, msg) ::icarus::GetLogService().Trace(sim_time, msg)
549
550#define ICARUS_LOG_DEBUG(sim_time, msg) ::icarus::GetLogService().Debug(sim_time, msg)
551
552#define ICARUS_LOG_INFO(sim_time, msg) ::icarus::GetLogService().Info(sim_time, msg)
553
554#define ICARUS_LOG_EVENT(sim_time, msg) ::icarus::GetLogService().Event(sim_time, msg)
555
556#define ICARUS_LOG_WARN(sim_time, msg) ::icarus::GetLogService().Warning(sim_time, msg)
557
558#define ICARUS_LOG_ERROR(sim_time, msg) ::icarus::GetLogService().Error(sim_time, msg)
559
560#define ICARUS_LOG_FATAL(sim_time, msg) ::icarus::GetLogService().Fatal(sim_time, msg)
561// NOLINTEND(cppcoreguidelines-macro-usage)
Console abstraction with ANSI color support.
Core type definitions, concepts, and configuration for Icarus.
Console output with color and formatting support.
Definition Console.hpp:111
std::string Colorize(std::string_view text, const char *color) const
Apply color if enabled.
Definition Console.hpp:177
ScopedContext(const std::string &entity, const std::string &component, const std::string &type="")
Definition LogService.hpp:245
ScopedContext(const ScopedContext &)=delete
ScopedContext(ScopedContext &&)=delete
ScopedContext & operator=(const ScopedContext &)=delete
~ScopedContext()
Definition LogService.hpp:253
ScopedContext & operator=(ScopedContext &&)=delete
Thread-local log context manager.
Definition LogService.hpp:229
static const LogContext & GetContext()
Get current context (called internally by LogBuffer::Log).
Definition LogService.hpp:238
static void SetContext(const LogContext &ctx)
Set context for current thread (called by Simulator before Step).
Definition LogService.hpp:232
static void ClearContext()
Clear context (called by Simulator after Step).
Definition LogService.hpp:235
~BufferedScope()
Definition LogService.hpp:315
BufferedScope(BufferedScope &&)=delete
BufferedScope(LogService &service)
Definition LogService.hpp:310
BufferedScope & operator=(const BufferedScope &)=delete
BufferedScope & operator=(BufferedScope &&)=delete
BufferedScope(const BufferedScope &)=delete
Unified logging service for Icarus.
Definition LogService.hpp:289
void Log(LogLevel level, double sim_time, std::string_view message, const LogContext &ctx)
Log with explicit context (bypasses thread-local).
Definition LogService.hpp:357
std::vector< LogEntry > GetEntriesForEntity(std::string_view entity) const
Definition LogService.hpp:491
void Fatal(double t, std::string_view msg)
Definition LogService.hpp:387
void Event(double t, std::string_view msg)
Definition LogService.hpp:384
bool HasErrors() const
Check if any errors were logged (in buffer or flushed).
Definition LogService.hpp:451
std::size_t ErrorCount() const
Definition LogService.hpp:461
void Warning(double t, std::string_view msg)
Definition LogService.hpp:385
void Error(double t, std::string_view msg)
Definition LogService.hpp:386
void SetMinLevel(LogLevel level)
Set minimum level (below this = dropped).
Definition LogService.hpp:334
std::size_t FatalCount() const
Definition LogService.hpp:466
void Flush(bool sort_by_time=false)
Flush buffer to all sinks.
Definition LogService.hpp:392
LogLevel GetMinLevel() const
Definition LogService.hpp:335
void Reserve(std::size_t capacity)
Set buffer capacity (pre-allocate for performance).
Definition LogService.hpp:338
void ClearSinks()
Clear all sinks.
Definition LogService.hpp:347
std::function< void(const std::vector< LogEntry > &)> Sink
Sink callback type: receives batch of entries to output.
Definition LogService.hpp:292
void Trace(double t, std::string_view msg)
Definition LogService.hpp:381
void Clear()
Clear buffer without flushing (discard pending logs).
Definition LogService.hpp:428
bool IsImmediateMode() const
Definition LogService.hpp:300
void ResetErrorCounts()
Reset error counts (call at start of new run).
Definition LogService.hpp:472
void AddSink(Sink sink)
Add an output sink.
Definition LogService.hpp:341
void Debug(double t, std::string_view msg)
Definition LogService.hpp:382
std::size_t PendingCount() const
Definition LogService.hpp:435
bool HasPending() const
Definition LogService.hpp:440
std::vector< LogEntry > GetEntriesAtLevel(LogLevel level) const
Definition LogService.hpp:480
std::vector< LogEntry > GetPending() const
Definition LogService.hpp:445
void FlushAndClear(bool sort_by_time=false)
Flush and clear buffer.
Definition LogService.hpp:421
LogService()=default
void Log(LogLevel level, double sim_time, std::string_view message)
Log a message (uses current thread-local context).
Definition LogService.hpp:352
void SetImmediateMode(bool immediate)
Enable immediate flush after each log (for lifecycle phases).
Definition LogService.hpp:299
void AddSink(Sink sink, LogLevel min_level)
Add a sink that only receives entries at or above a level.
Definition LogService.hpp:344
void Info(double t, std::string_view msg)
Definition LogService.hpp:383
bool HasFatalErrors() const
Definition LogService.hpp:456
std::vector< LogEntry > GetEntriesForComponent(std::string_view path) const
Definition LogService.hpp:502
Definition AggregationTypes.hpp:13
std::string MakeFullPath(const std::string &entity, const std::string &name)
Build a full path from entity and name.
Definition CoreTypes.hpp:166
LogLevel
Log severity levels.
Definition Console.hpp:35
@ Warning
Potential issues.
Definition Console.hpp:40
@ Info
Normal operation.
Definition Console.hpp:38
@ Fatal
Unrecoverable errors.
Definition Console.hpp:42
@ Error
Recoverable errors.
Definition Console.hpp:41
@ Event
Simulation events (phase changes, etc.).
Definition Console.hpp:39
@ Debug
Debugging info.
Definition Console.hpp:37
@ Trace
Most verbose, internal debugging.
Definition Console.hpp:36
LogService & GetLogService()
Global log service singleton.
Definition LogService.hpp:536
static constexpr const char * BgRed
Definition Console.hpp:68
static constexpr const char * Cyan
Definition Console.hpp:63
static constexpr const char * Gray
Definition Console.hpp:65
static constexpr const char * Red
Definition Console.hpp:58
static constexpr const char * Dim
Definition Console.hpp:55
static constexpr const char * White
Definition Console.hpp:64
static constexpr const char * Green
Definition Console.hpp:59
static constexpr const char * Yellow
Definition Console.hpp:60
Logging configuration.
Definition LogService.hpp:35
static LogConfig Default()
Create default config.
Definition LogService.hpp:57
bool profiling_enabled
Definition LogService.hpp:48
bool file_rotate
Definition LogService.hpp:44
bool quiet_mode
Suppress all but errors.
Definition LogService.hpp:49
LogLevel console_level
Definition LogService.hpp:37
std::size_t max_file_size_mb
Definition LogService.hpp:45
bool progress_enabled
Definition LogService.hpp:38
std::string telemetry_path
Definition LogService.hpp:53
static LogConfig Verbose()
Create verbose config (all output).
Definition LogService.hpp:72
bool telemetry_enabled
Definition LogService.hpp:52
std::vector< std::string > telemetry_signals
Which signals to record.
Definition LogService.hpp:54
static LogConfig Quiet()
Create quiet config (errors only).
Definition LogService.hpp:63
LogLevel file_level
Definition LogService.hpp:43
bool file_enabled
Definition LogService.hpp:41
static LogConfig WithProfiling()
Create profiling config.
Definition LogService.hpp:81
std::string file_path
Definition LogService.hpp:42
Immutable log context - set by component/entity during execution.
Definition LogService.hpp:98
std::string type
Component type (e.g., "LiquidEngine").
Definition LogService.hpp:101
std::string FullPath() const
Full path: "entity.component" or just "component" if no entity.
Definition LogService.hpp:104
std::string component
Component name (e.g., "merlinEngine2").
Definition LogService.hpp:100
std::string entity
Entity name (e.g., "falcon9_1").
Definition LogService.hpp:99
bool IsSet() const
Check if context is set.
Definition LogService.hpp:107
A single log entry with full context.
Definition LogService.hpp:119
LogLevel level
Severity level.
Definition LogService.hpp:120
std::string message
Log message.
Definition LogService.hpp:122
LogContext context
Entity/component context.
Definition LogService.hpp:123
double sim_time
Simulation time when logged.
Definition LogService.hpp:121
std::chrono::steady_clock::time_point wall_time
Wall clock time for ordering logs from concurrent sources.
Definition LogService.hpp:126
std::string Format(bool include_context=true) const
Format for output: "[sim_time] [LEVEL] [entity.component] message".
Definition LogService.hpp:141
static LogEntry Create(LogLevel level, double sim_time, std::string_view message, const LogContext &ctx)
Create a log entry with current context.
Definition LogService.hpp:129
std::string FormatColored(const Console &console) const
Format with colors (for terminal).
Definition LogService.hpp:155