This commit is contained in:
Mateusz Gruszczyński committed 2026-10-09 17:09:41 +02:00
1 parent 3e33982cd3
commit ad95bfb050
27 files changed
+309 -80

No files matched your search

+33 -5
View File
@@ -161,7 +161,7 @@ fn format_error_event(event: &ErrorEvent, format: ErrorLogFormat) -> Vec<u8> {
ErrorLogFormat::Json => {
let mut object = Map::new();
object.insert("timestamp".into(), Value::String(event.timestamp.clone()));
object.insert("level".into(), Value::String("ERROR".into()));
object.insert("level".into(), Value::String(event.level.clone()));
object.insert("target".into(), Value::String(event.target.clone()));
object.insert("message".into(), Value::String(event.message.clone()));
for (key, value) in &event.fields {
@@ -176,8 +176,9 @@ fn format_error_event(event: &ErrorEvent, format: ErrorLogFormat) -> Vec<u8> {
ErrorLogFormat::Raw => {
let one_line = |value: &str| value.replace('\r', "\\r").replace('\n', "\\n");
let mut line = format!(
"{} ERROR {} {}",
"{} {} {} {}",
event.timestamp,
event.level,
event.target,
one_line(&event.message)
);
@@ -193,6 +194,21 @@ fn format_error_event(event: &ErrorEvent, format: ErrorLogFormat) -> Vec<u8> {
}
}
fn write_error_fallback(event: &ErrorEvent, format: ErrorLogFormat) {
// File unavailable or the bounded queue is full: preserve the redacted
// diagnostic on stderr rather than dropping it into a black hole.
let line = format_error_event(event, format);
let mut stderr = io::stderr().lock();
let _ = stderr.write_all(&line);
}
fn warn_error_sink_failure(path: &std::path::Path, error: &dyn std::fmt::Display, action: &str) {
let path = redact_text(&path.display().to_string());
let error = redact_text(&error.to_string());
let mut stderr = io::stderr().lock();
let _ = writeln!(stderr, "RustProxy error log {action} ({path}): {error}; using stderr fallback");
}
async fn error_log_loop(
mut receiver: mpsc::Receiver<ErrorEvent>,
settings: Arc<ArcSwap<ErrorLogSettings>>,
@@ -232,6 +248,9 @@ async fn error_log_loop(
file = None;
open_path = None;
retry_after = None;
// A queued event may outlive a live settings change. It was
// suppressed on the console when queued; do not lose it now.
write_error_fallback(&event, config.format);
continue;
}
let path = PathBuf::from(&config.path);
@@ -250,24 +269,33 @@ async fn error_log_loop(
errors.fetch_add(1, Ordering::Relaxed);
retry_after = Some(Instant::now() + ERROR_LOG_RETRY);
if last_warning.is_none_or(|time| time.elapsed() >= ERROR_LOG_RETRY) {
tracing::warn!(path=%path.display(), %error, "optional error log file is unavailable");
warn_error_sink_failure(&path, &error, "open failed");
last_warning = Some(Instant::now());
}
}
}
}
let Some(opened) = file.as_mut() else {
write_error_fallback(&event, config.format);
continue;
};
let line = format_error_event(&event, config.format);
if let Err(error) = opened.write_all(&line).await {
// Tokio's File buffers writes; flush so diagnostics are visible to
// log readers promptly and a later shutdown cannot strand a record.
let write = async {
opened.write_all(&line).await?;
opened.flush().await
}
.await;
if let Err(error) = write {
errors.fetch_add(1, Ordering::Relaxed);
file = None;
retry_after = Some(Instant::now() + ERROR_LOG_RETRY);
if last_warning.is_none_or(|time| time.elapsed() >= ERROR_LOG_RETRY) {
tracing::warn!(path=%path.display(), %error, "optional error log write failed");
warn_error_sink_failure(&path, &error, "write failed");
last_warning = Some(Instant::now());
}
write_error_fallback(&event, config.format);
} else {
written.fetch_add(1, Ordering::Relaxed);
}