diff --git a/Wave/Data/Features.cs b/Wave/Data/Features.cs index c33e57a..e30bfc5 100644 --- a/Wave/Data/Features.cs +++ b/Wave/Data/Features.cs @@ -4,4 +4,5 @@ public class Features { public bool Rss { get; set; } = true; public bool EmailSubscriptions { get; set; } = false; public bool NativeSignup { get; set; } = true; + public bool Telemetry {get; set; } = false; } \ No newline at end of file diff --git a/Wave/Program.cs b/Wave/Program.cs index 9f7520c..a330303 100644 --- a/Wave/Program.cs +++ b/Wave/Program.cs @@ -19,6 +19,10 @@ using Wave.Services; using Wave.Utilities; using Microsoft.IdentityModel.Protocols.OpenIdConnect; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; using Serilog; using Serilog.Events; using Serilog.Sinks.Grafana.Loki; @@ -48,6 +52,8 @@ .AddXmlFile( Path.Combine(FileSystemService.ConfigurationDirectory, "config.xml"), true, false) .AddEnvironmentVariables("WAVE_"); +var customizations = builder.Configuration.GetSection(nameof(Customization)).Get(); + #region Logging builder.Services.AddSerilog((services, configuration) => { @@ -56,8 +62,7 @@ .ReadFrom.Services(services) .Enrich.WithProperty("Application", "Wave") .Enrich.WithProperty("WaveVersion", humanReadableVersion) - .Enrich.WithProperty("AppName", - builder.Configuration.GetSection(nameof(Customization))[nameof(Customization.AppName)]) + .Enrich.WithProperty("AppName", customizations?.AppName) .Enrich.FromLogContext(); if (builder.Configuration["loki"] is {} lokiConfiguration) { configuration.WriteTo.GrafanaLoki(lokiConfiguration, null, [ @@ -270,6 +275,38 @@ #endregion +#region Open Telemetry + +var features = builder.Configuration.GetSection(nameof(Features)).Get(); +if (features?.Telemetry is true) { + var otel = builder.Services.AddOpenTelemetry(); + + otel.ConfigureResource(resource => resource.AddService(serviceName:customization?.AppName ?? "Wave")); + + // Prometheus + otel.WithMetrics(metrics => metrics + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddMeter("Microsoft.AspNetCore.Hosting") + .AddMeter("Microsoft.AspNetCore.Server.Kestrel") + .AddMeter("Microsoft.AspNetCore.Http.Connections") + .AddMeter("Microsoft.AspNetCore.Http.Routing") + .AddMeter("Microsoft.AspNetCore.Diagnostics") + + .AddPrometheusExporter()); + + // Jaeger etc. + if (builder.Configuration["OTLP_ENDPOINT_URL"] is {} otlpUrl) { + otel.WithTracing(tracing => { + tracing.AddAspNetCoreInstrumentation(); + tracing.AddHttpClientInstrumentation(); + tracing.AddOtlpExporter(options => options.Endpoint = new Uri(otlpUrl)); + }); + } +} + +#endregion + var app = builder.Build(); // Configure the HTTP request pipeline. @@ -279,6 +316,10 @@ app.UseExceptionHandler("/Error", createScopeForErrors: true); } +if (features?.Telemetry is true) { + app.UseOpenTelemetryPrometheusScrapingEndpoint(); +} + app.UseSerilogRequestLogging(); app.UseStaticFiles(new StaticFileOptions { diff --git a/Wave/Wave.csproj b/Wave/Wave.csproj index 80dcbb3..204c049 100644 --- a/Wave/Wave.csproj +++ b/Wave/Wave.csproj @@ -32,6 +32,11 @@ + + + + + diff --git a/docker-compose.dcproj b/docker-compose.dcproj index 875fa3e..d93ecec 100644 --- a/docker-compose.dcproj +++ b/docker-compose.dcproj @@ -15,6 +15,7 @@ + \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index ba73bd0..59f4db8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,6 +17,7 @@ services: - "WAVE_ConnectionStrings__DefaultConnection=Host=db; Username=wave; Password=development; Include Error Detail=true" - "WAVE_ConnectionStrings__Redis=redis,password=development" - "WAVE_Loki=http://loki:3100" + - "WAVE_Features__Telemetry=true" volumes: - wave-files:/app/files - wave-config:/configuration @@ -64,6 +65,13 @@ services: depends_on: loki: condition: service_healthy + prometheus: + image: prom/prometheus:v2.45.0 + restart: unless-stopped + ports: + - 9090:9090 + volumes: + - ./prometheus:/etc/prometheus mailhog: image: mailhog/mailhog:latest diff --git a/prometheus/prometheus.yml b/prometheus/prometheus.yml new file mode 100644 index 0000000..bc1a4f3 --- /dev/null +++ b/prometheus/prometheus.yml @@ -0,0 +1,8 @@ +global: + scrape_interval: 5s + +scrape_configs: + - job_name: wave + scrape_interval: 1s + static_configs: + - targets: ["wave:8080"] \ No newline at end of file diff --git a/provisioning/datasources/auomatic.yml b/provisioning/datasources/auomatic.yml index e2704d5..91aa2d9 100644 --- a/provisioning/datasources/auomatic.yml +++ b/provisioning/datasources/auomatic.yml @@ -5,7 +5,7 @@ deleteDatasources: orgId: 1 datasources: - - name: Wave + - name: Wave Logs type: loki isDefault: true orgId: 1 @@ -15,3 +15,9 @@ datasources: jsonData: timeout: 60 maxLines: 1000 + - name: Wave Metrics + type: prometheus + ordId: 1 + access: proxy + url: http://prometheus:9090 + editable: true