Added stacktrace handling

This commit is contained in:
Thorsten Sommer 2026-01-13 13:08:19 +01:00
parent 2210118596
commit 08f562db12
Signed by: tsommer
GPG Key ID: 371BBA77A02C0108
4 changed files with 72 additions and 18 deletions

View File

@ -5,5 +5,6 @@ public readonly record struct LogEventRequest(
string Level, string Level,
string Category, string Category,
string Message, string Message,
string? Exception string? Exception,
string? StackTrace
); );

View File

@ -20,13 +20,14 @@ public sealed partial class RustService
/// <param name="level">The log level.</param> /// <param name="level">The log level.</param>
/// <param name="category">The category of the log event.</param> /// <param name="category">The category of the log event.</param>
/// <param name="message">The log message.</param> /// <param name="message">The log message.</param>
/// <param name="exception">Optional exception details.</param> /// <param name="exception">Optional exception message.</param>
public void LogEvent(string timestamp, string level, string category, string message, string? exception = null) /// <param name="stackTrace">Optional exception stack trace.</param>
public void LogEvent(string timestamp, string level, string category, string message, string? exception = null, string? stackTrace = null)
{ {
try try
{ {
// Fire-and-forget the log event to avoid blocking: // Fire-and-forget the log event to avoid blocking:
var request = new LogEventRequest(timestamp, level, category, message, exception); var request = new LogEventRequest(timestamp, level, category, message, exception, stackTrace);
_ = this.http.PostAsJsonAsync("/log/event", request, this.jsonRustSerializerOptions); _ = this.http.PostAsJsonAsync("/log/event", request, this.jsonRustSerializerOptions);
} }
catch catch

View File

@ -33,17 +33,26 @@ public sealed class TerminalLogger() : ConsoleFormatter(FORMATTER_NAME)
var timestamp = DateTimeOffset.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff"); var timestamp = DateTimeOffset.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff");
var logLevel = logEntry.LogLevel.ToString(); var logLevel = logEntry.LogLevel.ToString();
var category = logEntry.Category; var category = logEntry.Category;
var exception = logEntry.Exception?.ToString(); var exceptionMessage = logEntry.Exception?.Message;
var stackTrace = logEntry.Exception?.StackTrace;
var colorCode = GetColorForLogLevel(logEntry.LogLevel); var colorCode = GetColorForLogLevel(logEntry.LogLevel);
textWriter.Write($"{colorCode}[{timestamp}] {logLevel} [{category}]{ANSI_RESET} {message}"); textWriter.Write($"[{colorCode}{timestamp}{ANSI_RESET}] {colorCode}{logLevel}{ANSI_RESET} [{category}] {colorCode}{message}{ANSI_RESET}");
if (exception is not null) if (logEntry.Exception is not null)
textWriter.Write($" Exception: {exception}"); {
textWriter.Write($" {colorCode}Exception: {exceptionMessage}{ANSI_RESET}");
if (stackTrace is not null)
{
textWriter.WriteLine();
foreach (var line in stackTrace.Split('\n'))
textWriter.WriteLine($" {line.TrimEnd()}");
}
}
else
textWriter.WriteLine(); textWriter.WriteLine();
// Send log event to Rust via API (fire-and-forget): // Send log event to Rust via API (fire-and-forget):
RUST_SERVICE?.LogEvent(timestamp, logLevel, category, message, exception); RUST_SERVICE?.LogEvent(timestamp, logLevel, category, message, exceptionMessage, stackTrace);
} }
private static string GetColorForLogLevel(LogLevel logLevel) => logLevel switch private static string GetColorForLogLevel(LogLevel logLevel) => logLevel switch

View File

@ -240,16 +240,58 @@ pub fn log_event(_token: APIToken, event: Json<LogEvent>) -> Json<LogEventRespon
// Log with the appropriate level // Log with the appropriate level
match event.level.as_str() { match event.level.as_str() {
"Trace" | "Debug" => debug!(Source = ".NET Server", Comp = category; "{message}"), "Trace" | "Debug" => {
"Information" => info!(Source = ".NET Server", Comp = category; "{message}"), debug!(Source = ".NET Server", Comp = category; "{message}");
"Warning" => warn!(Source = ".NET Server", Comp = category; "{message}"),
"Error" | "Critical" => {
if let Some(ref ex) = event.exception { if let Some(ref ex) = event.exception {
error!(Source = ".NET Server", Comp = category; "{message} Exception: {ex}") debug!(Source = ".NET Server", Comp = category; " Exception: {ex}");
} else { }
error!(Source = ".NET Server", Comp = category; "{message}")
if let Some(ref stack_trace) = event.stack_trace {
for line in stack_trace.lines() {
debug!(Source = ".NET Server", Comp = category; " {line}");
}
} }
}, },
"Information" => {
info!(Source = ".NET Server", Comp = category; "{message}");
if let Some(ref ex) = event.exception {
info!(Source = ".NET Server", Comp = category; " Exception: {ex}");
}
if let Some(ref stack_trace) = event.stack_trace {
for line in stack_trace.lines() {
info!(Source = ".NET Server", Comp = category; " {line}");
}
}
},
"Warning" => {
warn!(Source = ".NET Server", Comp = category; "{message}");
if let Some(ref ex) = event.exception {
warn!(Source = ".NET Server", Comp = category; " Exception: {ex}");
}
if let Some(ref stack_trace) = event.stack_trace {
for line in stack_trace.lines() {
warn!(Source = ".NET Server", Comp = category; " {line}");
}
}
},
"Error" | "Critical" => {
error!(Source = ".NET Server", Comp = category; "{message}");
if let Some(ref ex) = event.exception {
error!(Source = ".NET Server", Comp = category; " Exception: {ex}");
}
if let Some(ref stack_trace) = event.stack_trace {
for line in stack_trace.lines() {
error!(Source = ".NET Server", Comp = category; " {line}");
}
}
},
_ => error!(Source = ".NET Server", Comp = category; "{message} (unknown log level '{}')", event.level), _ => error!(Source = ".NET Server", Comp = category; "{message} (unknown log level '{}')", event.level),
} }
@ -272,6 +314,7 @@ pub struct LogEvent {
category: String, category: String,
message: String, message: String,
exception: Option<String>, exception: Option<String>,
stack_trace: Option<String>,
} }
/// The response to a log event request. /// The response to a log event request.