Skip to content

Commit 222bd83

Browse files
committed
feat(otel): add decorators for instrumentation
1 parent e2d5c03 commit 222bd83

30 files changed

+1266
-254
lines changed

compose-stack.yaml

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,61 @@
11
services:
2+
grafana:
3+
image: grafana/grafana:latest
4+
container_name: grafana
5+
ports:
6+
- "3000:3000"
7+
volumes:
8+
- ./grafana/provisioning:/etc/grafana/provisioning
9+
environment:
10+
- GF_AUTH_ANONYMOUS_ENABLED=true
11+
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
12+
- GF_FEATURE_TOGGLES_ENABLE=alertingSimplifiedRouting,alertingQueryAndExpressionsStepMode
13+
depends_on:
14+
- prometheus
15+
- loki
16+
- jaeger
17+
18+
loki:
19+
image: grafana/loki:latest
20+
container_name: loki
21+
ports:
22+
- "3100:3100"
23+
command: -config.file=/etc/loki/local-config.yml
24+
volumes:
25+
- ./loki-config.yml:/etc/loki/local-config.yml
26+
227
jaeger:
328
image: jaegertracing/all-in-one:latest
429
ports:
5-
- "5775:5775/udp"
6-
- "6831:6831/udp"
7-
- "6832:6832/udp"
8-
- "5778:5778"
930
- "16686:16686"
1031
- "14268:14268"
1132
- "14250:14250"
12-
- "9411:9411"
13-
- "4317:4317"
1433
environment:
1534
COLLECTOR_ZIPKIN_HOST_PORT: :9411
1635
COLLECTOR_OTLP_ENABLED: true
1736

37+
prometheus:
38+
image: prom/prometheus
39+
volumes:
40+
- ./prometheus.yml:/etc/prometheus/prometheus.yml
41+
command:
42+
- "--config.file=/etc/prometheus/prometheus.yml"
43+
ports:
44+
- "9090:9090"
45+
46+
otel-collector:
47+
image: otel/opentelemetry-collector-contrib:latest
48+
command: [ "--config=/etc/otel-collector-config.yaml" ]
49+
volumes:
50+
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
51+
ports:
52+
- "4317:4317"
53+
- "4318:4318"
54+
- "55679:55679"
55+
depends_on:
56+
- jaeger
57+
- prometheus
58+
1859
user-management-postgres:
1960
image: postgres:16.0
2061
ports:

loki-config.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
auth_enabled: false
2+
3+
server:
4+
http_listen_port: 3100
5+
grpc_listen_port: 9096
6+
7+
common:
8+
instance_addr: 127.0.0.1
9+
path_prefix: /tmp/loki
10+
storage:
11+
filesystem:
12+
chunks_directory: /tmp/loki/chunks
13+
rules_directory: /tmp/loki/rules
14+
replication_factor: 1
15+
ring:
16+
kvstore:
17+
store: inmemory
18+
19+
query_range:
20+
results_cache:
21+
cache:
22+
embedded_cache:
23+
enabled: true
24+
max_size_mb: 100
25+
26+
limits_config:
27+
allow_structured_metadata: true
28+
29+
schema_config:
30+
configs:
31+
- from: 2020-05-15
32+
store: tsdb
33+
object_store: filesystem
34+
schema: v13
35+
index:
36+
prefix: index_
37+
period: 24h
38+
39+
storage_config:
40+
filesystem:
41+
directory: /tmp/loki/chunks

