ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
format.c File Reference

Log format parser implementation. More...

Go to the source code of this file.

Functions

log_template_t * log_template_parse (const char *format_str, bool console_only)
 
void log_template_free (log_template_t *format)
 
int log_template_apply (const log_template_t *format, char *buf, size_t buf_size, log_level_t level, const char *timestamp, const char *file, int line, const char *func, uint64_t tid, const char *message, bool use_colors, uint64_t time_nanoseconds)
 

Detailed Description

Log format parser implementation.

Definition in file log/format.c.

Function Documentation

◆ log_template_apply()

int log_template_apply ( const log_template_t *  format,
char *  buf,
size_t  buf_size,
log_level_t  level,
const char *  timestamp,
const char *  file,
int  line,
const char *  func,
uint64_t  tid,
const char *  message,
bool  use_colors,
uint64_t  time_nanoseconds 
)

Definition at line 455 of file log/format.c.

457 {
458 if (!format || !buf || buf_size == 0) {
459 return -1;
460 }
461
462 int total_written = 0;
463 char *p = buf;
464 size_t remaining = buf_size - 1; /* Reserve space for null terminator */
465
466 (void)timestamp; /* May be used in future; for now, LOG_FORMAT_TIME uses custom formatting */
467
468 for (size_t i = 0; i < format->spec_count; i++) {
469 const log_format_spec_t *spec = &format->specs[i];
470 int written = 0;
471
472 switch (spec->type) {
473 case LOG_FORMAT_LITERAL:
474 written = safe_snprintf(p, remaining + 1, "%s", spec->literal ? spec->literal : "");
475 break;
476
477 case LOG_FORMAT_TIME:
478 /* Format time using custom time formatter */
479 if (spec->literal) {
480 written = time_format_now(spec->literal, p, remaining + 1);
481 if (written <= 0) {
482 log_debug("time_format_now failed for format: %s", spec->literal);
483 written = 0;
484 }
485 }
486 break;
487
488 case LOG_FORMAT_LEVEL:
489 /* Log level as string (DEV, DEBUG, INFO, WARN, ERROR, FATAL) */
490 written = safe_snprintf(p, remaining + 1, "%s", get_level_string(level));
491 break;
492
493 case LOG_FORMAT_LEVEL_ALIGNED:
494 /* Log level padded to exactly 5 characters (prevents truncation to [DEBU]/[ERRO]) */
495 written = safe_snprintf(p, remaining + 1, "%s", get_level_string_padded(level));
496 break;
497
498 case LOG_FORMAT_FILE:
499 if (file) {
500 written = safe_snprintf(p, remaining + 1, "%s", file);
501 }
502 break;
503
504 case LOG_FORMAT_FILE_RELATIVE:
505 if (file) {
506 const char *rel_file = extract_project_relative_path(file);
507 written = safe_snprintf(p, remaining + 1, "%s", rel_file);
508 }
509 break;
510
511 case LOG_FORMAT_LINE:
512 if (line > 0) {
513 written = safe_snprintf(p, remaining + 1, "%d", line);
514 }
515 break;
516
517 case LOG_FORMAT_FUNC:
518 if (func) {
519 written = safe_snprintf(p, remaining + 1, "%s", func);
520 }
521 break;
522
523 case LOG_FORMAT_TID:
524 written = safe_snprintf(p, remaining + 1, "%llu", (unsigned long long)tid);
525 break;
526
527 case LOG_FORMAT_MICROSECONDS: {
528 /* Extract microseconds from nanoseconds (ns_value % 1_000_000_000 / 1000) */
529 long nanoseconds = (long)(time_nanoseconds % NS_PER_SEC_INT);
530 long microseconds = nanoseconds / 1000;
531 if (microseconds < 0)
532 microseconds = 0;
533 if (microseconds > 999999)
534 microseconds = 999999;
535 written = safe_snprintf(p, remaining + 1, "%06ld", microseconds);
536 break;
537 }
538
539 case LOG_FORMAT_NANOSECONDS: {
540 /* Extract nanoseconds component (ns_value % 1_000_000_000) */
541 long nanoseconds = (long)(time_nanoseconds % NS_PER_SEC_INT);
542 if (nanoseconds < 0)
543 nanoseconds = 0;
544 if (nanoseconds > 999999999)
545 nanoseconds = 999999999;
546 written = safe_snprintf(p, remaining + 1, "%09ld", nanoseconds);
547 break;
548 }
549
550 case LOG_FORMAT_STRFTIME_CODE: {
551 /* strftime format code (like %H, %M, %S, %A, %B, etc.)
552 * Let strftime handle all the parsing and validation */
553 if (spec->literal) {
554 /* Construct format string with % prefix */
555 size_t fmt_len = spec->literal_len + 1;
556 char *format_str = SAFE_MALLOC(fmt_len + 1, char *);
557 if (format_str) {
558 format_str[0] = '%';
559 memcpy(format_str + 1, spec->literal, spec->literal_len);
560 format_str[fmt_len] = '\0';
561 written = time_format_now(format_str, p, remaining + 1);
562 if (written <= 0) {
563 log_debug("time_format_now failed for format code: %s", format_str);
564 written = 0;
565 }
566 SAFE_FREE(format_str);
567 }
568 }
569 break;
570 }
571
572 case LOG_FORMAT_MESSAGE:
573 if (message) {
574 written = safe_snprintf(p, remaining + 1, "%s", message);
575 }
576 break;
577
578 case LOG_FORMAT_COLORED_MESSAGE: {
579 /* Apply colorize_log_message() for number/unit/hex highlighting (keeps text white) */
580 if (message) {
581 const char *colorized_msg = colorize_log_message(message);
582 written = safe_snprintf(p, remaining + 1, "%s", colorized_msg);
583 }
584 break;
585 }
586
587 case LOG_FORMAT_COLORLOG_LEVEL:
588 /* Color code for the log level (placeholder for future color support) */
589 (void)use_colors; /* Suppress unused parameter warning */
590 written = 0;
591 break;
592
593 case LOG_FORMAT_COLOR: {
594 /* Parse and apply %color(LEVEL, content)
595 * literal stores "LEVEL,content" where LEVEL is the color level name (or "*" for current level)
596 * and content is a format string to render and colorize */
597 if (!spec->literal || spec->literal_len == 0) {
598 written = 0;
599 break;
600 }
601
602 /* Find the comma separating LEVEL and content */
603 const char *comma_pos = strchr(spec->literal, ',');
604 if (!comma_pos) {
605 /* Invalid format - no comma found */
606 log_debug("log_template_apply: %%color format missing comma in: %s", spec->literal);
607 written = 0;
608 break;
609 }
610
611 /* Extract level name (before comma) */
612 size_t level_len = comma_pos - spec->literal;
613 char level_name[32];
614 if (level_len >= sizeof(level_name)) {
615 /* Level name too long */
616 written = 0;
617 break;
618 }
619 memcpy(level_name, spec->literal, level_len);
620 level_name[level_len] = '\0';
621
622 /* Parse level name to log_color_t (pass current level for "*" support) */
623 log_color_t color = parse_color_level(level_name, level);
624
625 /* Extract content (after comma), skip leading whitespace */
626 const char *content_start = comma_pos + 1;
627 while (*content_start == ' ' || *content_start == '\t') {
628 content_start++;
629 }
630
631 /* Render content recursively */
632 char content_buf[512];
633 int content_len = render_format_content(content_start, content_buf, sizeof(content_buf), level, timestamp, file,
634 line, func, tid, message, use_colors, time_nanoseconds);
635
636 if (content_len < 0 || content_len >= (int)sizeof(content_buf)) {
637 written = 0;
638 break;
639 }
640
641 /* Apply color to rendered content using colored_string */
642 const char *colored_content = colored_string(color, content_buf);
643
644 /* Copy colored content to output buffer */
645 written = safe_snprintf(p, remaining + 1, "%s", colored_content);
646 break;
647 }
648
649 case LOG_FORMAT_NEWLINE:
650 /* Platform-aware newline */
651#ifdef _WIN32
652 written = safe_snprintf(p, remaining + 1, "\r\n");
653#else
654 written = safe_snprintf(p, remaining + 1, "\n");
655#endif
656 break;
657
658 default:
659 break;
660 }
661
662 if (written < 0) {
663 /* snprintf error */
664 return -1;
665 }
666
667 if ((size_t)written > remaining) {
668 /* Buffer overflow prevention */
669 log_debug("log_template_apply: buffer overflow prevented");
670 return -1;
671 }
672
673 p += written;
674 remaining -= written;
675 total_written += written;
676 }
677
678 *p = '\0';
679 return total_written;
680}
const char * colorize_log_message(const char *message)
Colorize a log message for terminal output.
Definition colorize.c:513
const char * get_level_string_padded(log_level_t level)
Get padded level string for consistent alignment.
const char * extract_project_relative_path(const char *file)
Definition path.c:410
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:456
const char * colored_string(log_color_t color, const char *text)
int time_format_now(const char *format_str, char *buf, size_t buf_size)
Definition util/time.c:558

References colored_string(), colorize_log_message(), extract_project_relative_path(), get_level_string_padded(), safe_snprintf(), and time_format_now().

Referenced by platform_print_backtrace_symbols().

◆ log_template_free()

void log_template_free ( log_template_t *  format)

Definition at line 293 of file log/format.c.

293 {
294 if (!format) {
295 SET_ERRNO(ERROR_INVALID_PARAM, "null format");
296 return;
297 }
298
299 if (format->specs) {
300 for (size_t i = 0; i < format->spec_count; i++) {
301 if (format->specs[i].literal) {
302 SAFE_FREE(format->specs[i].literal);
303 }
304 }
305 SAFE_FREE(format->specs);
306 }
307
308 if (format->original) {
309 SAFE_FREE(format->original);
310 }
311
312 SAFE_FREE(format);
313}

Referenced by log_destroy(), and log_set_format().

◆ log_template_parse()

log_template_t * log_template_parse ( const char *  format_str,
bool  console_only 
)

Definition at line 289 of file log/format.c.

289 {
290 return parse_format_string(format_str, console_only);
291}

Referenced by log_set_format().