commit 10eb301637ea6dc276476139defb7304dad9a660
parent 6d6820114684944ad0fa336ec15cbec534aa2560
Author: triesap <triesap@radroots.dev>
Date: Tue, 20 Jan 2026 16:51:40 +0000
app: limit init log persistence to errors
- add filtered buffer flush helper for warn and error
- use critical flush for init and health runs
- keep full flush for logs page refresh
- add unit tests for filtered flush behavior
Diffstat:
4 files changed, 71 insertions(+), 4 deletions(-)
diff --git a/app/src/app.rs b/app/src/app.rs
@@ -15,7 +15,7 @@ use crate::{
app_init_stage_set,
app_init_total_add,
app_init_total_unknown,
- app_log_buffer_flush,
+ app_log_buffer_flush_critical,
app_log_debug_emit,
app_log_error_emit,
app_log_error_store,
@@ -162,7 +162,11 @@ fn HomePage() -> impl IntoView {
}
match app_init_backends(config).await {
Ok(value) => {
- let _ = app_log_buffer_flush(&value.datastore, &value.config.datastore.key_maps).await;
+ let _ = app_log_buffer_flush_critical(
+ &value.datastore,
+ &value.config.datastore.key_maps,
+ )
+ .await;
backends.set(Some(value));
app_init_mark_completed();
let stage = AppInitStage::Ready;
diff --git a/app/src/health.rs b/app/src/health.rs
@@ -84,7 +84,7 @@ use crate::{
app_datastore_has_config,
app_datastore_key_nostr_key,
app_datastore_read_app_data,
- app_log_buffer_flush,
+ app_log_buffer_flush_critical,
app_log_debug_emit,
app_log_entry_new,
app_log_entry_record,
@@ -300,7 +300,7 @@ pub async fn app_health_check_all_logged<T: RadrootsClientDatastore, K: Radroots
key_maps: &AppKeyMapConfig,
) -> AppHealthReport {
let report = app_health_check_all(datastore, keystore, notifications, tangle, key_maps).await;
- let _ = app_log_buffer_flush(datastore, key_maps).await;
+ let _ = app_log_buffer_flush_critical(datastore, key_maps).await;
report
}
diff --git a/app/src/lib.rs b/app/src/lib.rs
@@ -55,6 +55,7 @@ pub use logging::{
app_log_entry_record,
app_log_entry_store,
app_log_buffer_drain,
+ app_log_buffer_flush_critical,
app_log_buffer_flush,
app_log_buffer_push,
app_log_entries_dump,
diff --git a/app/src/logging.rs b/app/src/logging.rs
@@ -363,6 +363,10 @@ pub fn app_log_buffer_drain() -> Vec<AppLogEntry> {
}
}
+fn app_log_entry_should_persist(level: AppLogLevel) -> bool {
+ matches!(level, AppLogLevel::Warn | AppLogLevel::Error)
+}
+
pub async fn app_log_buffer_flush<T: RadrootsClientDatastore>(
datastore: &T,
key_maps: &AppKeyMapConfig,
@@ -384,6 +388,42 @@ pub async fn app_log_buffer_flush<T: RadrootsClientDatastore>(
Ok(stored)
}
+pub async fn app_log_buffer_flush_critical<T: RadrootsClientDatastore>(
+ datastore: &T,
+ key_maps: &AppKeyMapConfig,
+) -> AppLogResult<usize> {
+ let entries = app_log_buffer_drain();
+ let mut keep = Vec::new();
+ let mut persist = Vec::new();
+ for entry in entries {
+ if app_log_entry_should_persist(entry.level) {
+ persist.push(entry);
+ } else {
+ keep.push(entry);
+ }
+ }
+ let mut stored = 0;
+ let mut iter = persist.into_iter();
+ while let Some(entry) = iter.next() {
+ if let Err(err) = app_log_entry_store(datastore, key_maps, &entry).await {
+ app_log_buffer_push(entry);
+ for remaining in iter {
+ app_log_buffer_push(remaining);
+ }
+ for remaining in keep {
+ app_log_buffer_push(remaining);
+ }
+ return Err(err);
+ }
+ stored += 1;
+ }
+ for entry in keep {
+ app_log_buffer_push(entry);
+ }
+ let _ = app_log_entries_prune(datastore, key_maps, APP_LOG_MAX_ENTRIES).await?;
+ Ok(stored)
+}
+
pub fn app_log_entry_prefix(key_maps: &AppKeyMapConfig) -> AppLogResult<String> {
let param = app_datastore_param_key(key_maps, "log_entry")?;
Ok(param(""))
@@ -518,6 +558,7 @@ mod tests {
app_log_entry_key,
app_log_entry_prefix,
app_log_buffer_drain,
+ app_log_buffer_flush_critical,
app_log_buffer_flush,
app_log_buffer_push,
app_log_metadata,
@@ -754,6 +795,27 @@ mod tests {
}
#[test]
+ fn log_buffer_flush_critical_keeps_debug_entries() {
+ let _guard = LOG_TEST_LOCK.lock().unwrap_or_else(|err| err.into_inner());
+ let _ = app_log_buffer_drain();
+ let debug = app_log_entry_new(AppLogLevel::Debug, "log.code.debug", "debug", None);
+ let error = app_log_entry_new(AppLogLevel::Error, "log.code.error", "error", None);
+ app_log_buffer_push(debug.clone());
+ app_log_buffer_push(error.clone());
+ let datastore = TestDatastore::new(Vec::new());
+ let key_maps = app_key_maps_default();
+ let stored = futures::executor::block_on(app_log_buffer_flush_critical(
+ &datastore,
+ &key_maps,
+ ))
+ .expect("flush");
+ assert_eq!(stored, 1);
+ let remaining = app_log_buffer_drain();
+ assert_eq!(remaining.len(), 1);
+ assert_eq!(remaining[0].id, debug.id);
+ }
+
+ #[test]
fn log_entry_prefix_uses_log_key() {
let key_maps = app_key_maps_default();
let prefix = app_log_entry_prefix(&key_maps).expect("prefix");