From 4fd17415566e7e002d036e6f7fc5dd772622066e Mon Sep 17 00:00:00 2001
From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com>
Date: Sat, 7 Jun 2025 14:13:03 -0500
Subject: [PATCH] feat: setup otel and send to seq
---
.github/workflows/deploy_api.yml | 17 ++++++-
src/AltGen.API/AltGen.API.csproj | 5 ++
src/AltGen.API/Common/Instrumentation.cs | 21 ++++++++
src/AltGen.API/Common/SeqOptions.cs | 12 +++++
.../Common/WebAppBuilderExtensions.cs | 51 +++++++++++++++++++
src/AltGen.API/Program.cs | 5 +-
src/AltGen.API/Usings.cs | 9 +++-
7 files changed, 115 insertions(+), 5 deletions(-)
create mode 100644 src/AltGen.API/Common/Instrumentation.cs
create mode 100644 src/AltGen.API/Common/SeqOptions.cs
create mode 100644 src/AltGen.API/Common/WebAppBuilderExtensions.cs
diff --git a/.github/workflows/deploy_api.yml b/.github/workflows/deploy_api.yml
index f4c33d3..3153027 100644
--- a/.github/workflows/deploy_api.yml
+++ b/.github/workflows/deploy_api.yml
@@ -44,8 +44,23 @@ jobs:
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: |
+ cd altgen
+
+ echo 'SeqOptions__ServerUrl=http://seq:80' >> .env
+ echo 'SeqOptions__ApiKeyHeader=X-Seq-ApiKey' >> .env
+ echo 'SeqOptions__ApiKey=${{ secrets.SEQ_API_KEY }}' >> .env
+
docker stop api.altgen.stevanfreeborn.com
docker rm api.altgen.stevanfreeborn.com
docker pull ${{ secrets.DOCKERHUB_USERNAME }}/api.altgen.stevanfreeborn.com:${{ needs.build.outputs.version }}
- docker run --restart always -d -p 7778:8080 --name api.altgen.stevanfreeborn.com ${{ secrets.DOCKERHUB_USERNAME }}/api.altgen.stevanfreeborn.com:${{ needs.build.outputs.version }}
+ docker run \
+ --env-file .env \
+ --restart always \
+ -d \
+ -p 7778:8080 \
+ --name api.altgen.stevanfreeborn.com \
+ --network seq-network \
+ ${{ secrets.DOCKERHUB_USERNAME }}/api.altgen.stevanfreeborn.com:${{ needs.build.outputs.version }}
+
+ rm .env
diff --git a/src/AltGen.API/AltGen.API.csproj b/src/AltGen.API/AltGen.API.csproj
index 3aabe5f..9eeabf2 100644
--- a/src/AltGen.API/AltGen.API.csproj
+++ b/src/AltGen.API/AltGen.API.csproj
@@ -7,6 +7,11 @@
+
+
+
+
+
diff --git a/src/AltGen.API/Common/Instrumentation.cs b/src/AltGen.API/Common/Instrumentation.cs
new file mode 100644
index 0000000..16fd5b0
--- /dev/null
+++ b/src/AltGen.API/Common/Instrumentation.cs
@@ -0,0 +1,21 @@
+using System.Diagnostics;
+
+namespace AltGen.API.Common;
+
+class Instrumentation : IDisposable
+{
+ internal const string ActivitySourceName = "AltGen.API";
+ internal const string ActivitySourceVersion = "1.0.0";
+
+ public Instrumentation()
+ {
+ ActivitySource = new ActivitySource(ActivitySourceName, ActivitySourceVersion);
+ }
+
+ public ActivitySource ActivitySource { get; }
+
+ public void Dispose()
+ {
+ ActivitySource.Dispose();
+ }
+}
\ No newline at end of file
diff --git a/src/AltGen.API/Common/SeqOptions.cs b/src/AltGen.API/Common/SeqOptions.cs
new file mode 100644
index 0000000..9c7934a
--- /dev/null
+++ b/src/AltGen.API/Common/SeqOptions.cs
@@ -0,0 +1,12 @@
+namespace AltGen.API.Common;
+
+internal sealed class SeqOptions
+{
+ public string ServerUrl { get; set; } = string.Empty;
+ public string ApiKeyHeader { get; set; } = string.Empty;
+ public string ApiKey { get; set; } = string.Empty;
+ public bool IsEnabled => !string.IsNullOrWhiteSpace(ServerUrl) && !string.IsNullOrWhiteSpace(ApiKeyHeader) && !string.IsNullOrWhiteSpace(ApiKey);
+ 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/AltGen.API/Common/WebAppBuilderExtensions.cs b/src/AltGen.API/Common/WebAppBuilderExtensions.cs
new file mode 100644
index 0000000..d215dd3
--- /dev/null
+++ b/src/AltGen.API/Common/WebAppBuilderExtensions.cs
@@ -0,0 +1,51 @@
+namespace AltGen.API.Common;
+
+static class WebAppBuilderExtensions
+{
+ public static WebApplicationBuilder AddTelemetry(this WebApplicationBuilder builder)
+ {
+ var seq = new SeqOptions();
+ builder.Configuration.GetSection(nameof(SeqOptions)).Bind(seq);
+
+ if (seq.IsEnabled is false)
+ {
+ return builder;
+ }
+
+ builder.Services.AddSingleton();
+
+ builder.Services.AddOpenTelemetry()
+ .ConfigureResource(resource =>
+ {
+ resource.AddService(Instrumentation.ActivitySourceName, Instrumentation.ActivitySourceVersion);
+ resource.AddAttributes(new Dictionary
+ {
+ ["service.name"] = Instrumentation.ActivitySourceName,
+ ["service.version"] = Instrumentation.ActivitySourceVersion,
+ ["service.instance.id"] = Environment.MachineName,
+ ["service.namespace"] = "stevesbot",
+ ["service.environment"] = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? "production",
+ });
+ })
+ .WithLogging(lb => lb.AddOtlpExporter(o =>
+ {
+ o.Protocol = OtlpExportProtocol.HttpProtobuf;
+ o.Endpoint = new Uri(seq.LogEndpoint);
+ o.Headers = seq.AuthHeader;
+ }))
+ .WithTracing(tb =>
+ {
+ tb.AddSource(Instrumentation.ActivitySourceName);
+ tb.AddAspNetCoreInstrumentation();
+ tb.AddHttpClientInstrumentation();
+ tb.AddOtlpExporter(o =>
+ {
+ o.Protocol = OtlpExportProtocol.HttpProtobuf;
+ o.Endpoint = new Uri(seq.TraceEndpoint);
+ o.Headers = seq.AuthHeader;
+ });
+ });
+
+ return builder;
+ }
+}
\ No newline at end of file
diff --git a/src/AltGen.API/Program.cs b/src/AltGen.API/Program.cs
index 6caf5b4..03d9ac1 100644
--- a/src/AltGen.API/Program.cs
+++ b/src/AltGen.API/Program.cs
@@ -1,8 +1,7 @@
-using System.Globalization;
-using System.Threading.RateLimiting;
-
var builder = WebApplication.CreateBuilder(args);
+builder.AddTelemetry();
+
builder.Services.AddProblemDetails();
builder.Services.AddOpenApi();
diff --git a/src/AltGen.API/Usings.cs b/src/AltGen.API/Usings.cs
index 8254e07..a3c02f2 100644
--- a/src/AltGen.API/Usings.cs
+++ b/src/AltGen.API/Usings.cs
@@ -1,13 +1,20 @@
global using System.ComponentModel.DataAnnotations;
global using System.Diagnostics.CodeAnalysis;
+global using System.Globalization;
global using System.Reflection;
global using System.Text;
global using System.Text.Json;
global using System.Text.Json.Serialization;
+global using System.Threading.RateLimiting;
global using AltGen.API.Common;
global using AltGen.API.Generate;
global using AltGen.API.Generate.Providers;
global using AltGen.API.Generate.Providers.Gemini;
-global using Microsoft.AspNetCore.Mvc;
\ No newline at end of file
+global using Microsoft.AspNetCore.Mvc;
+
+global using OpenTelemetry.Exporter;
+global using OpenTelemetry.Logs;
+global using OpenTelemetry.Resources;
+global using OpenTelemetry.Trace;