feat: add telemetry support with OpenTelemetry and Seq configuration
This commit is contained in:
Vendored
+1
@@ -4,6 +4,7 @@
|
|||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"Reconnectable",
|
"Reconnectable",
|
||||||
"Steves",
|
"Steves",
|
||||||
|
"stevesbot",
|
||||||
"Szalay"
|
"Szalay"
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,7 @@ internal static class Extensions
|
|||||||
var discordOptions = sp.GetRequiredService<DiscordClientOptions>();
|
var discordOptions = sp.GetRequiredService<DiscordClientOptions>();
|
||||||
c.BaseAddress = new Uri(discordOptions.ApiUrl);
|
c.BaseAddress = new Uri(discordOptions.ApiUrl);
|
||||||
c.DefaultRequestHeaders.Authorization = new("Bot", discordOptions.AppToken);
|
c.DefaultRequestHeaders.Authorization = new("Bot", discordOptions.AppToken);
|
||||||
c.DefaultRequestHeaders.Add("User-Agent", $"DiscordBot (https://github.com/StevanFreeborn/steves-bot, 0.0.0)");
|
c.DefaultRequestHeaders.Add("User-Agent", $"DiscordBot (https://github.com/StevanFreeborn/steves-bot, {Instrumentation.SourceVersion})");
|
||||||
})
|
})
|
||||||
.AddStandardResilienceHandler();
|
.AddStandardResilienceHandler();
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Activity = StevesBot.Worker.Discord.Gateway.Events.Data.Activity;
|
||||||
|
|
||||||
namespace StevesBot.Worker.Discord.Gateway;
|
namespace StevesBot.Worker.Discord.Gateway;
|
||||||
|
|
||||||
internal sealed class DiscordGatewayClient : IDiscordGatewayClient
|
internal sealed class DiscordGatewayClient : IDiscordGatewayClient
|
||||||
@@ -201,7 +203,7 @@ internal sealed class DiscordGatewayClient : IDiscordGatewayClient
|
|||||||
}
|
}
|
||||||
catch (OperationCanceledException ex)
|
catch (OperationCanceledException ex)
|
||||||
{
|
{
|
||||||
_logger.LogInformation(ex, "Receive messages operation canceled: {Message}", ex.Message);
|
_logger.LogInformation(ex, "Receive messages operation canceled");
|
||||||
}
|
}
|
||||||
#pragma warning disable CA1031 // Do not catch general exception types
|
#pragma warning disable CA1031 // Do not catch general exception types
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Activity = StevesBot.Worker.Discord.Gateway.Events.Data.Activity;
|
||||||
|
|
||||||
namespace StevesBot.Worker.Discord.Gateway.Events;
|
namespace StevesBot.Worker.Discord.Gateway.Events;
|
||||||
|
|
||||||
internal sealed record UpdatePresenceDiscordEvent : DiscordEvent
|
internal sealed record UpdatePresenceDiscordEvent : DiscordEvent
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ builder.Services.AddSingleton(static sp =>
|
|||||||
return discordOptions;
|
return discordOptions;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
builder.AddTelemetry();
|
||||||
|
|
||||||
builder.Services.AddSingleton<IWebSocketFactory, WebSocketFactory>();
|
builder.Services.AddSingleton<IWebSocketFactory, WebSocketFactory>();
|
||||||
builder.Services.AddSingleton(TimeProvider.System);
|
builder.Services.AddSingleton(TimeProvider.System);
|
||||||
|
|
||||||
@@ -23,4 +25,5 @@ builder.Services.AddDiscordGatewayClient(static (client) =>
|
|||||||
builder.Services.AddHostedService<Worker>();
|
builder.Services.AddHostedService<Worker>();
|
||||||
|
|
||||||
var host = builder.Build();
|
var host = builder.Build();
|
||||||
|
|
||||||
await host.RunAsync();
|
await host.RunAsync();
|
||||||
@@ -8,6 +8,11 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.5" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.5" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="9.5.0" />
|
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="9.5.0" />
|
||||||
|
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.12.0" />
|
||||||
|
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.12.0" />
|
||||||
|
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.12.0" />
|
||||||
|
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.12.0" />
|
||||||
|
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.12.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
namespace StevesBot.Worker.Telemetry;
|
||||||
|
|
||||||
|
internal static class Extensions
|
||||||
|
{
|
||||||
|
public static HostApplicationBuilder AddTelemetry(this HostApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
builder.Services.AddSingleton<Instrumentation>();
|
||||||
|
|
||||||
|
var seq = new SeqOptions();
|
||||||
|
builder.Configuration.GetSection(nameof(SeqOptions)).Bind(seq);
|
||||||
|
|
||||||
|
if (seq.IsEnabled is false)
|
||||||
|
{
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Services.AddOpenTelemetry()
|
||||||
|
.ConfigureResource(resource =>
|
||||||
|
{
|
||||||
|
resource.AddService(Instrumentation.SourceName, Instrumentation.SourceVersion);
|
||||||
|
resource.AddAttributes(new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["service.name"] = Instrumentation.SourceName,
|
||||||
|
["service.version"] = Instrumentation.SourceVersion,
|
||||||
|
["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.SourceName);
|
||||||
|
tb.AddAspNetCoreInstrumentation();
|
||||||
|
tb.AddHttpClientInstrumentation();
|
||||||
|
tb.AddOtlpExporter(o =>
|
||||||
|
{
|
||||||
|
o.Protocol = OtlpExportProtocol.HttpProtobuf;
|
||||||
|
o.Endpoint = new Uri(seq.TraceEndpoint);
|
||||||
|
o.Headers = seq.AuthHeader;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
namespace StevesBot.Worker.Telemetry;
|
||||||
|
|
||||||
|
internal sealed class Instrumentation : IDisposable
|
||||||
|
{
|
||||||
|
public const string SourceName = "StevesBot.Worker";
|
||||||
|
public const string SourceVersion = "0.0.0";
|
||||||
|
public readonly ActivitySource Source = new(SourceName, SourceVersion);
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Source.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace StevesBot.Worker.Telemetry;
|
||||||
|
|
||||||
|
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}";
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
global using System.Diagnostics;
|
||||||
global using System.Net.Http.Json;
|
global using System.Net.Http.Json;
|
||||||
global using System.Net.WebSockets;
|
global using System.Net.WebSockets;
|
||||||
global using System.Reflection;
|
global using System.Reflection;
|
||||||
@@ -7,6 +8,11 @@ global using System.Text.Json.Serialization;
|
|||||||
|
|
||||||
global using Microsoft.Extensions.Options;
|
global using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
global using OpenTelemetry.Exporter;
|
||||||
|
global using OpenTelemetry.Logs;
|
||||||
|
global using OpenTelemetry.Resources;
|
||||||
|
global using OpenTelemetry.Trace;
|
||||||
|
|
||||||
global using StevesBot.Worker;
|
global using StevesBot.Worker;
|
||||||
global using StevesBot.Worker.Discord;
|
global using StevesBot.Worker.Discord;
|
||||||
global using StevesBot.Worker.Discord.Gateway;
|
global using StevesBot.Worker.Discord.Gateway;
|
||||||
@@ -17,5 +23,6 @@ global using StevesBot.Worker.Discord.Rest.Requests;
|
|||||||
global using StevesBot.Worker.Discord.Rest.Responses;
|
global using StevesBot.Worker.Discord.Rest.Responses;
|
||||||
global using StevesBot.Worker.Discord.Shared;
|
global using StevesBot.Worker.Discord.Shared;
|
||||||
global using StevesBot.Worker.Handlers;
|
global using StevesBot.Worker.Handlers;
|
||||||
|
global using StevesBot.Worker.Telemetry;
|
||||||
global using StevesBot.Worker.Threading;
|
global using StevesBot.Worker.Threading;
|
||||||
global using StevesBot.Worker.WebSockets;
|
global using StevesBot.Worker.WebSockets;
|
||||||
@@ -9,5 +9,10 @@
|
|||||||
"ApiUrl": "ApiUrl",
|
"ApiUrl": "ApiUrl",
|
||||||
"AppToken": "AppToken",
|
"AppToken": "AppToken",
|
||||||
"Intents": 0
|
"Intents": 0
|
||||||
|
},
|
||||||
|
"SeqOptions": {
|
||||||
|
"ServerUrl": "ServerUrl",
|
||||||
|
"ApiKeyHeader": "ApiKeyHeader",
|
||||||
|
"ApiKey": "ApiKey"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user