| File: Diagnostics\CliLogFormat.cs | Web Access |
| Project: src\src\Aspire.Cli\Aspire.Cli.csproj (aspire) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Extensions.Logging; namespace Aspire.Cli.Diagnostics; internal static class CliLogFormat { internal readonly record struct FileLogEntry(string Level, string Category, string Message); internal static class Categories { internal const string Stdout = "Stdout"; internal const string Stderr = "Stderr"; internal const string AppHost = "AppHost"; internal const string AppHostPrefix = AppHost + "/"; internal const string DetachedAppHostPrefix = "DetachedAppHost/"; internal const string Build = "Build"; internal const string Dashboard = "Dashboard"; internal const string Package = "Package"; internal const string GuestAppHostProject = nameof(Projects.GuestAppHostProject); internal const string AspireCliTelemetry = nameof(Telemetry.AspireCliTelemetry); } internal static class FileLevelTokens { internal const string Trace = "TRCE"; internal const string Debug = "DBUG"; internal const string Information = "INFO"; internal const string Warning = "WARN"; internal const string Error = "FAIL"; internal const string Critical = "CRIT"; } internal static class ConsoleLevelTokens { internal const string Debug = "dbug"; internal const string Information = "info"; internal const string Warning = "warn"; internal const string Error = "fail"; internal const string Critical = "crit"; } internal static class MessagePrefixes { internal const string Executing = "Executing: "; } internal static string GetFileLevelToken(LogLevel logLevel) => logLevel switch { LogLevel.Trace => FileLevelTokens.Trace, LogLevel.Debug => FileLevelTokens.Debug, LogLevel.Information => FileLevelTokens.Information, LogLevel.Warning => FileLevelTokens.Warning, LogLevel.Error => FileLevelTokens.Error, LogLevel.Critical => FileLevelTokens.Critical, _ => logLevel.ToString().ToUpperInvariant() }; internal static string GetConsoleLevelToken(LogLevel logLevel) => logLevel switch { LogLevel.Debug => ConsoleLevelTokens.Debug, LogLevel.Information => ConsoleLevelTokens.Information, LogLevel.Warning => ConsoleLevelTokens.Warning, LogLevel.Error => ConsoleLevelTokens.Error, LogLevel.Critical => ConsoleLevelTokens.Critical, _ => logLevel.ToString().ToLowerInvariant() }; internal static bool TryGetLogLevelFromFileToken(string token, out LogLevel logLevel) { logLevel = token switch { FileLevelTokens.Trace => LogLevel.Trace, FileLevelTokens.Debug => LogLevel.Debug, FileLevelTokens.Information => LogLevel.Information, FileLevelTokens.Warning => LogLevel.Warning, FileLevelTokens.Error => LogLevel.Error, FileLevelTokens.Critical => LogLevel.Critical, _ => LogLevel.None }; return logLevel is not LogLevel.None; } internal static string GetShortCategoryName(string categoryName) { var lastDotIndex = categoryName.LastIndexOf('.'); return lastDotIndex >= 0 ? categoryName.Substring(lastDotIndex + 1) : categoryName; } internal static string GetDetachedAppHostCategory(string categoryName) => Categories.DetachedAppHostPrefix + categoryName; internal static bool TryParseFileLogLine(string line, out FileLogEntry entry) { entry = default; if (!line.StartsWith('[')) { return false; } // Parse file log lines written by FileLoggerProvider as: // [2026-05-15 17:07:30.501] [INFO] [AppHost] apphost.ts(5,22): error TS1109: Expression expected. // The message can contain additional brackets or delimiters, so only consume // the fixed timestamp, level, and category segments from the front of the line. var timestampEnd = line.IndexOf("] [", StringComparison.Ordinal); if (timestampEnd < 0) { return false; } var levelStart = timestampEnd + 3; var levelEnd = line.IndexOf(']', levelStart); if (levelEnd < 0 || levelEnd + 3 > line.Length) { return false; } var categoryStart = levelEnd + 3; var categoryEnd = line.IndexOf(']', categoryStart); if (categoryEnd < 0 || categoryEnd + 2 > line.Length) { return false; } entry = new FileLogEntry( line[levelStart..levelEnd], line[categoryStart..categoryEnd], line[(categoryEnd + 2)..]); return true; } }