Skip to content

Commit

Permalink
Introduce dedicated error collector for serve command
Browse files Browse the repository at this point in the history
  • Loading branch information
Mpdreamz committed Jan 10, 2025
1 parent 085b9fe commit fad189a
Show file tree
Hide file tree
Showing 14 changed files with 225 additions and 134 deletions.
18 changes: 2 additions & 16 deletions src/Elastic.Markdown/Diagnostics/DiagnosticsChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,23 +58,9 @@ public interface IDiagnosticsOutput
public void Write(Diagnostic diagnostic);
}

public class LogDiagnosticOutput(ILogger logger) : IDiagnosticsOutput
{
public void Write(Diagnostic diagnostic)
{
if (diagnostic.Severity == Severity.Error)
logger.LogError($"{diagnostic.Message} ({diagnostic.File}:{diagnostic.Line})");
else
logger.LogWarning($"{diagnostic.Message} ({diagnostic.File}:{diagnostic.Line})");
}
}

public class DiagnosticsCollector(ILoggerFactory loggerFactory, IReadOnlyCollection<IDiagnosticsOutput> outputs)
public class DiagnosticsCollector(IReadOnlyCollection<IDiagnosticsOutput> outputs)
: IHostedService
{
private readonly IReadOnlyCollection<IDiagnosticsOutput> _outputs =
[new LogDiagnosticOutput(loggerFactory.CreateLogger<LogDiagnosticOutput>()), .. outputs];

public DiagnosticsChannel Channel { get; } = new();

private int _errors;
Expand Down Expand Up @@ -117,7 +103,7 @@ void Drain()
IncrementSeverityCount(item);
HandleItem(item);
OffendingFiles.Add(item.File);
foreach (var output in _outputs)
foreach (var output in outputs)
output.Write(item);
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/docs-builder/Cli/Commands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Actions.Core.Services;
using ConsoleAppFramework;
using Documentation.Builder.Diagnostics;
using Documentation.Builder.Diagnostics.Console;
using Documentation.Builder.Http;
using Elastic.Markdown;
using Elastic.Markdown.IO;
Expand All @@ -27,6 +28,8 @@ public async Task Serve(string? path = null, Cancel ctx = default)
{
var host = new DocumentationWebHost(path, logger, new FileSystem());
await host.RunAsync(ctx);
await host.StopAsync(ctx);

}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using Actions.Core.Services;
using Elastic.Markdown.Diagnostics;
using Microsoft.Extensions.Logging;
using Spectre.Console;
using Diagnostic = Elastic.Markdown.Diagnostics.Diagnostic;

namespace Documentation.Builder.Diagnostics.Console;

public class ConsoleDiagnosticsCollector(ILoggerFactory loggerFactory, ICoreService? githubActions = null)
: DiagnosticsCollector([new Log(loggerFactory.CreateLogger<Log>()), new GithubAnnotationOutput(githubActions)]
)
{
private readonly List<Diagnostic> _items = new();

protected override void HandleItem(Diagnostic diagnostic) => _items.Add(diagnostic);

public override async Task StopAsync(Cancel ctx)
{
var repository = new ErrataFileSourceRepository();
repository.WriteDiagnosticsToConsole(_items);

AnsiConsole.WriteLine();
AnsiConsole.Write(new Markup($" [bold red]{Errors} Errors[/] / [bold blue]{Warnings} Warnings[/]"));
AnsiConsole.WriteLine();
AnsiConsole.WriteLine();

await Task.CompletedTask;
}
}
53 changes: 53 additions & 0 deletions src/docs-builder/Diagnostics/Console/ErrataFileSourceRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using System.Diagnostics.CodeAnalysis;
using System.Text;
using Cysharp.IO;
using Elastic.Markdown.Diagnostics;
using Errata;
using Spectre.Console;
using Diagnostic = Elastic.Markdown.Diagnostics.Diagnostic;

namespace Documentation.Builder.Diagnostics.Console;

public class ErrataFileSourceRepository : ISourceRepository
{
public bool TryGet(string id, [NotNullWhen(true)] out Source? source)
{
using var reader = new Utf8StreamReader(id);
var text = Encoding.UTF8.GetString(reader.ReadToEndAsync().GetAwaiter().GetResult());
source = new Source(id, text);
return true;
}

public void WriteDiagnosticsToConsole(IReadOnlyCollection<Diagnostic> items)
{
var report = new Report(this);
foreach (var item in items)
{
var d = item.Severity switch
{
Severity.Error => Errata.Diagnostic.Error(item.Message),
Severity.Warning => Errata.Diagnostic.Warning(item.Message),
_ => Errata.Diagnostic.Info(item.Message)
};
if (item is { Line: not null, Column: not null })
{
var location = new Location(item.Line ?? 0, item.Column ?? 0);
d = d.WithLabel(new Label(item.File, location, "")
.WithLength(item.Length == null ? 1 : Math.Clamp(item.Length.Value, 1, item.Length.Value + 3))
.WithPriority(1)
.WithColor(item.Severity == Severity.Error ? Color.Red : Color.Blue));
}
else
d = d.WithNote(item.File);

report.AddDiagnostic(d);
}

// Render the report
report.Render(AnsiConsole.Console);
}
}
30 changes: 30 additions & 0 deletions src/docs-builder/Diagnostics/Console/GithubAnnotationOutput.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using Actions.Core;
using Actions.Core.Services;
using Elastic.Markdown.Diagnostics;

namespace Documentation.Builder.Diagnostics.Console;

public class GithubAnnotationOutput(ICoreService? githubActions) : IDiagnosticsOutput
{
public void Write(Diagnostic diagnostic)
{
if (githubActions == null) return;
if (string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("GITHUB_ACTION")))
return;
var properties = new AnnotationProperties
{
File = diagnostic.File,
StartColumn = diagnostic.Column,
StartLine = diagnostic.Line,
EndColumn = diagnostic.Column + diagnostic.Length ?? 1
};
if (diagnostic.Severity == Severity.Error)
githubActions.WriteError(diagnostic.Message, properties);
if (diagnostic.Severity == Severity.Warning)
githubActions.WriteWarning(diagnostic.Message, properties);
}
}
92 changes: 0 additions & 92 deletions src/docs-builder/Diagnostics/ErrorCollector.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using Elastic.Markdown.Diagnostics;
using Microsoft.Extensions.Logging;
using Diagnostic = Elastic.Markdown.Diagnostics.Diagnostic;

