Fügt farbige Terminalausgabe je nach Log-Level hinzu

Die komplette Ausgabezeile wird nun abhängig vom Schweregrad eingefärbt
(Error rot, Warn gelb, Info grün, Debug cyan), aber nur wenn stdout/stderr
ein echtes Terminal ist und weder NO_COLOR noch TERM=dumb gesetzt sind.
Die Logdatei bleibt unverändert klartextbasiert ohne ANSI-Codes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-20 23:49:20 +02:00
co-authored by Claude Sonnet 5
parent 91189e0cfc
commit 5d15c15cb0
3 changed files with 37 additions and 2 deletions
+1
View File
@@ -4,6 +4,7 @@
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/examples" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
+1
View File
@@ -10,6 +10,7 @@
- `Warn` (2) — Warnungen (Ausgabe auf `stdout` mit `[?]`-Präfix)
- `Info` (3) — Betriebsinformationen (Ausgabe auf `stdout` mit `[i]`-Präfix)
- `Debug` (4) — Diagnoseausgaben (Ausgabe auf `stdout` mit `[d]`-Präfix)
- **Farbige Terminalausgabe**: Jede Zeile wird je nach Schweregrad eingefärbt (`Error` rot, `Warn` gelb, `Info` grün, `Debug` cyan). Farben werden nur ausgegeben, wenn `stdout`/`stderr` ein echtes Terminal ist (keine Umleitung in Datei/Pipe), `NO_COLOR` nicht gesetzt ist und `TERM` nicht `dumb` ist. Die Logdatei enthält stets reinen Text ohne ANSI-Codes.
- **Threadsicherheit**: Dateischreibzugriffe und Konfiguration sind thread-sicher serialisiert.
- **Einheitliches Format**: `[Präfix][DD.MM.YYYY HH:MM:SS.mmm][LEVEL][Tag]: Nachricht`
- **Flexible Konfiguration**:
+34 -1
View File
@@ -24,7 +24,7 @@
use std::env;
use std::fmt::{Display, Formatter};
use std::fs::{File, OpenOptions, create_dir_all};
use std::io::Write;
use std::io::{IsTerminal, Write};
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::atomic::{AtomicU8, Ordering};
@@ -156,6 +156,33 @@ pub fn set_log_dir(path: impl Into<PathBuf>) {
}
}
/// ANSI-Escape-Sequenz zum Zurücksetzen der Textfarbe.
const ANSI_RESET: &str = "\x1b[0m";
/// Liefert die ANSI-Farbsequenz für den jeweiligen Schweregrad.
///
/// Farben: `Error` -> Rot, `Warn` -> Gelb, `Info` -> Grün, `Debug` -> Cyan.
fn ansi_color(log_level: &LogLevel) -> &'static str {
match log_level {
LogLevel::Error => "\x1b[31m",
LogLevel::Warn => "\x1b[33m",
LogLevel::Info => "\x1b[32m",
LogLevel::Debug => "\x1b[36m",
}
}
/// Prüft, ob Farbausgabe für den gegebenen Stream sinnvoll ist.
///
/// Voraussetzungen:
/// - Der Stream muss ein echtes Terminal sein (`is_terminal`), keine Umleitung in eine Datei oder Pipe.
/// - Die Umgebungsvariable `NO_COLOR` darf nicht gesetzt sein (siehe <https://no-color.org/>).
/// - `TERM` darf nicht auf `dumb` stehen.
fn color_supported(stream_is_terminal: bool) -> bool {
stream_is_terminal
&& env::var_os("NO_COLOR").is_none()
&& env::var("TERM").map(|term| term != "dumb").unwrap_or(true)
}
/// Formatiert eine Lognachricht mit Zeitstempel, Level, Tag und Präfix.
///
/// Format:
@@ -224,7 +251,13 @@ pub fn log(tag: &str, message: &str, log_level: LogLevel) {
let formatted = format_message(tag, message, &log_level);
if log_level == LogLevel::Error {
if color_supported(std::io::stderr().is_terminal()) {
eprintln!("{}{}{}", ansi_color(&log_level), formatted, ANSI_RESET);
} else {
eprintln!("{}", formatted);
}
} else if color_supported(std::io::stdout().is_terminal()) {
println!("{}{}{}", ansi_color(&log_level), formatted, ANSI_RESET);
} else {
println!("{}", formatted);
}