From 64c64e8d9514049f4b9efd9350aa05283449cd91 Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Wed, 11 Mar 2026 18:52:05 -0500 Subject: [PATCH] feat(servicedefaults): add SeqOptions configuration and integrate with OpenTelemetry logging and tracing --- .github/workflows/deploy.yml | 6 ++++ docker/docker-compose.prod.yml | 3 ++ docker/docker-compose.yml | 3 ++ src/FiscalOS.API/appsettings.Example.json | 7 +++- src/FiscalOS.ServiceDefaults/SeqOptions.cs | 14 ++++++++ .../ServiceDefaultsExtensions.cs | 36 +++++++++++++++++-- 6 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 src/FiscalOS.ServiceDefaults/SeqOptions.cs diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 840c5a9..b60cb88 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -70,6 +70,9 @@ jobs: PLAID_CLIENT_ID: ${{ secrets.PLAID_CLIENT_ID }} PLAID_SECRET: ${{ secrets.PLAID_SECRET }} PLAID_WEBHOOK: ${{ secrets.PLAID_WEBHOOK }} + SEQ_SERVER_URL: ${{ secrets.SEQ_SERVER_URL }} + SEQ_API_KEY_HEADER: ${{ secrets.SEQ_API_KEY_HEADER }} + SEQ_API_KEY: ${{ secrets.SEQ_API_KEY }} with: host: ${{ secrets.DEPLOY_HOST }} username: ${{ secrets.DEPLOY_USER }} @@ -88,6 +91,9 @@ jobs: PLAID_CLIENT_ID=${PLAID_CLIENT_ID} PLAID_SECRET=${PLAID_SECRET} PLAID_WEBHOOK=${PLAID_WEBHOOK} + SEQ_SERVER_URL=${SEQ_SERVER_URL} + SEQ_API_KEY_HEADER=${SEQ_API_KEY_HEADER} + SEQ_API_KEY=${SEQ_API_KEY} EOF docker compose -f docker-compose.prod.yml pull docker compose -f docker-compose.prod.yml up -d --remove-orphans diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index 37e3bd9..6a9e601 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -21,6 +21,9 @@ services: - PlaidClientOptions__ClientId=${PLAID_CLIENT_ID} - PlaidClientOptions__Secret=${PLAID_SECRET} - PlaidClientOptions__Webhook=${PLAID_WEBHOOK} + - SeqOptions__ServerUrl=${SEQ_SERVER_URL} + - SeqOptions__ApiKeyHeader=${SEQ_API_KEY_HEADER} + - SeqOptions__ApiKey=${SEQ_API_KEY} volumes: - ./data:/data - ./keys:/keys diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index e6c6205..8c2dd34 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -27,6 +27,9 @@ services: - PlaidClientOptions__ClientId=${PLAID_CLIENT_ID} - PlaidClientOptions__Secret=${PLAID_SECRET} - PlaidClientOptions__Webhook=${PLAID_WEBHOOK} + - SeqOptions__ServerUrl=${SEQ_SERVER_URL} + - SeqOptions__ApiKeyHeader=${SEQ_API_KEY_HEADER} + - SeqOptions__ApiKey=${SEQ_API_KEY} volumes: - ./data:/data - ./keys:/keys diff --git a/src/FiscalOS.API/appsettings.Example.json b/src/FiscalOS.API/appsettings.Example.json index 585eaf8..7dd96ea 100644 --- a/src/FiscalOS.API/appsettings.Example.json +++ b/src/FiscalOS.API/appsettings.Example.json @@ -23,5 +23,10 @@ "ClientId": "ClientId", "Secret": "Secret", "Webhook": "Webhook" + }, + "SeqOptions": { + "ServerUrl": "ServerUrl", + "ApiKeyHeader": "ApiKeyHeader", + "ApiKey": "ApiKey" } -} +} \ No newline at end of file diff --git a/src/FiscalOS.ServiceDefaults/SeqOptions.cs b/src/FiscalOS.ServiceDefaults/SeqOptions.cs new file mode 100644 index 0000000..892f3f0 --- /dev/null +++ b/src/FiscalOS.ServiceDefaults/SeqOptions.cs @@ -0,0 +1,14 @@ +namespace FiscalOS.ServiceDefaults; + +internal sealed class SeqOptions +{ + public string ServerUrl { get; init; } = string.Empty; + public string ApiKeyHeader { get; init; } = string.Empty; + public string ApiKey { get; init; } = string.Empty; + public bool IsEnabled => string.IsNullOrWhiteSpace(ServerUrl) is false && + string.IsNullOrWhiteSpace(ApiKeyHeader) is false && + string.IsNullOrWhiteSpace(ApiKey) is false; + public string LogEndpoint => $"{ServerUrl}/ingest/otlp/v1/logs"; + public string TraceEndpoint => $"{ServerUrl}/ingest/otlp/v1/traces"; + public string AuthHeader => $"{ApiKeyHeader}={ApiKey}"; +} \ No newline at end of file diff --git a/src/FiscalOS.ServiceDefaults/ServiceDefaultsExtensions.cs b/src/FiscalOS.ServiceDefaults/ServiceDefaultsExtensions.cs index 3fe1393..28936eb 100644 --- a/src/FiscalOS.ServiceDefaults/ServiceDefaultsExtensions.cs +++ b/src/FiscalOS.ServiceDefaults/ServiceDefaultsExtensions.cs @@ -1,3 +1,8 @@ +using Microsoft.Extensions.Configuration; + +using OpenTelemetry.Exporter; +using OpenTelemetry.Logs; + namespace FiscalOS.ServiceDefaults; public static class ServiceDefaultsExtensions @@ -76,11 +81,38 @@ public static class ServiceDefaultsExtensions private static TBuilder AddOpenTelemetryExporters(this TBuilder builder) where TBuilder : IHostApplicationBuilder { + var seqOptions = new SeqOptions(); + builder.Configuration.GetSection(nameof(SeqOptions)).Bind(seqOptions); + var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); - if (useOtlpExporter) + var otel = builder.Services.AddOpenTelemetry(); + + if (seqOptions.IsEnabled) { - builder.Services.AddOpenTelemetry().UseOtlpExporter(); + otel + .WithLogging(lb => + { + lb.AddOtlpExporter(o => + { + o.Protocol = OtlpExportProtocol.HttpProtobuf; + o.Endpoint = new Uri(seqOptions.LogEndpoint); + o.Headers = seqOptions.AuthHeader; + }); + }) + .WithTracing(tb => + { + tb.AddOtlpExporter(o => + { + o.Protocol = OtlpExportProtocol.HttpProtobuf; + o.Endpoint = new Uri(seqOptions.TraceEndpoint); + o.Headers = seqOptions.AuthHeader; + }); + }); + } + else if (useOtlpExporter) + { + otel.UseOtlpExporter(); } return builder;