diff --git a/app/MindWork AI Studio/Tools/Rust/LogEventRequest.cs b/app/MindWork AI Studio/Tools/Rust/LogEventRequest.cs
index 18e95aed..c3f8fe89 100644
--- a/app/MindWork AI Studio/Tools/Rust/LogEventRequest.cs
+++ b/app/MindWork AI Studio/Tools/Rust/LogEventRequest.cs
@@ -5,5 +5,6 @@ public readonly record struct LogEventRequest(
string Level,
string Category,
string Message,
- string? Exception
+ string? Exception,
+ string? StackTrace
);
diff --git a/app/MindWork AI Studio/Tools/Services/RustService.Log.cs b/app/MindWork AI Studio/Tools/Services/RustService.Log.cs
index 595a9708..b8542ddd 100644
--- a/app/MindWork AI Studio/Tools/Services/RustService.Log.cs
+++ b/app/MindWork AI Studio/Tools/Services/RustService.Log.cs
@@ -20,13 +20,14 @@ public sealed partial class RustService
/// The log level.
/// The category of the log event.
/// The log message.
- /// Optional exception details.
- public void LogEvent(string timestamp, string level, string category, string message, string? exception = null)
+ /// Optional exception message.
+ /// Optional exception stack trace.
+ public void LogEvent(string timestamp, string level, string category, string message, string? exception = null, string? stackTrace = null)
{
try
{
// 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);
}
catch
diff --git a/app/MindWork AI Studio/Tools/TerminalLogger.cs b/app/MindWork AI Studio/Tools/TerminalLogger.cs
index 65075d4a..ed05222a 100644
--- a/app/MindWork AI Studio/Tools/TerminalLogger.cs
+++ b/app/MindWork AI Studio/Tools/TerminalLogger.cs
@@ -33,17 +33,26 @@ public sealed class TerminalLogger() : ConsoleFormatter(FORMATTER_NAME)
var timestamp = DateTimeOffset.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff");
var logLevel = logEntry.LogLevel.ToString();
var category = logEntry.Category;
- var exception = logEntry.Exception?.ToString();
+ var exceptionMessage = logEntry.Exception?.Message;
+ var stackTrace = logEntry.Exception?.StackTrace;
var colorCode = GetColorForLogLevel(logEntry.LogLevel);
- textWriter.Write($"{colorCode}[{timestamp}] {logLevel} [{category}]{ANSI_RESET} {message}");
- if (exception is not null)
- textWriter.Write($" Exception: {exception}");
-
- textWriter.WriteLine();
+ textWriter.Write($"[{colorCode}{timestamp}{ANSI_RESET}] {colorCode}{logLevel}{ANSI_RESET} [{category}] {colorCode}{message}{ANSI_RESET}");
+ if (logEntry.Exception is not null)
+ {
+ 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();
// 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
diff --git a/runtime/src/log.rs b/runtime/src/log.rs
index d9d403f3..46bdbad9 100644
--- a/runtime/src/log.rs
+++ b/runtime/src/log.rs
@@ -240,16 +240,58 @@ pub fn log_event(_token: APIToken, event: Json) -> Json debug!(Source = ".NET Server", Comp = category; "{message}"),
- "Information" => info!(Source = ".NET Server", Comp = category; "{message}"),
- "Warning" => warn!(Source = ".NET Server", Comp = category; "{message}"),
- "Error" | "Critical" => {
+ "Trace" | "Debug" => {
+ debug!(Source = ".NET Server", Comp = category; "{message}");
if let Some(ref ex) = event.exception {
- error!(Source = ".NET Server", Comp = category; "{message} Exception: {ex}")
- } else {
- error!(Source = ".NET Server", Comp = category; "{message}")
+ debug!(Source = ".NET Server", Comp = category; " Exception: {ex}");
+ }
+
+ 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),
}
@@ -272,6 +314,7 @@ pub struct LogEvent {
category: String,
message: String,
exception: Option,
+ stack_trace: Option,
}
/// The response to a log event request.