app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit 4ee4535921f8c39bfbd81836aced5bcb7cfb9ebb
parent 8fc0dfd820423fbb8cfadade9872d4d91d5dafc3
Author: triesap <triesap@radroots.dev>
Date:   Wed, 21 Jan 2026 14:11:01 +0000

app: add log dump header metadata

- define log dump metadata payload

- generate header with app metadata and timestamp

- expose header helper for dump usage

- add unit test for header serialization

Diffstat:
Mapp/src/lib.rs | 2++
Mapp/src/logging.rs | 34++++++++++++++++++++++++++++++++++
2 files changed, 36 insertions(+), 0 deletions(-)

diff --git a/app/src/lib.rs b/app/src/lib.rs @@ -69,11 +69,13 @@ pub use logging::{ app_log_entry_key, app_log_entry_prefix, app_log_debug_emit, + app_log_dump_header, app_log_info_emit, app_log_metadata, app_log_timestamp_ms, app_log_warn_emit, app_logging_init, + RadrootsAppLogDumpMeta, RadrootsAppLogEntry, RadrootsAppLogError, RadrootsAppLogLevel, diff --git a/app/src/logging.rs b/app/src/logging.rs @@ -60,6 +60,23 @@ impl Default for RadrootsAppLogMetadata { static LOG_META: OnceLock<RadrootsAppLogMetadata> = OnceLock::new(); +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct RadrootsAppLogDumpMeta { + pub kind: String, + pub generated_at_ms: i64, + pub metadata: RadrootsAppLogMetadata, +} + +impl RadrootsAppLogDumpMeta { + pub fn new() -> Self { + Self { + kind: String::from("radroots_log_dump"), + generated_at_ms: app_log_timestamp_ms(), + metadata: app_log_metadata().clone(), + } + } +} + #[cfg(not(test))] static LOG_BUFFER: OnceLock<Mutex<Vec<RadrootsAppLogEntry>>> = OnceLock::new(); @@ -534,6 +551,12 @@ pub fn app_log_metadata() -> &'static RadrootsAppLogMetadata { LOG_META.get_or_init(RadrootsAppLogMetadata::default) } +pub fn app_log_dump_header() -> String { + let header = RadrootsAppLogDumpMeta::new(); + serde_json::to_string(&header) + .unwrap_or_else(|_| String::from("{\"error\":\"log_dump_header_failed\"}")) +} + pub fn app_logging_init(meta: Option<RadrootsAppLogMetadata>) -> RadrootsAppLoggingResult<()> { if LOG_META.get().is_none() { let _ = LOG_META.set(meta.unwrap_or_default()); @@ -579,10 +602,12 @@ mod tests { app_log_buffer_flush_no_prune, app_log_buffer_flush, app_log_buffer_push, + app_log_dump_header, app_log_metadata, app_log_timestamp_ms, RadrootsAppLogLevel, RadrootsAppLogEntry, + RadrootsAppLogDumpMeta, RadrootsAppLogMetadata, }; use crate::{ @@ -782,6 +807,15 @@ mod tests { } #[test] + fn log_dump_header_serializes() { + let header = app_log_dump_header(); + let parsed: RadrootsAppLogDumpMeta = + serde_json::from_str(&header).expect("header"); + assert_eq!(parsed.kind, "radroots_log_dump"); + assert!(!parsed.metadata.app_name.is_empty()); + } + + #[test] fn log_entry_error_includes_context() { let err = RadrootsAppConfigError::MissingKeyMap("nostr_key"); let entry = app_log_entry_error(&err);