diff --git a/src/BUTR.CrashReportServer/BUTR.CrashReportServer.csproj b/src/BUTR.CrashReportServer/BUTR.CrashReportServer.csproj
index bb5ba83..affebb7 100644
--- a/src/BUTR.CrashReportServer/BUTR.CrashReportServer.csproj
+++ b/src/BUTR.CrashReportServer/BUTR.CrashReportServer.csproj
@@ -16,13 +16,29 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BUTR.CrashReportServer/Controllers/CrashUploadController.cs b/src/BUTR.CrashReportServer/Controllers/CrashUploadController.cs
index 1b9deb5..6b55237 100644
--- a/src/BUTR.CrashReportServer/Controllers/CrashUploadController.cs
+++ b/src/BUTR.CrashReportServer/Controllers/CrashUploadController.cs
@@ -16,6 +16,7 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics.Metrics;
using System.IO.Pipelines;
using System.Text.Json;
using System.Text.Json.Serialization;
@@ -36,8 +37,20 @@ public sealed record CrashReportUploadBody(CrashReportModel CrashReport, ICollec
private readonly AppDbContext _dbContext;
private readonly GZipCompressor _gZipCompressor;
- public CrashUploadController(ILogger logger, IOptionsSnapshot options, IOptionsSnapshot jsonSerializerOptions, AppDbContext dbContext, GZipCompressor gZipCompressor)
+ private readonly Counter _reportVersion;
+
+ public CrashUploadController(
+ ILogger logger,
+ IOptionsSnapshot options,
+ IOptionsSnapshot jsonSerializerOptions,
+ AppDbContext dbContext,
+ GZipCompressor gZipCompressor,
+ IMeterFactory meterFactory)
{
+ var meter = meterFactory.Create("BUTR.CrashReportServer.Controllers.CrashUploadController", "1.0.0");
+
+ _reportVersion = meter.CreateCounter("report-version", unit: "Count");
+
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_jsonSerializerOptions = jsonSerializerOptions.Value ?? throw new ArgumentNullException(nameof(jsonSerializerOptions));
_options = options.Value ?? throw new ArgumentNullException(nameof(options));
@@ -86,6 +99,8 @@ private async Task UploadHtmlAsync(CancellationToken ct)
if (version >= 13) await _dbContext.Set().AddAsync(new JsonEntity { Id = idEntity, CrashReportCompressed = compressedJsonStream.ToArray(), }, ct);
await _dbContext.SaveChangesAsync(ct);
+ _reportVersion.Add(1, new[] {new KeyValuePair("Version", version)});
+
return Ok($"{_options.BaseUri}/{idEntity.FileId}");
}
@@ -112,6 +127,8 @@ private async Task UploadJsonAsync(CancellationToken ct)
await _dbContext.Set().AddAsync(new FileEntity { Id = idEntity, DataCompressed = compressedHtmlStream.ToArray(), }, ct);
await _dbContext.SaveChangesAsync(ct);
+ _reportVersion.Add(1, new[] {new KeyValuePair("Version", crashReport.Version)});
+
return Ok($"{_options.BaseUri}/{idEntity.FileId}");
}
diff --git a/src/BUTR.CrashReportServer/Controllers/ReportController.cs b/src/BUTR.CrashReportServer/Controllers/ReportController.cs
index 976ecab..f053eaa 100644
--- a/src/BUTR.CrashReportServer/Controllers/ReportController.cs
+++ b/src/BUTR.CrashReportServer/Controllers/ReportController.cs
@@ -52,7 +52,7 @@ public ReportController(ILogger logger, AppDbContext dbContext
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool ValidateFileName(string? fileName) => fileName?.Length is 6 or 8 or 10 && fileName.All(IsHex);
- private IActionResult? ValidateRequest(ref string filename)
+ private StatusCodeResult? ValidateRequest(ref string filename)
{
if (string.IsNullOrEmpty(filename))
return StatusCode((int) HttpStatusCode.InternalServerError);
diff --git a/src/BUTR.CrashReportServer/Program.cs b/src/BUTR.CrashReportServer/Program.cs
index aea588a..fdcd06d 100644
--- a/src/BUTR.CrashReportServer/Program.cs
+++ b/src/BUTR.CrashReportServer/Program.cs
@@ -2,8 +2,22 @@
using BUTR.CrashReportServer.Extensions;
using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using OpenTelemetry.Exporter;
+using OpenTelemetry.Logs;
+using OpenTelemetry.Metrics;
+using OpenTelemetry.ResourceDetectors.Container;
+using OpenTelemetry.Resources;
+using OpenTelemetry.Trace;
+
+using Serilog;
+using Serilog.Events;
+
+using System;
using System.Security.Authentication;
using System.Threading.Tasks;
@@ -13,14 +27,88 @@ public static class Program
{
public static async Task Main(string[] args)
{
- var builder = CreateHostBuilder(args);
- var host = builder.Build();
- await host.SeedDbContextAsync();
- await host.RunAsync();
+ Log.Logger = new LoggerConfiguration()
+ .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
+ .Enrich.FromLogContext()
+ .WriteTo.Console()
+ .CreateBootstrapLogger();
+
+ try
+ {
+ Log.Information("Starting web application");
+
+ var builder = CreateHostBuilder(args);
+ var host = builder.Build();
+ await host.SeedDbContextAsync();
+ await host.RunAsync();
+ }
+ catch (Exception ex)
+ {
+ Log.Fatal(ex, "Application terminated unexpectedly");
+ }
+ finally
+ {
+ await Log.CloseAndFlushAsync();
+ }
}
public static IHostBuilder CreateHostBuilder(string[] args) => Host
.CreateDefaultBuilder(args)
+ .ConfigureServices((ctx, services) =>
+ {
+ if (ctx.Configuration.GetSection("Oltp") is { } oltpSection)
+ {
+ var openTelemetry = services.AddOpenTelemetry()
+ .ConfigureResource(builder =>
+ {
+ builder.AddDetector(new ContainerResourceDetector());
+ builder.AddService(
+ ctx.HostingEnvironment.ApplicationName,
+ ctx.HostingEnvironment.EnvironmentName,
+ typeof(Program).Assembly.GetName().Version?.ToString(),
+ false,
+ Environment.MachineName);
+ builder.AddTelemetrySdk();
+ });
+
+ if (oltpSection.GetValue("MetricsEndpoint") is { } metricsEndpoint)
+ {
+ var metricsProtocol = oltpSection.GetValue("MetricsProtocol");
+ openTelemetry.WithMetrics(builder => builder
+ .AddMeter("BUTR.CrashReportServer.Controllers.CrashUploadController")
+ .AddProcessInstrumentation()
+ .AddRuntimeInstrumentation(instrumentationOptions =>
+ {
+
+ })
+ .AddAspNetCoreInstrumentation()
+ .AddOtlpExporter(o =>
+ {
+ o.Endpoint = new Uri(metricsEndpoint);
+ o.Protocol = metricsProtocol;
+ }));
+ }
+
+ if (oltpSection.GetValue("TracingEndpoint") is { } tracingEndpoint)
+ {
+ var tracingProtocol = oltpSection.GetValue("TracingProtocol");
+ openTelemetry.WithTracing(builder => builder
+ .AddEntityFrameworkCoreInstrumentation(instrumentationOptions =>
+ {
+ instrumentationOptions.SetDbStatementForText = true;
+ })
+ .AddAspNetCoreInstrumentation(instrumentationOptions =>
+ {
+ instrumentationOptions.RecordException = true;
+ })
+ .AddOtlpExporter(o =>
+ {
+ o.Endpoint = new Uri(tracingEndpoint);
+ o.Protocol = tracingProtocol;
+ }));
+ }
+ }
+ })
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseKestrel(kestrelOptions =>
@@ -32,5 +120,32 @@ public static IHostBuilder CreateHostBuilder(string[] args) => Host
});
webBuilder.UseStartup();
+ })
+ .UseSerilog((context, services, configuration) =>
+ {
+ configuration
+ .ReadFrom.Configuration(context.Configuration)
+ .ReadFrom.Services(services);
+ }, writeToProviders: true)
+ .ConfigureLogging((ctx, builder) =>
+ {
+ var oltpSection = ctx.Configuration.GetSection("Oltp");
+ if (oltpSection == null!) return;
+
+ var loggingEndpoint = oltpSection.GetValue("LoggingEndpoint");
+ if (loggingEndpoint is null) return;
+ var loggingProtocol = oltpSection.GetValue("LoggingProtocol");
+
+ builder.AddOpenTelemetry(o =>
+ {
+ o.IncludeScopes = true;
+ o.ParseStateValues = true;
+ o.IncludeFormattedMessage = true;
+ o.AddOtlpExporter((options, processorOptions) =>
+ {
+ options.Endpoint = new Uri(loggingEndpoint);
+ options.Protocol = loggingProtocol;
+ });
+ });
});
}
\ No newline at end of file