namespace Documentation.Builder.Diagnostics.LiveMode;

public class LiveModeDiagnosticsCollector(ILoggerFactory loggerFactory)
: DiagnosticsCollector([new Log(loggerFactory.CreateLogger<Log>())])
{
protected override void HandleItem(Diagnostic diagnostic) { }

public override async Task StopAsync(Cancel ctx) => await Task.CompletedTask;
}
22 changes: 22 additions & 0 deletions src/docs-builder/Diagnostics/Log.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using Elastic.Markdown.Diagnostics;
using Microsoft.Extensions.Logging;

// ReSharper disable once CheckNamespace
namespace Documentation.Builder;

// named Log for terseness on console output
public class Log(ILogger logger) : IDiagnosticsOutput
{
public void Write(Diagnostic diagnostic)
{
if (diagnostic.Severity == Severity.Error)
logger.LogError($"{diagnostic.Message} ({diagnostic.File}:{diagnostic.Line})");
else
logger.LogWarning($"{diagnostic.Message} ({diagnostic.File}:{diagnostic.Line})");
}
}

40 changes: 31 additions & 9 deletions src/docs-builder/Http/DocumentationWebHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using System.Diagnostics.CodeAnalysis;
using System.IO.Abstractions;
using Documentation.Builder.Diagnostics;
using Documentation.Builder.Diagnostics.Console;
using Documentation.Builder.Diagnostics.LiveMode;
using Elastic.Markdown;
using Elastic.Markdown.Diagnostics;
using Elastic.Markdown.IO;
Expand All @@ -25,25 +27,35 @@ public class DocumentationWebHost
private readonly WebApplication _webApplication;

private readonly string _staticFilesDirectory;
private readonly BuildContext _context;

public DocumentationWebHost(string? path, ILoggerFactory logger, IFileSystem fileSystem)
{
var builder = WebApplication.CreateSlimBuilder();
var context = new BuildContext(fileSystem, fileSystem, path, null)

builder.Logging.ClearProviders();
builder.Logging.SetMinimumLevel(LogLevel.Warning)
.AddFilter("Microsoft.AspNetCore.Hosting.Diagnostics", LogLevel.Error)
.AddFilter("Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware", LogLevel.Error)
.AddFilter("Microsoft.Hosting.Lifetime", LogLevel.Information)

.AddSimpleConsole(o => o.SingleLine = true);

_context = new BuildContext(fileSystem, fileSystem, path, null)
{
Collector = new ConsoleDiagnosticsCollector(logger)
Collector = new LiveModeDiagnosticsCollector(logger)
};
builder.Services.AddAotLiveReload(s =>
{
s.FolderToMonitor = context.SourcePath.FullName;
s.FolderToMonitor = _context.SourcePath.FullName;
s.ClientFileExtensions = ".md,.yml";
});
builder.Services.AddSingleton<ReloadableGeneratorState>(_ => new ReloadableGeneratorState(context.SourcePath, null, context, logger));
builder.Services.AddSingleton<ReloadableGeneratorState>(_ => new ReloadableGeneratorState(_context.SourcePath, null, _context, logger));
builder.Services.AddHostedService<ReloadGeneratorService>();
builder.Services.AddSingleton(logger);
builder.Logging.SetMinimumLevel(LogLevel.Warning);

_staticFilesDirectory = Path.Combine(context.SourcePath.FullName, "_static");
//builder.Services.AddSingleton(logger);

_staticFilesDirectory = Path.Combine(_context.SourcePath.FullName, "_static");
#if DEBUG
// this attempts to serve files directly from their source rather than the embedded resourses during development.
// this allows us to change js/css files without restarting the webserver
Expand All @@ -57,7 +69,17 @@ public DocumentationWebHost(string? path, ILoggerFactory logger, IFileSystem fil
}


public async Task RunAsync(Cancel ctx) => await _webApplication.RunAsync(ctx);
public async Task RunAsync(Cancel ctx)
{
_ = _context.Collector.StartAsync(ctx);
await _webApplication.RunAsync(ctx);
}

public async Task StopAsync(Cancel ctx)
{
_context.Collector.Channel.TryComplete();
await _context.Collector.StopAsync(ctx);
}

private void SetUpRoutes()
{
Expand Down Expand Up @@ -87,8 +109,8 @@ private static async Task<IResult> ServeDocumentationFile(ReloadableGeneratorSta
{
case MarkdownFile markdown:
{
await markdown.ParseFullAsync(ctx);
var rendered = await generator.RenderLayout(markdown, ctx);

return Results.Content(rendered, "text/html");
}
case ImageFile image:
Expand Down
Loading

0 comments on commit fad189a

Please sign in to comment.