otel-collector-config.yaml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
receivers:
2+
otlp:
3+
protocols:
4+
grpc:
5+
endpoint: 0.0.0.0:4317
6+
7+
prometheus/own_metrics:
8+
config:
9+
scrape_configs:
10+
- job_name: otel-collector
11+
scrape_interval: 10s
12+
static_configs:
13+
- targets: [ "0.0.0.0:8888" ]
14+
15+
exporters:
16+
prometheus:
17+
endpoint: "0.0.0.0:9090"
18+
otlp:
19+
endpoint: "jaeger:4317"
20+
tls:
21+
insecure: true
22+
otlphttp:
23+
endpoint: http://loki:3100/otlp
24+
debug:
25+
verbosity: detailed
26+
processors:
27+
batch:
28+
29+
extensions:
30+
zpages:
31+
32+
service:
33+
extensions: [zpages]
34+
pipelines:
35+
metrics:
36+
receivers: [otlp,prometheus/own_metrics]
37+
processors: [batch]
38+
exporters: [prometheus]
39+
traces:
40+
receivers: [otlp]
41+
processors: [batch]
42+
exporters: [otlp]
43+
logs:
44+
receivers: [otlp]
45+
exporters: [otlphttp, debug]

prometheus.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
scrape_configs:
2+
- job_name: 'otel-collector'
3+
static_configs:
4+
- targets: ['otel-collector:9090']

src/user-management/UserManagement.sln.DotSettings.user

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003A02000010pdb26High_002Ecs_002Fl_003AC_0021_003FUsers_003Fnikit_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003FILViewer_003F63faa70f849b4a7f9f003c61a4d6e91f14800_003F2a_003F67f88103_003F02000010pdb26High_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
33
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003A0200007Apdb110_002Eil_002Fl_003AC_0021_003FUsers_003Fnikit_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003FILViewer_003F2bcb52e7367c4fd489df852b54f818cedf800_003F9e_003Ff0f82243_003F0200007Apdb110_002Eil/@EntryIndexedValue">ForceIncluded</s:String>
44
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AActionMethodExecutor_002Ecs_002Fl_003AC_0021_003FUsers_003Fnikit_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F294078ecfce6fdb942ecfee089f09717de7a6fcfe5efd9fdb6f4f93c0fb4813_003FActionMethodExecutor_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
5+
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AActivitySource_002Ecs_002Fl_003AC_0021_003FUsers_003Fnikit_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F32c4c2f7b91a42c5a86e2fcbbb58aed26e930_003Fcf_003Ff1140224_003FActivitySource_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
56
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACancellationToken_002Ecs_002Fl_003AC_0021_003FUsers_003Fnikit_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F2565b9d99fdde488bc7801b84387b2cc864959cfb63212e1ff576fc9c6bb7e_003FCancellationToken_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
67
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADbConnectionOptions_002Ecs_002Fl_003AC_0021_003FUsers_003Fnikit_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F52e8b2de01764bb6bb45a8821b2a668c2b7908_003F69_003Fadab4036_003FDbConnectionOptions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
78
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnvironmentVariablesAWSCredentials_002Ecs_002Fl_003AC_0021_003FUsers_003Fnikit_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F9ae370fa3703991d2115892566732d5c46ff250a96d6de3123a5ce27b2b42_003FEnvironmentVariablesAWSCredentials_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>

src/user-management/UserManagement/Application/Extensions/ConfigurationBuilderExtensions.cs

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -111,24 +111,6 @@ public static IConfigurationBuilder AddJwtBearer(this IConfigurationBuilder conf
111111
}
112112

113113
await Task.CompletedTask;
114-
},
115-
OnChallenge = async context =>
116-
{
117-
if (!context.Response.HasStarted)
118-
{
119-
context.Response.ContentType = "application/json";
120-
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
121-
122-
string errorMessage = context.AuthenticateFailure?.Message switch
123-
{
124-
"Email not verified." => "Email has not been verified. Please verify your email to continue.",
125-
_ => "Invalid token."
126-
};
127-
128-
await context.Response.WriteAsync($"{{\"error\": \"{errorMessage}\"}}");
129-
}
130-
131-
context.HandleResponse();
132114
}
133115
};
134116
});

