Skip to content

Commit 58043ae

Browse files
committed
Merged otel-metrics
2 parents e994528 + 0d2ef57 commit 58043ae

File tree

14 files changed

+91
-125
lines changed

14 files changed

+91
-125
lines changed

.github/workflows/build-windows.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
with:
1616
fetch-depth: 0
1717
- name: Setup .NET SDK
18-
uses: actions/setup-dotnet@v4.2.0
18+
uses: actions/setup-dotnet@v4.3.0
1919
with:
2020
dotnet-version: 8.0.x
2121
- name: Download RavenDB Server

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
with:
3737
fetch-depth: 0
3838
- name: Setup .NET SDK
39-
uses: actions/setup-dotnet@v4.2.0
39+
uses: actions/setup-dotnet@v4.3.0
4040
with:
4141
dotnet-version: 8.0.x
4242
- name: Download RavenDB Server

src/Directory.Packages.props

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<PackageVersion Include="AWSSDK.CloudWatch" Version="3.7.402.21" />
99
<PackageVersion Include="AWSSDK.SecurityToken" Version="3.7.401.22" />
1010
<PackageVersion Include="Azure.Identity" Version="1.13.1" />
11+
<PackageVersion Include="Azure.Monitor.OpenTelemetry.Exporter" Version="1.3.0" />
1112
<PackageVersion Include="Azure.Monitor.Query" Version="1.6.0" />
1213
<PackageVersion Include="Azure.ResourceManager.ServiceBus" Version="1.0.1" />
1314
<PackageVersion Include="ByteSize" Version="2.1.2" />
@@ -50,6 +51,9 @@
5051
<PackageVersion Include="NUnit" Version="4.3.2" />
5152
<PackageVersion Include="NUnit.Analyzers" Version="4.6.0" />
5253
<PackageVersion Include="NUnit3TestAdapter" Version="4.6.0" />
54+
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.9.0" />
55+
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
56+
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
5357
<PackageVersion Include="Particular.Approvals" Version="2.0.1" />
5458
<PackageVersion Include="Particular.Licensing.Sources" Version="6.0.0" />
5559
<PackageVersion Include="Particular.LicensingComponent.Report" Version="1.0.0" />
@@ -87,4 +91,4 @@
8791
<GlobalPackageReference Include="Microsoft.Build.CopyOnWrite" Version="1.0.334" />
8892
<GlobalPackageReference Include="Particular.Packaging" Version="4.2.0" />
8993
</ItemGroup>
90-
</Project>
94+
</Project>

