From c481c5c97bc2c72eecdc8356383d535130ae1713 Mon Sep 17 00:00:00 2001 From: David Cozens Date: Sat, 15 Aug 2026 10:12:43 +0100 Subject: [PATCH] feat: install the SolidSyslog error handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nothing in the library fails loudly. A _Create that cannot succeed substitutes a Null object and reports it rather than returning NULL, so a logger that has silently stopped looks exactly like one with nothing to say. The handler is what tells them apart, and it goes in before the first _Create rather than after something looks wrong. Flash +404 B RAM +8 B The handler prints to the same console as everything else, so a fault lands in the run report next to the rest of what the device did. It names the four lifecycle categories a misconfigured integration raises and prints the rest numerically — a device reacting to those would switch on the category, not the text. It is more than an integration aid. The handler is the seam into the device's own error and health reporting, and later stages use it at run time: an edge-triggered warning when the collector becomes unreachable, a notice when delivery recovers. Co-Authored-By: Claude Opus 5 (1M context) --- CMakeLists.txt | 2 + README.md | 36 ++++++++----- app/main.c | 4 ++ app/syslog/SyslogErrorHandler.c | 89 +++++++++++++++++++++++++++++++++ app/syslog/SyslogErrorHandler.h | 15 ++++++ measurements/error-handler.csv | 13 +++++ measurements/stages.tsv | 1 + run-report.md | 26 +++++----- 8 files changed, 160 insertions(+), 26 deletions(-) create mode 100644 app/syslog/SyslogErrorHandler.c create mode 100644 app/syslog/SyslogErrorHandler.h create mode 100644 measurements/error-handler.csv diff --git a/CMakeLists.txt b/CMakeLists.txt index a13d066..3b193b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,6 +130,7 @@ add_executable(baseline ${APP_DIR}/net/EthernetIf.c ${APP_DIR}/storage/diskio.c ${APP_DIR}/storage/SemihostingDisk.c + ${APP_DIR}/syslog/SyslogErrorHandler.c $ ) @@ -150,6 +151,7 @@ target_include_directories(baseline PRIVATE ${APP_DIR}/net/smsc9220 ${APP_DIR}/platform # CmsdkUart.h, SemihostingExit.h, SemihostingIo.h ${APP_DIR}/storage # SemihostingDisk.h + ${APP_DIR}/syslog # SyslogErrorHandler.h ${FREERTOS_KERNEL_PATH}/include ${FREERTOS_PORT_DIR} ${LWIP_DIR}/src/include diff --git a/README.md b/README.md index d3e5ea7..a417d3d 100644 --- a/README.md +++ b/README.md @@ -10,26 +10,35 @@ It builds on a baseline that simulates the sort of device you might be adding th measures itself: see [docs/baseline.md](docs/baseline.md) for what the baseline is, how the figures are made, and how to run it. -## This stage — Linked +## This stage — Error handler -SolidSyslog is linked into the application without any of it being called. The stage is broken out -for clarity: it separates getting the build to accept the library from getting the device to use -it, so anything that goes wrong here is a build problem and nothing else. +Install the handler before any other call into SolidSyslog. -Three lines carry it. `FetchContent` nests the library under this build. `SOLIDSYSLOG_PLATFORMS` -names the platforms rather than letting the library infer them from the environment — lwIP alone, -because nothing at this stage reaches any other pack. Then one link line, for the core library and -that pack. +```c +SolidSyslog_SetErrorHandler(OnSyslogError, NULL); +``` -`--gc-sections` discards what nothing calls, so a platform pack that is linked but unused does not -reach the image. +Nothing in the library fails loudly. A `_Create` that cannot succeed substitutes a Null object and +reports it rather than returning `NULL`, so a logger that has silently stopped looks exactly like +one with nothing to say. The handler is what tells the two apart, which is why it goes in before +the first `_Create` and not after something looks wrong. It reports many misconfiguration errors, +and can save significant time while integrating. -For now you need only the core and a network platform. The library has no release tag yet, so the -pin is a commit. +This is not only an integration aid. The handler is the seam into the device's own error and health +reporting, and it stays valuable at run time: later stages raise an edge-triggered warning when the +collector becomes unreachable and a notice when delivery recovers. Route it wherever the device +already routes faults — here, the same console as everything else, so a fault lands in the run +report next to the rest of what the device did. + +The handler names the four lifecycle categories a misconfigured integration raises and prints the +rest numerically. A device reacting to a fault would switch on the category rather than the text. + +**When you need it.** Every device, and first. It is the only thing standing between a +misconfigured logger and a silent one. -**Cost above baseline: Flash +0 B, RAM +0 B.** +**Cost above baseline: Flash +404 B, RAM +8 B.** @@ -44,6 +53,7 @@ committed as [`run-report.md`](run-report.md), and rewritten by every stage. |---|---|---|---| | Baseline | a device that already networks, stores, and holds an mTLS session — before any syslog | — | — | | Linked | the core library and lwIP raw-mode networking, linked but not yet called | +0 | +0 | +| Error handler | a fault inside the logger reaches the console instead of being silent | +404 | +8 | *Deltas are bytes above the baseline, which is itself Flash 350,308 B, RAM 111,192 B.* diff --git a/app/main.c b/app/main.c index db87cfc..b5a7b11 100644 --- a/app/main.c +++ b/app/main.c @@ -9,6 +9,7 @@ #include "SemihostingExit.h" #include "ServiceTask.h" #include "SimulatedExistingApp.h" +#include "SyslogErrorHandler.h" #include "lwip/tcpip.h" @@ -93,6 +94,9 @@ int main(void) CmsdkUart_Init(&UART_ACCESS, DEVICE_UART0_BASE); (void) printf("[device] solid-syslog-example (FreeRTOS + lwIP + mbedTLS + FatFs)\n"); + /* Before the first _Create — see SyslogErrorHandler.h for why that matters. */ + SyslogErrorHandler_Install(); + /* lwIP tcpip thread + core-lock mutex + mbox. Pre-scheduler safe. */ tcpip_init(NULL, NULL); diff --git a/app/syslog/SyslogErrorHandler.c b/app/syslog/SyslogErrorHandler.c new file mode 100644 index 0000000..4c6f76a --- /dev/null +++ b/app/syslog/SyslogErrorHandler.c @@ -0,0 +1,89 @@ +/* See SyslogErrorHandler.h. Prints to the same console as the rest of the + * device, so a fault shows up in the run report next to everything else. */ + +#include "SyslogErrorHandler.h" + +#include "SolidSyslogError.h" +#include "SolidSyslogErrorCategory.h" +#include "SolidSyslogPrival.h" + +#include +#include + +static const char* SeverityName(enum SolidSyslogSeverity severity) +{ + switch (severity) + { + case SOLIDSYSLOG_SEVERITY_EMERGENCY: + return "EMERGENCY"; + case SOLIDSYSLOG_SEVERITY_ALERT: + return "ALERT"; + case SOLIDSYSLOG_SEVERITY_CRITICAL: + return "CRITICAL"; + case SOLIDSYSLOG_SEVERITY_ERROR: + return "ERROR"; + case SOLIDSYSLOG_SEVERITY_WARNING: + return "WARNING"; + case SOLIDSYSLOG_SEVERITY_NOTICE: + return "NOTICE"; + case SOLIDSYSLOG_SEVERITY_INFORMATIONAL: + return "INFO"; + case SOLIDSYSLOG_SEVERITY_DEBUG: + return "DEBUG"; + } + return "?"; +} + +/* The four a misconfigured integration raises. Role-specific categories sit in + * ranges above 0x0100 and print numerically. */ +static const char* CategoryName(uint16_t category) +{ + switch (category) + { + case SOLIDSYSLOG_CAT_BAD_CONFIG: + return "bad-config"; + case SOLIDSYSLOG_CAT_BAD_ARGUMENT: + return "bad-argument"; + case SOLIDSYSLOG_CAT_POOL_EXHAUSTED: + return "pool-exhausted"; + case SOLIDSYSLOG_CAT_UNKNOWN_DESTROY: + return "unknown-destroy"; + default: + return NULL; + } +} + +static void OnSyslogError(void* context, const struct SolidSyslogErrorEvent* event) +{ + (void) context; + + if (event == NULL) + { + return; + } + + /* Sources are matched by pointer identity — Name is only ever a label, which + * is all it is used for here. */ + const char* source = ((event->Source != NULL) && (event->Source->Name != NULL)) ? event->Source->Name : "?"; + const char* category = CategoryName(event->Category); + + if (category != NULL) + { + (void) printf("[syslog] %s %s %s (detail %ld)\n", SeverityName(event->Severity), source, category, (long) event->Detail); + } + else + { + (void) printf( + "[syslog] %s %s category 0x%04X (detail %ld)\n", + SeverityName(event->Severity), + source, + (unsigned int) event->Category, + (long) event->Detail + ); + } +} + +void SyslogErrorHandler_Install(void) +{ + SolidSyslog_SetErrorHandler(OnSyslogError, NULL); +} diff --git a/app/syslog/SyslogErrorHandler.h b/app/syslog/SyslogErrorHandler.h new file mode 100644 index 0000000..089865a --- /dev/null +++ b/app/syslog/SyslogErrorHandler.h @@ -0,0 +1,15 @@ +/* The device's reaction to a SolidSyslog internal fault. + * + * Nothing in the library fails loudly — a _Create that cannot succeed returns a + * Null object instead — so a logger that has silently stopped looks exactly like + * one with nothing to say. This is the only thing that tells them apart, which + * is why it goes in before the first _Create and not after something looks + * wrong. */ +#ifndef SYSLOG_ERROR_HANDLER_H +#define SYSLOG_ERROR_HANDLER_H + +/** Install the handler on the library's single global slot. Call once, at + * startup, before any SolidSyslog object is created. */ +void SyslogErrorHandler_Install(void); + +#endif /* SYSLOG_ERROR_HANDLER_H */ diff --git a/measurements/error-handler.csv b/measurements/error-handler.csv new file mode 100644 index 0000000..3f2c21b --- /dev/null +++ b/measurements/error-handler.csv @@ -0,0 +1,13 @@ +# error-handler figures (bytes) — captured by scripts/run.sh (CAPTURE=1). +# The device reads measurements/Baseline.csv as its frozen baseline and reports current-minus-Baseline. +flash_text,350392 +flash_data,320 +static_bss,110880 +heap_used,4440 +mbedtls_peak,21296 +mbedtls_free,11472 +lwip_mem_free,7576 +lwip_pbufs_free,13 +stack_log,120 +stack_service,52 +stack_harness,2840 diff --git a/measurements/stages.tsv b/measurements/stages.tsv index ed70112..7153a7c 100644 --- a/measurements/stages.tsv +++ b/measurements/stages.tsv @@ -11,3 +11,4 @@ # after it. Baseline Baseline a device that already networks, stores, and holds an mTLS session — before any syslog linked Linked the core library and lwIP raw-mode networking, linked but not yet called +error-handler Error handler a fault inside the logger reaches the console instead of being silent diff --git a/run-report.md b/run-report.md index 896280f..f867fc5 100644 --- a/run-report.md +++ b/run-report.md @@ -1,4 +1,4 @@ -# solid-syslog-example — run (linked) +# solid-syslog-example — run (error-handler) ## Device (self-measured) @@ -9,12 +9,12 @@ [device] sim app (lwIP up, FatFs mounted, broker session held over mTLS): ready [report] --- SolidSyslog cost above baseline (simulated existing application) --- [report] key,current,baseline,used_above_baseline -[report] flash_text,349992,349992,0 -[report] flash_data,316,316,0 -[report] static_bss,110876,110876,0 +[report] flash_text,350392,349992,400 +[report] flash_data,320,316,4 +[report] static_bss,110880,110876,4 [report] heap_used,4440,4440,0 -[report] mbedtls_peak,21232,21332,-100 -[report] mbedtls_free,11536,11436,100 +[report] mbedtls_peak,21336,21332,4 +[report] mbedtls_free,11432,11436,-4 [report] lwip_mem_free,7576,7576,0 [report] lwip_pbufs_free,13,14,-1 [report] stack_log,120,120,0 @@ -28,7 +28,7 @@ ```text text data bss dec hex filename - 349984 324 110876 461184 70980 /w/build/baseline-cross/baseline.elf + 350384 328 110880 461592 70b18 /w/build/baseline-cross/baseline.elf ``` ## Listeners (proved before the device ran) @@ -49,15 +49,15 @@ (nothing — this device sends no records yet) ``` -## Self-check (vs measurements/linked.csv) +## Self-check (vs measurements/error-handler.csv) ```text - OK flash_text: 349992 (expected 349992, Δ0) - OK flash_data: 316 (expected 316, Δ0) - OK static_bss: 110876 (expected 110876, Δ0) + OK flash_text: 350392 (expected 350392, Δ0) + OK flash_data: 320 (expected 320, Δ0) + OK static_bss: 110880 (expected 110880, Δ0) OK heap_used: 4440 (expected 4440, Δ0) - OK mbedtls_peak: 21232 (expected 21280, Δ48) - OK mbedtls_free: 11536 (expected 11488, Δ48) + OK mbedtls_peak: 21336 (expected 21296, Δ40) + OK mbedtls_free: 11432 (expected 11472, Δ40) OK lwip_mem_free: 7576 (expected 7576, Δ0) OK lwip_pbufs_free: 13 (expected 13, Δ0) OK stack_log: 120 (expected 120, Δ0)