src/user-management/UserManagement/Application/Extensions/ServiceCollectionExtensions.cs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
using System.Collections.Concurrent;
22
using System.Reflection;
33
using Asp.Versioning;
4+
using OpenTelemetry.Metrics;
5+
using OpenTelemetry.Resources;
6+
using OpenTelemetry.Trace;
47
using RabbitMQ.Client;
58
using StackExchange.Redis;
69
using UserManagement.Application.Builders;
710
using UserManagement.Application.Factories;
811
using UserManagement.Application.Factories.Abstractions;
12+
using UserManagement.Application.Helpers;
13+
using UserManagement.Application.Services;
914
using UserManagement.Application.Services.Abstractions;
1015
using UserManagement.Application.Settings;
1116
using UserManagement.HostedServices;
@@ -14,6 +19,35 @@ namespace UserManagement.Application.Extensions;
1419

1520
internal static class ServiceCollectionExtensions
1621
{
22+
public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder)
23+
{
24+
builder.Services.AddOpenTelemetry()
25+
.ConfigureResource(resourceBuilder => resourceBuilder.AddTelemetrySdk().AddService(serviceName: "user-management", serviceVersion: "1.0.0"))
26+
.WithMetrics(providerBuilder =>
27+
{
28+
providerBuilder.AddRuntimeInstrumentation()
29+
.AddAspNetCoreInstrumentation()
30+
.AddHttpClientInstrumentation()
31+
.AddMeter("Microsoft.AspNetCore.Hosting", "Microsoft.AspNetCore.Server.Kestrel", "System.Net.Http", Instrumentation.MeterName)
32+
.AddView("request-duration",
33+
new ExplicitBucketHistogramConfiguration
34+
{
35+
Boundaries = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0]
36+
})
37+
.AddOtlpExporter();
38+
})
39+
.WithTracing(providerBuilder =>
40+
{
41+
providerBuilder.AddAspNetCoreInstrumentation()
42+
.AddHttpClientInstrumentation()
43+
.AddAWSInstrumentation()
44+
.AddOtlpExporter();
45+
});
46+
47+
builder.Services.AddSingleton<Instrumentation>();
48+
return builder;
49+
}
50+
1751
public static IServiceCollection AddOptionsSettingsWithValidation<TOptions>(
1852
this IServiceCollection services,
1953
IConfiguration configuration)
@@ -38,7 +72,7 @@ public static IServiceCollection AddApplicationSettings(
3872
var allSettings = Assembly.GetAssembly(baseSettingsType)!
3973
.ExportedTypes
4074
.Where(t => t is { IsInterface: false, IsAbstract: false } && t.BaseType == baseSettingsType);
41-
75+
4276
foreach (var settings in allSettings)
4377
{
4478
var method = typeof(ServiceCollectionExtensions)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using System.Collections.Concurrent;
2+
using System.Diagnostics;
3+
using System.Diagnostics.Metrics;
4+
5+
namespace UserManagement.Application.Helpers;
6+
7+
public sealed class Instrumentation : IDisposable
8+
{
9+
internal const string ActivitySourceName = "user-management";
10+
internal const string ActivitySourceVersion = "1.0.0";
11+
internal const string MeterName = "UserManagementAPI";
12+
13+
public Instrumentation(IMeterFactory factory, ActivitySource activitySource)
14+
{
15+
ActivitySource = activitySource;
16+
Meter = factory.Create(MeterName);
17+
18+
Counters = new ConcurrentDictionary<string, Counter<long>>();
19+
Histograms = new ConcurrentDictionary<string, Histogram<double>>();
20+
}
21+
22+
internal ActivitySource ActivitySource { get; }
23+
internal Meter Meter { get; }
24+
25+
private ConcurrentDictionary<string, Counter<long>> Counters { get; }
26+
private ConcurrentDictionary<string, Histogram<double>> Histograms { get; }
27+
28+
public void Dispose()
29+
{
30+
ActivitySource.Dispose();
31+
Meter.Dispose();
32+
}
33+
34+
public Counter<long> GetCounterOrCreate(string name, string? description = null)
35+
{
36+
return Counters.GetOrAdd(name, Meter.CreateCounter<long>(name, description: description));
37+
}
38+
39+
public Histogram<double> GetHistogramOrCreate(string name, string? description = null)
40+
{
41+
return Histograms.GetOrAdd(name, Meter.CreateHistogram<double>(name, description: description));
42+
}
43+
}

0 commit comments

Comments
 (0)