Skip to content

Logging ordering fixes and feedback #5398

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions playground/Stress/Stress.ApiService/ConsoleStresser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ public static void Stress()
for (var color = 40; color <= 47; color++)
{
Console.Write("\x1b[" + color + "m"); // Set background color
Console.WriteLine($"This is background color {color}");
Console.Write("\x1b[0m"); // Reset colors to default after each background to maintain readability
Console.Write($"This is background color {color}");
Console.WriteLine("\x1b[0m"); // Reset colors to default after each background to maintain readability
}
Console.Write("\x1b[0m"); // Reset all colors to default at the end

Expand Down
17 changes: 17 additions & 0 deletions playground/Stress/Stress.ApiService/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,23 @@

app.MapGet("/", () => "Hello world");

app.MapGet("/write-console", () =>
{
for (var i = 0; i < 5000; i++)
{
if (i % 500 == 0)
{
Console.Error.WriteLine($"{i} Error");
}
else
{
Console.Out.WriteLine($"{i} Out");
}
}

return "Console written";
});

app.MapGet("/increment-counter", (TestMetrics metrics) =>
{
metrics.IncrementCounter(1, new TagList([new KeyValuePair<string, object?>("add-tag", "1")]));
Expand Down
3 changes: 3 additions & 0 deletions src/Aspire.Dashboard/Aspire.Dashboard.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -229,5 +229,8 @@
<Compile Include="$(SharedDir)TokenGenerator.cs" Link="Utils\TokenGenerator.cs" />
<Compile Include="$(SharedDir)LoggingHelpers.cs" Link="Utils\LoggingHelpers.cs" />
<Compile Include="$(SharedDir)StringUtils.cs" Link="Utils\StringUtils.cs" />
<Compile Include="$(SharedDir)ConsoleLogs\LogEntries.cs" Link="Utils\ConsoleLogs\LogEntries.cs" />
<Compile Include="$(SharedDir)ConsoleLogs\LogEntry.cs" Link="Utils\ConsoleLogs\LogEntry.cs" />
<Compile Include="$(SharedDir)ConsoleLogs\TimestampParser.cs" Link="Utils\ConsoleLogs\TimestampParser.cs" />
</ItemGroup>
</Project>
1 change: 1 addition & 0 deletions src/Aspire.Dashboard/Components/Controls/LogViewer.razor
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@namespace Aspire.Dashboard.Components
@using Aspire.Dashboard.Model
@using Aspire.Hosting.ConsoleLogs
@inject IJSRuntime JS
@implements IAsyncDisposable

Expand Down
12 changes: 10 additions & 2 deletions src/Aspire.Dashboard/Components/Controls/LogViewer.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Aspire.Dashboard.Extensions;
using Aspire.Dashboard.Model;
using Aspire.Dashboard.Utils;
using Aspire.Hosting.ConsoleLogs;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Options;
using Microsoft.JSInterop;
Expand All @@ -32,7 +33,7 @@ public sealed partial class LogViewer
[Inject]
public required IOptions<DashboardOptions> Options { get; set; }

public LogEntries LogEntries { get; set; } = null!;
internal LogEntries LogEntries { get; set; } = null!;

public string? ResourceName { get; set; }

Expand Down Expand Up @@ -85,7 +86,14 @@ internal async Task SetLogSourceAsync(string resourceName, IAsyncEnumerable<IRea

foreach (var (lineNumber, content, isErrorOutput) in batch)
{
LogEntries.InsertSorted(logParser.CreateLogEntry(content, isErrorOutput), lineNumber);
// Set the base line number using the reported line number of the first log line.
if (LogEntries.EntriesCount == 0)
{
LogEntries.BaseLineNumber = lineNumber;
}

var logEntry = logParser.CreateLogEntry(content, isErrorOutput);
LogEntries.InsertSorted(logEntry);
}

StateHasChanged();
Expand Down
91 changes: 0 additions & 91 deletions src/Aspire.Dashboard/ConsoleLogs/LogEntries.cs

This file was deleted.

26 changes: 0 additions & 26 deletions src/Aspire.Dashboard/ConsoleLogs/LogEntry.cs

This file was deleted.

57 changes: 12 additions & 45 deletions src/Aspire.Dashboard/ConsoleLogs/LogParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,90 +2,57 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Net;
using Aspire.Dashboard.Model;
using Aspire.Hosting.ConsoleLogs;

namespace Aspire.Dashboard.ConsoleLogs;

internal sealed class LogParser
{
private DateTimeOffset? _parentTimestamp;
private Guid? _parentId;
private int _lineIndex;
private AnsiParser.ParserState? _residualState;

public LogEntry CreateLogEntry(string rawText, bool isErrorOutput)
{
// Several steps to do here:
//
// 1. Parse the content to look for the timestamp
// 2. Parse the content to look for info/warn/dbug header
// 3. HTML Encode the raw text for security purposes
// 4. Parse the content to look for ANSI Control Sequences and color them if possible
// 5. Parse the content to look for URLs and make them links if possible
// 6. Create the LogEntry to get the ID
// 7. Set the relative properties of the log entry (parent/line index/etc)
// 8. Return the final result
// 2. HTML Encode the raw text for security purposes
// 3. Parse the content to look for ANSI Control Sequences and color them if possible
// 4. Parse the content to look for URLs and make them links if possible
// 5. Create the LogEntry

var content = rawText;

// 1. Parse the content to look for the timestamp
var isFirstLine = false;
DateTimeOffset? timestamp = null;
DateTime? timestamp = null;

if (TimestampParser.TryParseConsoleTimestamp(content, out var timestampParseResult))
{
isFirstLine = true;
content = timestampParseResult.Value.ModifiedText;
timestamp = timestampParseResult.Value.Timestamp;
}
// 2. Parse the content to look for info/warn/dbug header
// TODO extract log level and use here
else
{
if (LogLevelParser.StartsWithLogLevelHeader(content))
{
isFirstLine = true;
}
timestamp = timestampParseResult.Value.Timestamp.UtcDateTime;
}

// 3. HTML Encode the raw text for security purposes
// 2. HTML Encode the raw text for security purposes
content = WebUtility.HtmlEncode(content);

// 4. Parse the content to look for ANSI Control Sequences and color them if possible
// 3. Parse the content to look for ANSI Control Sequences and color them if possible
var conversionResult = AnsiParser.ConvertToHtml(content, _residualState);
content = conversionResult.ConvertedText;
_residualState = conversionResult.ResidualState;

// 5. Parse the content to look for URLs and make them links if possible
// 4. Parse the content to look for URLs and make them links if possible
if (UrlParser.TryParse(content, out var modifiedText))
{
content = modifiedText;
}

// 6. Create the LogEntry to get the ID
// 5. Create the LogEntry
var logEntry = new LogEntry
{
Timestamp = timestamp,
Content = content,
Type = isErrorOutput ? LogEntryType.Error : LogEntryType.Default,
IsFirstLine = isFirstLine
Type = isErrorOutput ? LogEntryType.Error : LogEntryType.Default
};

// 7. Set the relative properties of the log entry (parent/line index/etc)
if (isFirstLine)
{
_parentTimestamp = logEntry.Timestamp;
_parentId = logEntry.Id;
_lineIndex = 0;
}
else if (_parentId.HasValue)
{
logEntry.ParentTimestamp = _parentTimestamp;
logEntry.ParentId = _parentId;
logEntry.LineIndex = ++_lineIndex;
}

// 8. Return the final result
return logEntry;
}
}
77 changes: 0 additions & 77 deletions src/Aspire.Dashboard/ConsoleLogs/TimestampParser.cs

This file was deleted.

1 change: 1 addition & 0 deletions src/Aspire.Dashboard/ConsoleLogs/UrlParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Text.RegularExpressions;

namespace Aspire.Dashboard.ConsoleLogs;

public static partial class UrlParser
{
private static readonly Regex s_urlRegEx = GenerateUrlRegEx();
Expand Down
Loading