src/ServiceControl.Audit.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ async Task InitializeServiceControl(ScenarioContext context)
5151
TransportConnectionString = transportToUse.ConnectionString,
5252
MaximumConcurrencyLevel = 2,
5353
ServiceControlQueueAddress = "SHOULDNOTBEUSED",
54+
OtelMetricsUrl = "http://localhost:4317",
5455
MessageFilter = messageContext =>
5556
{
5657
var id = messageContext.NativeMessageId;

src/ServiceControl.Audit/App.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ These settings are only here so that we can debug ServiceControl while developin
99
<add key="ServiceControl.Audit/HostName" value="localhost" />
1010
<add key="ServiceControl.Audit/DatabaseMaintenancePort" value="44445" />
1111
<add key="ServiceControl.Audit/LogLevel" value="Debug" />
12-
<add key="ServiceControl.Audit/PrintMetrics" value="True" />
12+
<add key="ServiceControl.Audit/OtelMetricsUrl" value="http://localhost:4317" />
1313

1414
<!-- DEVS - Pick a transport to run Auditing instance on -->
1515
<add key="ServiceControl.Audit/TransportType" value="LearningTransport" />

src/ServiceControl.Audit/Auditing/AuditIngestion.cs

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System;
44
using System.Collections.Generic;
55
using System.Diagnostics;
6+
using System.Diagnostics.Metrics;
67
using System.Threading;
78
using System.Threading.Channels;
89
using System.Threading.Tasks;
@@ -14,18 +15,14 @@
1415
using Persistence;
1516
using Persistence.UnitOfWork;
1617
using ServiceControl.Infrastructure;
17-
using ServiceControl.Infrastructure.Metrics;
1818
using Transports;
1919

2020
class AuditIngestion : IHostedService
2121
{
22-
static readonly long FrequencyInMilliseconds = Stopwatch.Frequency / 1000;
23-
2422
public AuditIngestion(
2523
Settings settings,
2624
ITransportCustomization transportCustomization,
2725
TransportSettings transportSettings,
28-
Metrics metrics,
2926
IFailedAuditStorage failedImportsStorage,
3027
AuditIngestionCustomCheck.State ingestionState,
3128
AuditIngestor auditIngestor,
@@ -40,10 +37,6 @@ public AuditIngestion(
4037
this.settings = settings;
4138
this.applicationLifetime = applicationLifetime;
4239

43-
batchSizeMeter = metrics.GetMeter("Audit ingestion - batch size");
44-
batchDurationMeter = metrics.GetMeter("Audit ingestion - batch processing duration", FrequencyInMilliseconds);
45-
receivedMeter = metrics.GetCounter("Audit ingestion - received");
46-
4740
if (!transportSettings.MaxConcurrency.HasValue)
4841
{
4942
throw new ArgumentException("MaxConcurrency is not set in TransportSettings");
@@ -102,7 +95,6 @@ async Task EnsureStarted(CancellationToken cancellationToken = default)
10295
await stoppable.StopReceive(cancellationToken);
10396
logger.Info("Shutting down due to failed persistence health check. Infrastructure shut down completed");
10497
}
105-
10698
return;
10799
}
108100

@@ -199,7 +191,7 @@ async Task OnMessage(MessageContext messageContext, CancellationToken cancellati
199191
var taskCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
200192
messageContext.SetTaskCompletionSource(taskCompletionSource);
201193

202-
receivedMeter.Mark();
194+
receivedMeter.Add(1);
203195

204196
await channel.Writer.WriteAsync(messageContext, cancellationToken);
205197
await taskCompletionSource.Task;
@@ -222,11 +214,11 @@ async Task Loop()
222214
contexts.Add(context);
223215
}
224216

225-
batchSizeMeter.Mark(contexts.Count);
226-
using (batchDurationMeter.Measure())
227-
{
228-
await auditIngestor.Ingest(contexts);
229-
}
217+
batchSizeMeter.Record(contexts.Count);
218+
var sw = Stopwatch.StartNew();
219+
220+
await auditIngestor.Ingest(contexts);
221+
batchDurationMeter.Record(sw.ElapsedMilliseconds);
230222
}
231223
catch (OperationCanceledException e)
232224
{
@@ -274,9 +266,9 @@ async Task Loop()
274266
readonly IAuditIngestionUnitOfWorkFactory unitOfWorkFactory;
275267
readonly Settings settings;
276268
readonly Channel<MessageContext> channel;
277-
readonly Meter batchSizeMeter;
278-
readonly Meter batchDurationMeter;
279-
readonly Counter receivedMeter;
269+
readonly Histogram<long> batchSizeMeter = AuditMetrics.Meter.CreateHistogram<long>($"{AuditMetrics.Prefix}.batch_size");
270+
readonly Histogram<double> batchDurationMeter = AuditMetrics.Meter.CreateHistogram<double>($"{AuditMetrics.Prefix}.batch_duration_ms");
271+
readonly Counter<long> receivedMeter = AuditMetrics.Meter.CreateCounter<long>($"{AuditMetrics.Prefix}.received");
280272
readonly Watchdog watchdog;
281273
readonly Task ingestionWorker;
282274
readonly IHostApplicationLifetime applicationLifetime;

src/ServiceControl.Audit/Auditing/AuditIngestor.cs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,11 @@
1414
using Persistence.UnitOfWork;
1515
using Recoverability;
1616
using SagaAudit;
17-
using ServiceControl.Infrastructure.Metrics;
1817
using ServiceControl.Transports;
1918

2019
public class AuditIngestor
2120
{
2221
public AuditIngestor(
23-
Metrics metrics,
2422
Settings settings,
2523
IAuditIngestionUnitOfWorkFactory unitOfWorkFactory,
2624
EndpointInstanceMonitoring endpointInstanceMonitoring,
@@ -32,13 +30,6 @@ ITransportCustomization transportCustomization
3230
{
3331
this.settings = settings;
3432
this.messageDispatcher = messageDispatcher;
35-
36-
var ingestedAuditMeter = metrics.GetCounter("Audit ingestion - ingested audit");
37-
var ingestedSagaAuditMeter = metrics.GetCounter("Audit ingestion - ingested saga audit");
38-
var auditBulkInsertDurationMeter = metrics.GetMeter("Audit ingestion - audit bulk insert duration", FrequencyInMilliseconds);
39-
var sagaAuditBulkInsertDurationMeter = metrics.GetMeter("Audit ingestion - saga audit bulk insert duration", FrequencyInMilliseconds);
40-
var bulkInsertCommitDurationMeter = metrics.GetMeter("Audit ingestion - bulk insert commit duration", FrequencyInMilliseconds);
41-
4233
var enrichers = new IEnrichImportedAuditMessages[]
4334
{
4435
new MessageTypeEnricher(),
@@ -51,7 +42,7 @@ ITransportCustomization transportCustomization
5142

5243
logQueueAddress = transportCustomization.ToTransportQualifiedQueueName(settings.AuditLogQueue);
5344

54-
auditPersister = new AuditPersister(unitOfWorkFactory, enrichers, ingestedAuditMeter, ingestedSagaAuditMeter, auditBulkInsertDurationMeter, sagaAuditBulkInsertDurationMeter, bulkInsertCommitDurationMeter, messageSession, messageDispatcher);
45+
auditPersister = new AuditPersister(unitOfWorkFactory, enrichers, messageSession, messageDispatcher);
5546
}
5647

5748
public async Task Ingest(List<MessageContext> contexts)
@@ -159,7 +150,6 @@ public async Task VerifyCanReachForwardingAddress()
159150
readonly Lazy<IMessageDispatcher> messageDispatcher;
160151
readonly string logQueueAddress;
161152

162-
static readonly long FrequencyInMilliseconds = Stopwatch.Frequency / 1000;
163153
static readonly ILog Log = LogManager.GetLogger<AuditIngestor>();
164154
}
165155
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace ServiceControl.Audit.Auditing;
2+
3+
using System.Diagnostics.Metrics;
4+
5+
static class AuditMetrics
6+
{
7+
public static readonly Meter Meter = new("ServiceControl", "0.1.0");
8+
public static readonly string Prefix = "particular.servicecontrol.audit";
9+
}

src/ServiceControl.Audit/Auditing/AuditPersister.cs

Lines changed: 27 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System;
44
using System.Collections.Generic;
55
using System.Diagnostics;
6+
using System.Diagnostics.Metrics;
67
using System.Text.Json;
78
using System.Threading.Tasks;
89
using Infrastructure;
@@ -15,36 +16,20 @@
1516
using ServiceControl.Audit.Persistence.Monitoring;
1617
using ServiceControl.EndpointPlugin.Messages.SagaState;
1718
using ServiceControl.Infrastructure;
18-
using ServiceControl.Infrastructure.Metrics;
1919
using ServiceControl.SagaAudit;
2020

21-
class AuditPersister
21+
class AuditPersister(IAuditIngestionUnitOfWorkFactory unitOfWorkFactory,
22+
IEnrichImportedAuditMessages[] enrichers,
23+
IMessageSession messageSession,
24+
Lazy<IMessageDispatcher> messageDispatcher)
2225
{
23-
public AuditPersister(IAuditIngestionUnitOfWorkFactory unitOfWorkFactory,
24-
IEnrichImportedAuditMessages[] enrichers,
25-
Counter ingestedAuditMeter, Counter ingestedSagaAuditMeter, Meter auditBulkInsertDurationMeter,
26-
Meter sagaAuditBulkInsertDurationMeter, Meter bulkInsertCommitDurationMeter, IMessageSession messageSession,
27-
Lazy<IMessageDispatcher> messageDispatcher)
28-
{
29-
this.unitOfWorkFactory = unitOfWorkFactory;
30-
this.enrichers = enrichers;
31-
32-
this.ingestedAuditMeter = ingestedAuditMeter;
33-
this.ingestedSagaAuditMeter = ingestedSagaAuditMeter;
34-
this.auditBulkInsertDurationMeter = auditBulkInsertDurationMeter;
35-
this.sagaAuditBulkInsertDurationMeter = sagaAuditBulkInsertDurationMeter;
36-
this.bulkInsertCommitDurationMeter = bulkInsertCommitDurationMeter;
37-
this.messageSession = messageSession;
38-
this.messageDispatcher = messageDispatcher;
39-
}
40-
4126
public async Task<IReadOnlyList<MessageContext>> Persist(IReadOnlyList<MessageContext> contexts)
4227
{
4328
var stopwatch = Stopwatch.StartNew();
4429

4530
if (Logger.IsDebugEnabled)
4631
{
47-
Logger.DebugFormat("Batch size {0}", contexts.Count);
32+
Logger.Debug($"Batch size {contexts.Count}");
4833
}
4934

5035
var storedContexts = new List<MessageContext>(contexts.Count);
@@ -86,23 +71,25 @@ public async Task<IReadOnlyList<MessageContext>> Persist(IReadOnlyList<MessageCo
8671

8772
Logger.Debug("Adding audit message for bulk storage");
8873

89-
using (auditBulkInsertDurationMeter.Measure())
90-
{
91-
await unitOfWork.RecordProcessedMessage(processedMessage, context.Body);
92-
}
74+
var auditSw = Stopwatch.StartNew();
75+
await unitOfWork.RecordProcessedMessage(processedMessage, context.Body);
76+
auditSw.Stop();
77+
78+
auditBulkInsertDurationMeter.Record(auditSw.ElapsedMilliseconds);
9379

94-
ingestedAuditMeter.Mark();
80+
ingestedAuditMeter.Add(1);
9581
}
9682
else if (context.Extensions.TryGet(out SagaSnapshot sagaSnapshot))
9783
{
9884
Logger.Debug("Adding SagaSnapshot message for bulk storage");
9985

100-
using (sagaAuditBulkInsertDurationMeter.Measure())
101-
{
102-
await unitOfWork.RecordSagaSnapshot(sagaSnapshot);
103-
}
86+
var sagaSw = Stopwatch.StartNew();
87+
await unitOfWork.RecordSagaSnapshot(sagaSnapshot);
88+
sagaSw.Stop();
89+
90+
sagaAuditBulkInsertDurationMeter.Record(sagaSw.ElapsedMilliseconds);
10491

105-
ingestedSagaAuditMeter.Mark();
92+
ingestedSagaAuditMeter.Add(1);
10693
}
10794

10895
storedContexts.Add(context);
@@ -131,10 +118,10 @@ public async Task<IReadOnlyList<MessageContext>> Persist(IReadOnlyList<MessageCo
131118
try
132119
{
133120
// this can throw even though dispose is never supposed to throw
134-
using (bulkInsertCommitDurationMeter.Measure())
135-
{
136-
await unitOfWork.DisposeAsync();
137-
}
121+
var commitSw = Stopwatch.StartNew();
122+
await unitOfWork.DisposeAsync();
123+
commitSw.Stop();
124+
bulkInsertCommitDurationMeter.Record(commitSw.ElapsedMilliseconds);
138125
}
139126
catch (Exception e)
140127
{
@@ -284,16 +271,12 @@ await messageDispatcher.Value.Dispatch(new TransportOperations(messagesToEmit.To
284271
}
285272
}
286273

287-
readonly Counter ingestedAuditMeter;
288-
readonly Counter ingestedSagaAuditMeter;
289-
readonly Meter auditBulkInsertDurationMeter;
290-
readonly Meter sagaAuditBulkInsertDurationMeter;
291-
readonly Meter bulkInsertCommitDurationMeter;
292-
readonly IMessageSession messageSession;
293-
readonly Lazy<IMessageDispatcher> messageDispatcher;
274+
readonly Counter<long> ingestedAuditMeter = AuditMetrics.Meter.CreateCounter<long>($"{AuditMetrics.Prefix}.ingested_audit_messages"); // metrics.GetCounter("Audit ingestion - ingested audit");
275+
readonly Counter<long> ingestedSagaAuditMeter = AuditMetrics.Meter.CreateCounter<long>($"{AuditMetrics.Prefix}.ingested_saga_audits"); // metrics.GetCounter("Audit ingestion - ingested audit");
276+
readonly Histogram<double> auditBulkInsertDurationMeter = AuditMetrics.Meter.CreateHistogram<double>($"{AuditMetrics.Prefix}.audit_bulk_insert_duration_ms"); // metrics.GetCounter("Audit ingestion - ingested audit");
277+
readonly Histogram<double> sagaAuditBulkInsertDurationMeter = AuditMetrics.Meter.CreateHistogram<double>($"{AuditMetrics.Prefix}.saga_bulk_insert_duration_ms"); // metrics.GetCounter("Audit ingestion - ingested audit");
278+
readonly Histogram<double> bulkInsertCommitDurationMeter = AuditMetrics.Meter.CreateHistogram<double>($"{AuditMetrics.Prefix}.audit_commit_duration_ms"); // metrics.GetCounter("Audit ingestion - ingested audit");
294279

295-
readonly IEnrichImportedAuditMessages[] enrichers;
296-
readonly IAuditIngestionUnitOfWorkFactory unitOfWorkFactory;
297280
static readonly ILog Logger = LogManager.GetLogger<AuditPersister>();
298281
}
299282
}

src/ServiceControl.Audit/HostApplicationBuilderExtensions.cs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ namespace ServiceControl.Audit;
55
using System.Threading;
66
using System.Threading.Tasks;
77
using Auditing;
8+
using Azure.Monitor.OpenTelemetry.Exporter;
89
using Infrastructure;
9-
using Infrastructure.Metrics;
1010
using Infrastructure.Settings;
1111
using Microsoft.AspNetCore.HttpLogging;
1212
using Microsoft.Extensions.DependencyInjection;
@@ -20,6 +20,8 @@ namespace ServiceControl.Audit;
2020
using NServiceBus.Transport;
2121
using Persistence;
2222
using Transports;
23+
using OpenTelemetry.Metrics;
24+
using OpenTelemetry.Resources;
2325

2426
static class HostApplicationBuilderExtensions
2527
{
@@ -61,13 +63,38 @@ public static void AddServiceControlAudit(this IHostApplicationBuilder builder,
6163
// directly and to make things more complex of course the order of registration still matters ;)
6264
services.AddSingleton(provider => new Lazy<IMessageDispatcher>(provider.GetRequiredService<IMessageDispatcher>));
6365

64-
services.AddMetrics(settings.PrintMetrics);
65-
6666
services.AddPersistence(persistenceSettings, persistenceConfiguration);
6767

6868
NServiceBusFactory.Configure(settings, transportCustomization, transportSettings, onCriticalError, configuration);
6969
builder.UseNServiceBus(configuration);
7070

71+
if (!string.IsNullOrEmpty(settings.OtelMetricsUrl))
72+
{
73+
builder.Services.AddOpenTelemetry()
74+
.ConfigureResource(b => b.AddService(serviceName: settings.InstanceName))
75+
.WithMetrics(b =>
76+
{
77+
b.AddMeter("ServiceControl");
78+
79+
if (Uri.TryCreate(settings.OtelMetricsUrl, UriKind.Absolute, out var uri))
80+
{
81+
b.AddOtlpExporter(e =>
82+
{
83+
e.Endpoint = uri;
84+
});
85+
}
86+
else
87+
{
88+
b.AddAzureMonitorMetricExporter(o =>
89+
{
90+
o.ConnectionString = settings.OtelMetricsUrl;
91+
});
92+
}
93+
94+
b.AddConsoleExporter();
95+
});
96+
}
97+
7198
// Configure after the NServiceBus hosted service to ensure NServiceBus is already started
7299
if (settings.IngestAuditMessages)
73100
{

0 commit comments

Comments
 (0)