v0.34.0_2

This commit is contained in:
Mateusz Gruszczyński committed 2026-10-17 23:37:43 +02:00
1 parent 0a8bc6f040
commit 434e789144
38 files changed
+899 -238

No files matched your search

+21 -7
View File
@@ -136,8 +136,17 @@ impl Logs {
self.settings.load().capture_country
}
pub fn push(&self, entry: LogEntry) {
// String cloning and eviction cleanup do not need the history mutex.
let history_entry = entry.clone();
let has_viewers = self.live.receiver_count() != 0;
let has_sink = self.settings.load().enabled();
// The usual history-only configuration needs no deep copy at all.
// When external consumers are present, clone exactly for history and
// keep the original entry available for streaming/sink delivery.
let mut external = Some(entry);
let history_entry = if has_viewers || has_sink {
external.as_ref().expect("entry").clone()
} else {
external.take().expect("entry")
};
let evicted = if let Ok(mut entries) = self.entries.try_lock() {
let evicted = if entries.len() == HISTORY_CAPACITY {
entries.pop_front()
@@ -150,13 +159,18 @@ impl Logs {
self.dropped.fetch_add(1, Ordering::Relaxed);
None
};
// Destroy evicted strings outside of the history lock.
drop(evicted);
// No live viewers means no reason to allocate/clone a second LogEntry.
// History retention and external sinks remain unchanged.
if self.live.receiver_count() != 0 {
let _ = self.live.send(entry.clone());
if let Some(entry) = external {
if has_viewers {
let _ = self.live.send(entry.clone());
}
// Snapshot the sink choice once. This preserves sink drop
// accounting and avoids a second ArcSwap load per request.
if has_sink && self.sink.try_send(entry).is_err() {
self.sink_dropped.fetch_add(1, Ordering::Relaxed);
}
}
self.push_sink(entry);
}
fn push_sink(&self, entry: LogEntry) {
if self.settings.load().enabled() && self.sink.try_send(entry).is_err() {
+36
View File
@@ -422,4 +422,40 @@ mod tests {
assert_eq!(stats.sent, 1);
assert_eq!(stats.errors, 0);
}
#[test]
fn history_only_mode_preserves_exact_fields_without_any_live_or_sink_copies() {
let logs = Logs::default();
let item = entry();
let expected = serde_json::to_value(&item).unwrap();
logs.push(item);
let saved = logs.recent();
assert_eq!(saved.len(), 1);
assert_eq!(serde_json::to_value(&saved[0]).unwrap(), expected);
assert_eq!(logs.stats().queue_dropped, 0);
}
#[test]
fn live_viewers_receive_same_event_as_persisted_history() {
let logs = Logs::default();
let mut live = logs.subscribe();
let item = entry();
let expected = serde_json::to_value(&item).unwrap();
logs.push(item);
assert_eq!(serde_json::to_value(live.try_recv().unwrap()).unwrap(), expected);
assert_eq!(serde_json::to_value(&logs.recent()[0]).unwrap(), expected);
}
#[test]
fn enabled_external_sink_retains_existing_queue_drop_accounting() {
// The default test Logs has a closed sink receiver. Delivering to a
// configured external sink must still account for its failure.
let logs = Logs::default();
let mut settings = Settings::default();
settings.file.enabled = true;
logs.configure(settings);
logs.push(entry());
assert_eq!(logs.recent().len(), 1);
assert_eq!(logs.stats().queue_dropped, 1);
}
}