LogIt++
Header-only, macro-first C++ logging library
Loading...
Searching...
No Matches
PatternCompiler.hpp
Go to the documentation of this file.
1#pragma once
2#ifndef LOGIT_CPP_HEADER_LOGIT_CPP_LOGIT_FORMATTER_COMPILER_PATTERNCOMPILER_HPP_INCLUDED
3#define LOGIT_CPP_HEADER_LOGIT_CPP_LOGIT_FORMATTER_COMPILER_PATTERNCOMPILER_HPP_INCLUDED
4
7
8#include <time_shield/conversions.hpp>
9#include <vector>
10#include <string>
11#include <sstream>
12#include <iomanip>
13#include <cstdio>
14
15namespace logit {
16
20
23 enum class CompileContext {
24 Default,
26 };
27
30 enum class FormatType {
31 // Text
33
34 // Date and Time
35 Year,
36 Month,
37 Day,
38 Hour,
39 Minute,
40 Second,
43 DateTime,
44 ShortDate,
47 TimeStamp,
49
50 // Weekday and Month Names
55
56 // Log Level
57 LogLevel,
59
60 // File and Function
61 FileName,
66
67 // Thread
68 ThreadId,
69
70 // Diagnostic context
74
75 // Color
77 EndColor,
78
79 // Message
80 Message
81 };
82
85 std::string static_text;
86
87 // Fields for alignment and width
88 int width = 0;
89 bool left_align = false;
90 bool center_align = false;
91 bool truncate = false;
92 bool strip_ansi = false;
93 std::string context_key;
94
101 const std::string& text,
102 bool strip_ansi)
104 };
105
118 int width = 0,
119 bool left = false,
120 bool center = false,
121 bool trunc = false,
122 bool strip_ansi = false,
123 const std::string& context_key = std::string()) :
125 left_align(left), center_align(center),
128 };
129
135 template<class StreamType>
136 void apply(
137 StreamType& oss,
138 const LogRecord& record,
139 const time_shield::DateTimeStruct& dt) const {
140
142 !record.format.empty() ||
143 !record.args_array.empty())) return;
144
145 std::ostringstream temp_stream;
146 switch (type) {
147 // Text
149 temp_stream << static_text;
150 break;
151
152 // Date and Time
153 case FormatType::Year:
154 temp_stream << dt.year;
155 break;
157 temp_stream << std::setw(2) << std::setfill('0') << dt.mon;
158 break;
159 case FormatType::Day:
160 temp_stream << std::setw(2) << std::setfill('0') << dt.day;
161 break;
162 case FormatType::Hour:
163 temp_stream << std::setw(2) << std::setfill('0') << dt.hour;
164 break;
166 temp_stream << std::setw(2) << std::setfill('0') << dt.min;
167 break;
169 temp_stream << std::setw(2) << std::setfill('0') << dt.sec;
170 break;
172 temp_stream << std::setw(3) << std::setfill('0') << dt.ms;
173 break;
175 temp_stream << std::setw(2) << std::setfill('0') << (dt.year % 100);
176 break;
178 // Format equivalent to 'ctime'
179 char buffer[16];
180 temp_stream << time_shield::to_str(time_shield::day_of_week(dt.year, dt.mon, dt.day), time_shield::FormatType::SHORT_NAME);
181 temp_stream << " ";
182 temp_stream << time_shield::to_str(static_cast<time_shield::Month>(dt.mon), time_shield::FormatType::SHORT_NAME);
183 temp_stream << " ";
184 // day
185 snprintf(buffer, sizeof(buffer), "%2d ", dt.day);
186 temp_stream << buffer;
187 // time
188 snprintf(buffer, sizeof(buffer), "%.2d:%.2d:%.2d ", dt.hour, dt.min, dt.sec);
189 temp_stream << buffer;
190 // year
191 temp_stream << dt.year;
192 break;
193 }
195 temp_stream << std::setw(2) << std::setfill('0') << dt.mon << "/"
196 << std::setw(2) << std::setfill('0') << dt.day << "/"
197 << std::setw(2) << std::setfill('0') << (dt.year % 100);
198 break;
200 temp_stream
201 << std::setw(2) << std::setfill('0') << dt.hour << ":"
202 << std::setw(2) << std::setfill('0') << dt.min << ":"
203 << std::setw(2) << std::setfill('0') << dt.sec;
204 break;
206 temp_stream
207 << dt.year << "-"
208 << std::setw(2) << std::setfill('0') << dt.mon << "-"
209 << std::setw(2) << std::setfill('0') << dt.day;
210 break;
212 temp_stream << time_shield::ms_to_sec(record.timestamp_ms);
213 break;
215 temp_stream << record.timestamp_ms;
216 break;
217
218 // Weekday and Month Names
220 temp_stream << time_shield::to_str(static_cast<time_shield::Month>(dt.mon), time_shield::FormatType::SHORT_NAME);
221 break;
223 temp_stream << time_shield::to_str(static_cast<time_shield::Month>(dt.mon), time_shield::FormatType::FULL_NAME);
224 break;
226 temp_stream << time_shield::to_str(time_shield::day_of_week(dt.year, dt.mon, dt.day), time_shield::FormatType::SHORT_NAME);
227 break;
229 temp_stream << time_shield::to_str(time_shield::day_of_week(dt.year, dt.mon, dt.day), time_shield::FormatType::FULL_NAME);
230 break;
231
232 // Log Level
234 temp_stream << to_string(record.log_level);
235 break;
237 temp_stream << to_string(record.log_level, 1);
238 break;
239
240 // File and Function
242 std::string full_path = record.file;
243 size_t pos = full_path.find_last_of("/\\");
244 if (pos != std::string::npos) {
245 temp_stream << full_path.substr(pos + 1);
246 } else {
247 temp_stream << full_path;
248 }
249 break;
250 }
252 temp_stream << record.file;
253 break;
255 temp_stream << record.file << ":" << record.line;
256 break;
258 temp_stream << record.line;
259 break;
261 temp_stream << record.function;
262 break;
263
264 // Thread
266 temp_stream << record.thread_id;
267 break;
268
269 // Diagnostic context
271#ifdef LOGIT_WITH_CONTEXT
272 if (!record.context) {
273 break;
274 }
275 bool first = true;
276 for (std::map<std::string, std::string>::const_iterator it = record.context->mdc.begin();
277 it != record.context->mdc.end();
278 ++it) {
279 if (!first) {
280 temp_stream << " ";
281 }
282 temp_stream << it->first << "=" << it->second;
283 first = false;
284 }
285#endif
286 break;
287 }
289#ifdef LOGIT_WITH_CONTEXT
290 if (record.context) {
291 std::map<std::string, std::string>::const_iterator it =
292 record.context->mdc.find(context_key);
293 if (it != record.context->mdc.end()) {
294 temp_stream << it->second;
295 }
296 }
297#endif
298 break;
299 }
301#ifdef LOGIT_WITH_CONTEXT
302 if (!record.context) {
303 break;
304 }
305 bool first = true;
306 for (std::vector<std::string>::const_iterator it = record.context->ndc.begin();
307 it != record.context->ndc.end();
308 ++it) {
309 if (!first) {
310 temp_stream << " > ";
311 }
312 temp_stream << *it;
313 first = false;
314 }
315#endif
316 break;
317 }
318
319 // Color
321 if (!strip_ansi) {
322 oss << get_log_level_color(record.log_level);
323 }
324 break;
326 if (!strip_ansi) {
328 }
329 break;
330
331 // Message
333 if (!record.format.empty()) {
334 if (record.args_array.empty()) {
335 temp_stream << record.format;
336 break;
337 }
338 using ValueType = VariableValue::ValueType;
339 for (size_t i = 0; i < record.args_array.size(); ++i) {
340 if (!record.print_mode && i) temp_stream << ", ";
341 const auto& arg = record.args_array[i];
342 switch (arg.type) {
343 case ValueType::STRING_VAL:
344 case ValueType::EXCEPTION_VAL:
345 case ValueType::ERROR_CODE_VAL:
346 case ValueType::ENUM_VAL:
347 case ValueType::DURATION_VAL:
348 case ValueType::TIME_POINT_VAL:
349 case ValueType::POINTER_VAL:
350 case ValueType::SMART_POINTER_VAL:
351 case ValueType::VARIANT_VAL:
352 case ValueType::OPTIONAL_VAL:
353#ifdef LOGIT_WITH_FMT
354 temp_stream << (record.fmt_mode ? arg.to_string_fmt(record.format.c_str()) : arg.to_string(record.format.c_str()));
355#else
356 temp_stream << arg.to_string(record.format.c_str());
357#endif
358 break;
359 default:
360#ifdef LOGIT_WITH_FMT
361 if (arg.is_literal) {
362 temp_stream << arg.name << ": " << (record.fmt_mode ? arg.to_string_fmt(record.format.c_str()) : arg.to_string(record.format.c_str()));
363 } else {
364 temp_stream << (record.fmt_mode ? arg.to_string_fmt(record.format.c_str()) : arg.to_string(record.format.c_str()));
365 }
366#else
367 if (arg.is_literal) {
368 temp_stream << arg.name << ": " << arg.to_string(record.format.c_str());
369 } else {
370 temp_stream << arg.to_string(record.format.c_str());
371 }
372#endif
373 break;
374 };
375 }
376 } else
377 if (!record.args_array.empty()) {
378 using ValueType = VariableValue::ValueType;
379 for (size_t i = 0; i < record.args_array.size(); ++i) {
380 if (!record.print_mode && i) temp_stream << ", ";
381 const auto& arg = record.args_array[i];
382 switch (arg.type) {
383 case ValueType::STRING_VAL:
384 case ValueType::EXCEPTION_VAL:
385 case ValueType::ERROR_CODE_VAL:
386 temp_stream << arg.to_string();
387 break;
388 case ValueType::ENUM_VAL:
389 if (arg.is_literal) {
390 if (record.print_mode) temp_stream << arg.to_string();
391 else temp_stream << arg.name << ": " << arg.to_string();
392 break;
393 }
394 temp_stream << arg.to_string();
395 break;
396 case ValueType::PATH_VAL:
397 case ValueType::DURATION_VAL:
398 case ValueType::TIME_POINT_VAL:
399 case ValueType::POINTER_VAL:
400 case ValueType::SMART_POINTER_VAL:
401 case ValueType::VARIANT_VAL:
402 case ValueType::OPTIONAL_VAL:
403 if (record.print_mode) temp_stream << arg.to_string();
404 else temp_stream << arg.name << ": " << arg.to_string();
405 break;
406 default:
407 if (arg.is_literal) {
408 if (record.print_mode) temp_stream << arg.to_string();
409 else temp_stream << arg.name << ": " << arg.to_string();
410 break;
411 }
412 temp_stream << arg.to_string();
413 break;
414 };
415 }
416 }
417 break;
418 };
419
420 // Get the string representation
421 std::string result = strip_ansi ? remove_ansi_escape_codes(temp_stream.str()) : temp_stream.str();
422
423 // Truncate if required
424 if (truncate && result.size() > static_cast<size_t>(width)) {
425 switch (type) {
426 // File and Function
431 const std::string placeholder = "..."; // Placeholder for omitted sections
432 int placeholder_size = static_cast<int>(placeholder.size());
433
434 // If the width is less than or equal to the placeholder size, return only the placeholder
435 if (width <= placeholder_size) {
436 result = placeholder.substr(0, width);
437 } else {
438 // Keep portions of the string from the beginning and end
439 size_t keep_size = (width - placeholder_size) / 2; // Portion to keep from each side
440 size_t keep_end = result.size() - keep_size;
441 int line_size = static_cast<int>(2 * keep_size + placeholder.size());
442
443 while (line_size < width) {
444 if (keep_end > 0) {
445 --keep_end;
446 ++line_size;
447 } else break;
448 }
449
450 // Construct the result: start + placeholder + end
451 result = result.substr(0, keep_size) + placeholder + result.substr(keep_end);
452 }
453 break;
454 }
455 default:
456 // Standard truncation for other types
457 result = result.substr(0, width);
458 };
459 }
460
461 // Apply alignment and width
462 if (width > 0 && result.size() < static_cast<size_t>(width)) {
463 if (left_align) {
464 // Left alignment
465 oss << std::left << std::setw(width) << result;
466 } else
467 if (center_align) {
468 // Center alignment
469 const int padding = static_cast<int>((width - result.size()) / 2);
470 oss << std::string(padding, ' ') << result << std::string(width - padding - result.size(), ' ');
471 } else {
472 // Right alignment (default)
473 oss << std::right << std::setw(width) << result;
474 }
475 } else {
476 // If width not specified, simply append the result
477 oss << result;
478 }
479 }
480
481 private:
482
486 std::string remove_ansi_escape_codes(const std::string& input) const {
487 std::string result;
488 result.reserve(input.size());
489 bool in_escape_sequence = false;
490
491 for (size_t i = 0; i < input.size(); ++i) {
492 if (in_escape_sequence) {
493 if ((input[i] >= 'a' && input[i] <= 'z') || (input[i] >= 'A' && input[i] <= 'Z')) {
494 in_escape_sequence = false;
495 }
496 } else {
497 if (input[i] == '\033' && (i + 1) < input.size() && input[i + 1] == '[') {
498 in_escape_sequence = true;
499 ++i; // Skip '[' after '\033'
500 } else {
501 result += input[i]; // Append non-escape characters to the result
502 }
503 }
504 }
505
506 return result;
507 }
508 }; // FormatInstruction
509
513 public:
515
520 static std::vector<FormatInstruction> compile(
521 const std::string& pattern,
522 CompileContext context = CompileContext::Default) {
523 using FormatType = FormatInstruction::FormatType;
524 std::vector<FormatInstruction> instructions;
525 std::string buffer;
526 buffer.reserve(pattern.size());
527 bool strip_ansi = false;
528
529 for (size_t i = 0; i < pattern.size(); ++i) {
530 char c = pattern[i];
531
532 if (c == '%') {
533 if (!buffer.empty()) {
534 instructions.push_back(FormatInstruction(context, buffer, strip_ansi));
535 buffer.clear();
536 }
537
538 // Handling alignment, width, and truncation
539 bool left_align = false;
540 bool center_align = false;
541 bool truncate = false;
542 int width = 0;
543
544 // Check for alignment and width
545 while ((i + 1) < pattern.size() && (
546 std::isdigit(pattern[i + 1]) ||
547 pattern[i + 1] == '-' ||
548 pattern[i + 1] == '=')) {
549 char next = pattern[++i];
550 if (next == '-') {
551 left_align = true;
552 } else if (next == '=') {
553 center_align = true;
554 } else if (std::isdigit(next)) {
555 width = width * 10 + (next - '0');
556 }
557
558 // Check for truncation '!'
559 if ((i + 1) < pattern.size() && pattern[i + 1] == '!') {
560 truncate = true;
561 ++i;
562 break;
563 }
564 }
565
566 if ((i + 1) < pattern.size()) {
567 char next = pattern[++i];
568 switch (next) {
569 // Date and Time
570 case 'Y':
571 instructions.emplace_back(context, FormatType::Year, width, left_align, center_align, truncate, strip_ansi);
572 break;
573 case 'm':
574 if ((i + 1) < pattern.size() && pattern[i + 1] == 's') {
575 instructions.emplace_back(context, FormatType::MilliSecondTimeStamp, width, left_align, center_align, truncate, strip_ansi);
576 ++i; // Skip 's' after 'm'
577 break;
578 }
579 instructions.emplace_back(context, FormatType::Month, width, left_align, center_align, truncate, strip_ansi);
580 break;
581 case 'd':
582 instructions.emplace_back(context, FormatType::Day, width, left_align, center_align, truncate, strip_ansi);
583 break;
584 case 'H':
585 instructions.emplace_back(context, FormatType::Hour, width, left_align, center_align, truncate, strip_ansi);
586 break;
587 case 'M':
588 instructions.emplace_back(context, FormatType::Minute, width, left_align, center_align, truncate, strip_ansi);
589 break;
590 case 'S':
591 if ((i + 1) < pattern.size() && pattern[i + 1] == 'C') {
592 strip_ansi = true;
593 ++i; // Skip 'C' after 'S'
594 break;
595 }
596 instructions.emplace_back(context, FormatType::Second, width, left_align, center_align, truncate, strip_ansi);
597 break;
598 case 'e':
599 if ((i + 1) < pattern.size() && pattern[i + 1] == 'c') {
600 strip_ansi = false;
601 ++i; // Skip 'c' after 'e'
602 break;
603 }
604 instructions.emplace_back(context, FormatType::Millisecond, width, left_align, center_align, truncate, strip_ansi);
605 break;
606 case 'C':
607 instructions.emplace_back(context, FormatType::TwoDigitYear, width, left_align, center_align, truncate, strip_ansi);
608 break;
609 case 'c':
610 instructions.emplace_back(context, FormatType::DateTime, width, left_align, center_align, truncate, strip_ansi);
611 break;
612 case 'D':
613 instructions.emplace_back(context, FormatType::ShortDate, width, left_align, center_align, truncate, strip_ansi);
614 break;
615 case 'T':
616 case 'X':
617 instructions.emplace_back(context, FormatType::TimeISO8601, width, left_align, center_align, truncate, strip_ansi);
618 break;
619 case 'F':
620 instructions.emplace_back(context, FormatType::DateISO8601, width, left_align, center_align, truncate, strip_ansi);
621 break;
622 case 's':
623 if ((i + 1) < pattern.size() && pattern[i + 1] == 'c') {
624 strip_ansi = true;
625 ++i; // Skip 'c' after 's'
626 break;
627 } else {
628 instructions.emplace_back(context, FormatType::TimeStamp, width, left_align, center_align, truncate, strip_ansi);
629 }
630 break;
631 case 'E':
632 if ((i + 1) < pattern.size() && pattern[i + 1] == 'C') {
633 strip_ansi = false;
634 ++i; // Skip 'C' after 'E'
635 break;
636 }
637 instructions.emplace_back(context, FormatType::TimeStamp, width, left_align, center_align, truncate, strip_ansi);
638 break;
639 // Weekday and Month Names
640 case 'b':
641 if ((i + 1) < pattern.size() && pattern[i + 1] == 's') {
642 instructions.emplace_back(context, FormatType::FileName, width, left_align, center_align, truncate, strip_ansi);
643 ++i; // Skip 's' after 'b'
644 break;
645 }
646 instructions.emplace_back(context, FormatType::AbbreviatedMonthName, width, left_align, center_align, truncate, strip_ansi);
647 break;
648 case 'B':
649 instructions.emplace_back(context, FormatType::FullMonthName, width, left_align, center_align, truncate, strip_ansi);
650 break;
651 case 'a':
652 instructions.emplace_back(context, FormatType::AbbreviatedWeekdayName, width, left_align, center_align, truncate, strip_ansi);
653 break;
654 case 'A':
655 instructions.emplace_back(context, FormatType::FullWeekdayName, width, left_align, center_align, truncate, strip_ansi);
656 break;
657
658 // Log Level
659 case 'l':
660 instructions.emplace_back(context, FormatType::LogLevel, width, left_align, center_align, truncate, strip_ansi);
661 break;
662 case 'L':
663 instructions.emplace_back(context, FormatType::ShortLogLevel, width, left_align, center_align, truncate, strip_ansi);
664 break;
665
666 // Thread
667 case 't':
668 instructions.emplace_back(context, FormatType::ThreadId, width, left_align, center_align, truncate, strip_ansi);
669 break;
670
671 // Diagnostic context
672 case 'K':
673 if ((i + 1) < pattern.size() && pattern[i + 1] == '{') {
674 size_t end = pattern.find('}', i + 2);
675 if (end != std::string::npos) {
676 std::string key = pattern.substr(i + 2, end - i - 2);
677 instructions.emplace_back(context, FormatType::MappedDiagnosticContextValue, width, left_align, center_align, truncate, strip_ansi, key);
678 i = end;
679 break;
680 }
681 }
682 instructions.emplace_back(context, FormatType::MappedDiagnosticContext, width, left_align, center_align, truncate, strip_ansi);
683 break;
684 case 'J':
685 instructions.emplace_back(context, FormatType::NestedDiagnosticContext, width, left_align, center_align, truncate, strip_ansi);
686 break;
687
688 // File and Function
689 case 'f':
690 if ((i + 1) < pattern.size() && pattern[i + 1] == 'f' && (i + 2) < pattern.size() && pattern[i + 2] == 'n') {
691 instructions.emplace_back(context, FormatType::FullFileName, width, left_align, center_align, truncate, strip_ansi);
692 i += 2; // Skip 'fn' after 'f'
693 break;
694 }
695 if ((i + 1) < pattern.size() && pattern[i + 1] == 'n') {
696 instructions.emplace_back(context, FormatType::FileName, width, left_align, center_align, truncate, strip_ansi);
697 ++i; // Skip 'n' after 'f'
698 break;
699 }
700 instructions.emplace_back(context, FormatType::FileName, width, left_align, center_align, truncate, strip_ansi);
701 break;
702 case 'g':
703 instructions.emplace_back(context, FormatType::FullFileName, width, left_align, center_align, truncate, strip_ansi);
704 break;
705 case '@':
706 instructions.emplace_back(context, FormatType::SourceFileAndLine, width, left_align, center_align, truncate, strip_ansi);
707 break;
708 case '#':
709 instructions.emplace_back(context, FormatType::LineNumber, width, left_align, center_align, truncate, strip_ansi);
710 break;
711 case '!':
712 instructions.emplace_back(context, FormatType::FunctionName, width, left_align, center_align, truncate, strip_ansi);
713 break;
714
715 // Color
716 case '^':
717 instructions.emplace_back(context, FormatType::StartColor, 0, false, false, false, strip_ansi);
718 break;
719 case '$':
720 instructions.emplace_back(context, FormatType::EndColor, 0, false, false, false, strip_ansi);
721 break;
722
723 // Message
724 case 'v':
725 instructions.emplace_back(context, FormatType::Message, width, left_align, center_align, truncate, strip_ansi);
726 break;
727
728 //
729 case 'N':
730 if ((i + 2) < pattern.size() && pattern[i + 1] == '(') {
731 size_t end = pattern.find(')', i + 2);
732 if (end == std::string::npos) {
733 ++i;
734 break;
735 }
736 std::string params = pattern.substr(i + 2, end - i - 2);
737 auto no_args_instructions = compile(params, CompileContext::NoArgsFallback);
738 instructions.insert(instructions.end(), no_args_instructions.begin(), no_args_instructions.end());
739 i = end;
740 }
741 break;
742
743 // Escape character or unknown
744 case '%':
745 default:
746 buffer += next; // Unrecognized symbols are recorded as text
747 break;
748 };
749 }
750 } else {
751 buffer += c;
752 }
753 }
754
755 if (!buffer.empty()) {
756 instructions.push_back(FormatInstruction(context, buffer, strip_ansi));
757 }
758 return instructions;
759 }
760 }; // PatternCompiler
761
762}; // namespace logit
763
764#endif // LOGIT_CPP_HEADER_LOGIT_CPP_LOGIT_FORMATTER_COMPILER_PATTERNCOMPILER_HPP_INCLUDED
Compiler for log formatting patterns.
static std::vector< FormatInstruction > compile(const std::string &pattern, CompileContext context=CompileContext::Default)
Compiles a pattern string into a list of format instructions.
#define LOGIT_DEFAULT_COLOR
Defines the default color for console output.
Definition config.hpp:22
The primary namespace for the LogIt++ library.
std::string to_string(LogLevel level, int mode=0)
Convert LogLevel to a std::string representation.
Definition enums.hpp:109
std::string get_log_level_color(LogLevel log_level)
Get the ANSI color code associated with a log level.
Definition enums.hpp:150
Structure to store log formatting instructions.
FormatType type
The type of the format instruction.
int width
Width for formatting.
bool truncate
Truncation flag.
std::string static_text
Used only if type == StaticText.
bool strip_ansi
Removes ANSI escape codes (e.g., colors) if true.
FormatInstruction(CompileContext context, FormatType type, int width=0, bool left=false, bool center=false, bool trunc=false, bool strip_ansi=false, const std::string &context_key=std::string())
Constructor for other types.
bool center_align
Center alignment flag.
void apply(StreamType &oss, const LogRecord &record, const time_shield::DateTimeStruct &dt) const
Apply formatting considering alignment and width.
FormatType
Possible types of instructions for log formatting.
@ FullWeekdayName
A: Full weekday name
@ TimeStamp
s or E: Timestamp in seconds
@ FileName
f, fn, bs: Basename of the source file
@ SourceFileAndLine
%@: Source file and line number
@ StartColor
%^: Start of color range
@ MappedDiagnosticContextValue
K{key}: Mapped diagnostic context value by key
@ AbbreviatedWeekdayName
a: Abbreviated weekday name
@ EndColor
$: End of color range
@ FullFileName
g, ffn: Full file path
@ DateISO8601
F: Date in ISO 8601 format (Y-m-d)
@ ShortDate
D: Short date (m/d/y)
@ MilliSecondTimeStamp
ms: Timestamp in milliseconds
@ AbbreviatedMonthName
b: Abbreviated month name
@ MappedDiagnosticContext
K: All mapped diagnostic context values
@ NestedDiagnosticContext
J: Nested diagnostic context stack
@ TimeISO8601
T or X: Time in ISO 8601 format (H:M:S)
CompileContext context
Compilation context, e.g., default or handling no-argument cases.
FormatInstruction(CompileContext context, const std::string &text, bool strip_ansi)
Constructor for static text.
CompileContext
Compilation context for handling special cases, such as when arguments are missing.
@ Default
Standard behavior without modifications.
@ NoArgsFallback
Handle a special pattern for cases with no arguments.
bool left_align
Left alignment flag.
std::string remove_ansi_escape_codes(const std::string &input) const
Removes ANSI escape codes (including color codes and cursor movement) from a string.
std::string context_key
Optional MDC key for context formatting.
Stores log metadata and content.
Definition LogRecord.hpp:17
const std::string function
Function name.
Definition LogRecord.hpp:22
const int line
Line number in the source file.
Definition LogRecord.hpp:21
std::thread::id thread_id
ID of the logging thread.
Definition LogRecord.hpp:27
const LogLevel log_level
Log level (severity).
Definition LogRecord.hpp:18
const int64_t timestamp_ms
Timestamp in milliseconds.
Definition LogRecord.hpp:19
const bool fmt_mode
Flag indicating if fmt formatting should be used.
Definition LogRecord.hpp:30
const std::string file
Source file name.
Definition LogRecord.hpp:20
const std::string format
Format string for the message.
Definition LogRecord.hpp:23
std::vector< VariableValue > args_array
Argument values for the log.
Definition LogRecord.hpp:26
const bool print_mode
Flag to determine whether arguments are printed in a raw format without special symbols.
Definition LogRecord.hpp:29
ValueType
Enumeration of possible value types for VariableValue.