{"id":153,"date":"2025-09-26T13:36:00","date_gmt":"2025-09-26T11:36:00","guid":{"rendered":"https:\/\/microsproject.dev\/?p=153"},"modified":"2025-09-25T14:07:55","modified_gmt":"2025-09-25T12:07:55","slug":"building-a-lightweight-logging-framework-for-micros-part-2-coloring-and-structuring-logs","status":"publish","type":"post","link":"https:\/\/microsproject.dev\/index.php\/2025\/09\/26\/building-a-lightweight-logging-framework-for-micros-part-2-coloring-and-structuring-logs\/","title":{"rendered":"Building a Lightweight Logging Framework for MicrOS \u2013 Part 2: Coloring and Structuring Logs"},"content":{"rendered":"\n<p>In <a href=\"https:\/\/microsproject.dev\/index.php\/2025\/09\/25\/building-a-lightweight-logging-framework-for-micros-part-1-why-logging-matters-in-an-rtos\/\" target=\"_blank\" rel=\"noreferrer noopener\">Part 1<\/a> of this series, I explained why logging matters for MicrOS and how we started with a simple design: log levels, a global ceiling, and per-module registration. That gave us the skeleton of a logging system \u2014 but the output was still plain and hard to scan.<\/p>\n\n\n\n<p>In this part, we\u2019ll bring logs to life by adding <strong>VT100 colors, severity tags, and structured formatting<\/strong>. This way, you can spot errors immediately, follow debug traces line by line, and keep your terminal readable even when dozens of modules are talking at once.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Adding Color with VT100 Escape Codes<\/h2>\n\n\n\n<p>Most serial terminals and QEMU consoles understand <strong>VT100 escape codes<\/strong>. These are simple strings that tell the terminal to switch text color. For example:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>\\033[31m<\/code> \u2192 red<\/li>\n\n\n\n<li><code>\\033[32m<\/code> \u2192 green<\/li>\n\n\n\n<li><code>\\033[33m<\/code> \u2192 yellow<\/li>\n\n\n\n<li><code>\\033[34m<\/code> \u2192 blue<\/li>\n\n\n\n<li><code>\\033[0m<\/code> \u2192 reset<\/li>\n<\/ul>\n\n\n\n<p>We map each severity level to a color:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#define MICROS_LOG_ESCAPE_CODE_COLOR_RED    \"\\033&#91;31m\"\n#define MICROS_LOG_ESCAPE_CODE_COLOR_GREEN  \"\\033&#91;32m\"\n#define MICROS_LOG_ESCAPE_CODE_COLOR_YELLOW \"\\033&#91;33m\"\n#define MICROS_LOG_ESCAPE_CODE_COLOR_BLUE   \"\\033&#91;34m\"\n#define MICROS_LOG_ESCAPE_CODE_COLOR_RESET  \"\\033&#91;0m\"\n<\/code><\/pre>\n\n\n\n<p>Errors stand out in red, warnings in yellow, informational logs in green, and debug traces in blue.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Formatting Messages<\/h2>\n\n\n\n<p>The <code>_log<\/code> function now does three things:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Selects the <strong>color<\/strong> and <strong>severity tag<\/strong> (<code>E<\/code>, <code>W<\/code>, <code>I<\/code>, <code>D<\/code>).<\/li>\n\n\n\n<li>Prints either a <strong>compact message<\/strong> (for info\/warn\/error) or a <strong>detailed trace<\/strong> (for debug).<\/li>\n\n\n\n<li>Resets the color so the terminal stays clean.<\/li>\n<\/ol>\n\n\n\n<p>Here\u2019s the core logic:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>switch (level) {\n    case MICROS_LOG_LEVEL_ERROR:\n        color_escape_str = MICROS_LOG_ESCAPE_CODE_COLOR_RED;\n        level_str = \"E\";\n        break;\n    case MICROS_LOG_LEVEL_WARN:\n        color_escape_str = MICROS_LOG_ESCAPE_CODE_COLOR_YELLOW;\n        level_str = \"W\";\n        break;\n    case MICROS_LOG_LEVEL_INFO:\n        color_escape_str = MICROS_LOG_ESCAPE_CODE_COLOR_GREEN;\n        level_str = \"I\";\n        break;\n    case MICROS_LOG_LEVEL_DEBUG:\n        color_escape_str = MICROS_LOG_ESCAPE_CODE_COLOR_BLUE;\n        level_str = \"D\";\n        break;\n}\n<\/code><\/pre>\n\n\n\n<p>For <strong>debug logs<\/strong>, we include file and line:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fprintf(output, \"&#91;%s] &#91;%s] --%s:%d-- \",\n        level_str, module_name, file, line);\n<\/code><\/pre>\n\n\n\n<p>For everything else:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fprintf(output, \"&#91;%s] &#91;%s] \", level_str, module_name);\n<\/code><\/pre>\n\n\n\n<p>Finally, we call <code>vfprintf()<\/code> with the user\u2019s message and reset the color:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>vfprintf(output, fmt, args);\nfprintf(output, MICROS_LOG_ESCAPE_CODE_COLOR_RESET \"\\n\");\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Real Logs in Action<\/h2>\n\n\n\n<p>Here\u2019s what it looks like when running the <strong><code>init_functions<\/code><\/strong> sample in QEMU:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;D] &#91;main] --\/home\/grzegorz\/projects\/micros\/samples\/init_functions\/main.c:22-- System clock initialized - simulated\n&#91;D] &#91;main] --\/home\/grzegorz\/projects\/micros\/samples\/init_functions\/main.c:26-- UART initialized - simulated\n&#91;D] &#91;main] --\/home\/grzegorz\/projects\/micros\/samples\/init_functions\/main.c:31-- System initialized successfully - simulated\n&#91;D] &#91;main] --\/home\/grzegorz\/projects\/micros\/samples\/init_functions\/main.c:35-- Very early init function in section .init_array.50 - simulated\n&#91;I] &#91;main] MicrOS Initialization Framework Example\n&#91;I] &#91;main] System is up and running!\n&#91;I] &#91;main] Main function is exiting now.\n&#91;I] &#91;boot] Main function returned with code 0\n&#91;D] &#91;main] --\/home\/grzegorz\/projects\/micros\/samples\/init_functions\/main.c:39-- Very late fini function in section .fini_array - simulated\n&#91;I] &#91;boot] System halted after main return\n<\/code><\/pre>\n\n\n\n<p>In a terminal, those lines appear:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Blue<\/strong> for debug <code>[D]<\/code> (with file and line).<\/li>\n\n\n\n<li><strong>Green<\/strong> for info <code>[I]<\/code>.<\/li>\n\n\n\n<li><strong>Yellow<\/strong> for warnings <code>[W]<\/code>.<\/li>\n\n\n\n<li><strong>Red<\/strong> for errors <code>[E]<\/code>.<\/li>\n<\/ul>\n\n\n\n<p>This makes it instantly obvious what\u2019s routine, what\u2019s important, and what\u2019s catastrophic.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Why File and Line for Debug?<\/h2>\n\n\n\n<p>You\u2019ll notice that only debug logs show the full <code>--file:line--<\/code> prefix. That\u2019s intentional:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Debugging sessions<\/strong> often require pinpoint accuracy \u2014 \u201cwhich path hit this code?\u201d<\/li>\n\n\n\n<li><strong>Normal operation<\/strong> should remain readable and concise.<\/li>\n<\/ul>\n\n\n\n<p>This balance keeps everyday logs short, while still letting you crank up detail when hunting a bug.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Where We Stand<\/h2>\n\n\n\n<p>At this point, our logging system has:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Severity levels (<code>E<\/code>, <code>W<\/code>, <code>I<\/code>, <code>D<\/code>).<\/li>\n\n\n\n<li>VT100 colors for instant readability.<\/li>\n\n\n\n<li>File\/line context for debug logs.<\/li>\n\n\n\n<li>Clean reset after each line.<\/li>\n<\/ul>\n\n\n\n<p>Already, this makes MicrOS much easier to work with than raw <code>printf<\/code>. But we\u2019re not done yet.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Coming Up Next<\/h2>\n\n\n\n<p>In <strong>Part 3<\/strong>, we\u2019ll add <strong>compile-time and per-module filtering<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A global ceiling (<code>CONFIG_MICROS_LOG_LEVEL<\/code>).<\/li>\n\n\n\n<li><code>MICROS_LOG_REGISTER(module, level)<\/code> for per-module defaults.<\/li>\n\n\n\n<li>Macros like <code>D()<\/code>, <code>I()<\/code>, <code>W()<\/code>, and <code>E()<\/code> that automatically check both.<\/li>\n<\/ul>\n\n\n\n<p>That\u2019s where logging really starts to feel powerful: each part of the OS can speak at its own volume, and you can globally tune the noise.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>\ud83d\udc49 Stay tuned for <strong>Part 3: Compile-Time and Per-Module Filtering<\/strong> \u2014 where we turn the logging API into something you can really depend on in production.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In Part 1 of this series, I explained why logging matters for MicrOS and how we started with a simple design: log levels, a global ceiling, and per-module registration. That gave us the skeleton of a logging system \u2014 but the output was still plain and hard to scan. In this part, we\u2019ll bring logs [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6,7],"tags":[],"class_list":["post-153","post","type-post","status-publish","format-standard","hentry","category-development","category-logging"],"blocksy_meta":[],"_links":{"self":[{"href":"https:\/\/microsproject.dev\/index.php\/wp-json\/wp\/v2\/posts\/153","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/microsproject.dev\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/microsproject.dev\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/microsproject.dev\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/microsproject.dev\/index.php\/wp-json\/wp\/v2\/comments?post=153"}],"version-history":[{"count":1,"href":"https:\/\/microsproject.dev\/index.php\/wp-json\/wp\/v2\/posts\/153\/revisions"}],"predecessor-version":[{"id":154,"href":"https:\/\/microsproject.dev\/index.php\/wp-json\/wp\/v2\/posts\/153\/revisions\/154"}],"wp:attachment":[{"href":"https:\/\/microsproject.dev\/index.php\/wp-json\/wp\/v2\/media?parent=153"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/microsproject.dev\/index.php\/wp-json\/wp\/v2\/categories?post=153"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/microsproject.dev\/index.php\/wp-json\/wp\/v2\/tags?post=153"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}