diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index 0a0b8b0e83..ab58c4a2f1 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -50,6 +50,8 @@
+
+
diff --git a/src/ServiceControl.Audit.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs b/src/ServiceControl.Audit.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs
index 35990ee062..0866b504db 100644
--- a/src/ServiceControl.Audit.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs
+++ b/src/ServiceControl.Audit.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs
@@ -55,7 +55,6 @@ async Task InitializeServiceControl(ScenarioContext context)
{
var id = messageContext.NativeMessageId;
var headers = messageContext.Headers;
-
var log = NServiceBus.Logging.LogManager.GetLogger();
headers.TryGetValue(Headers.MessageId, out var originalMessageId);
log.Debug($"OnMessage for message '{id}'({originalMessageId ?? string.Empty}).");
diff --git a/src/ServiceControl.Audit.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt b/src/ServiceControl.Audit.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt
index c1dbbb4ec1..77b2ef7666 100644
--- a/src/ServiceControl.Audit.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt
+++ b/src/ServiceControl.Audit.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt
@@ -12,6 +12,7 @@
"ApiUrl": "http://localhost:8888/api",
"Port": 8888,
"PrintMetrics": false,
+ "OtlpEndpointUrl": null,
"Hostname": "localhost",
"VirtualDirectory": "",
"TransportType": "LearningTransport",
diff --git a/src/ServiceControl.Audit/App.config b/src/ServiceControl.Audit/App.config
index 83610fa6ee..00a70ad0a2 100644
--- a/src/ServiceControl.Audit/App.config
+++ b/src/ServiceControl.Audit/App.config
@@ -8,7 +8,6 @@ These settings are only here so that we can debug ServiceControl while developin
-
diff --git a/src/ServiceControl.Audit/Auditing/AuditIngestion.cs b/src/ServiceControl.Audit/Auditing/AuditIngestion.cs
index 56d4244e84..4753c498b0 100644
--- a/src/ServiceControl.Audit/Auditing/AuditIngestion.cs
+++ b/src/ServiceControl.Audit/Auditing/AuditIngestion.cs
@@ -2,7 +2,7 @@
{
using System;
using System.Collections.Generic;
- using System.Diagnostics;
+ using System.Diagnostics.Metrics;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
@@ -14,18 +14,14 @@
using Persistence;
using Persistence.UnitOfWork;
using ServiceControl.Infrastructure;
- using ServiceControl.Infrastructure.Metrics;
using Transports;
class AuditIngestion : IHostedService
{
- static readonly long FrequencyInMilliseconds = Stopwatch.Frequency / 1000;
-
public AuditIngestion(
Settings settings,
ITransportCustomization transportCustomization,
TransportSettings transportSettings,
- Metrics metrics,
IFailedAuditStorage failedImportsStorage,
AuditIngestionCustomCheck.State ingestionState,
AuditIngestor auditIngestor,
@@ -40,10 +36,6 @@ public AuditIngestion(
this.settings = settings;
this.applicationLifetime = applicationLifetime;
- batchSizeMeter = metrics.GetMeter("Audit ingestion - batch size");
- batchDurationMeter = metrics.GetMeter("Audit ingestion - batch processing duration", FrequencyInMilliseconds);
- receivedMeter = metrics.GetCounter("Audit ingestion - received");
-
if (!transportSettings.MaxConcurrency.HasValue)
{
throw new ArgumentException("MaxConcurrency is not set in TransportSettings");
@@ -102,6 +94,7 @@ async Task EnsureStarted(CancellationToken cancellationToken = default)
await stoppable.StopReceive(cancellationToken);
logger.Info("Shutting down due to failed persistence health check. Infrastructure shut down completed");
}
+
return;
}
@@ -168,6 +161,7 @@ async Task EnsureStopped(CancellationToken cancellationToken = default)
logger.Info("Shutting down. Already stopped, skipping shut down");
return; //Already stopped
}
+
var stoppable = queueIngestor;
queueIngestor = null;
logger.Info("Shutting down. Infrastructure shut down commencing");
@@ -188,18 +182,22 @@ async Task EnsureStopped(CancellationToken cancellationToken = default)
async Task OnMessage(MessageContext messageContext, CancellationToken cancellationToken)
{
- if (settings.MessageFilter != null && settings.MessageFilter(messageContext))
+ using (new DurationRecorder(ingestionDuration))
{
- return;
- }
+ if (settings.MessageFilter != null && settings.MessageFilter(messageContext))
+ {
+ return;
+ }
- var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- messageContext.SetTaskCompletionSource(taskCompletionSource);
+ var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+ messageContext.SetTaskCompletionSource(taskCompletionSource);
- receivedMeter.Mark();
+ await channel.Writer.WriteAsync(messageContext, cancellationToken);
+ await taskCompletionSource.Task;
- await channel.Writer.WriteAsync(messageContext, cancellationToken);
- await taskCompletionSource.Task;
+ ingestedMessagesCounter.Add(1);
+ messageSize.Record(messageContext.Body.Length / 1024.0);
+ }
}
async Task Loop()
@@ -217,11 +215,14 @@ async Task Loop()
contexts.Add(context);
}
- batchSizeMeter.Mark(contexts.Count);
- using (batchDurationMeter.Measure())
+ auditBatchSize.Record(contexts.Count);
+
+ using (new DurationRecorder(auditBatchDuration))
{
await auditIngestor.Ingest(contexts);
}
+
+ consecutiveBatchFailuresCounter.Record(0);
}
catch (OperationCanceledException)
{
@@ -240,6 +241,9 @@ async Task Loop()
{
context.GetTaskCompletionSource().TrySetException(e);
}
+
+ // no need to do interlocked increment since this is running sequential
+ consecutiveBatchFailuresCounter.Record(consecutiveBatchFailures++);
}
finally
{
@@ -251,8 +255,9 @@ async Task Loop()
TransportInfrastructure transportInfrastructure;
IMessageReceiver queueIngestor;
+ long consecutiveBatchFailures = 0;
- readonly SemaphoreSlim startStopSemaphore = new SemaphoreSlim(1);
+ readonly SemaphoreSlim startStopSemaphore = new(1);
readonly string inputEndpoint;
readonly ITransportCustomization transportCustomization;
readonly TransportSettings transportSettings;
@@ -261,9 +266,12 @@ async Task Loop()
readonly IAuditIngestionUnitOfWorkFactory unitOfWorkFactory;
readonly Settings settings;
readonly Channel channel;
- readonly Meter batchSizeMeter;
- readonly Meter batchDurationMeter;
- readonly Counter receivedMeter;
+ readonly Histogram auditBatchSize = Telemetry.Meter.CreateHistogram(Telemetry.CreateInstrumentName("ingestion", "batch_size"), description: "Audit ingestion average batch size");
+ readonly Histogram auditBatchDuration = Telemetry.Meter.CreateHistogram(Telemetry.CreateInstrumentName("ingestion", "batch_duration"), unit: "ms", "Average audit message batch processing duration");
+ readonly Histogram messageSize = Telemetry.Meter.CreateHistogram(Telemetry.CreateInstrumentName("ingestion", "message_size"), unit: "kilobytes", description: "Average audit message body size");
+ readonly Counter ingestedMessagesCounter = Telemetry.Meter.CreateCounter(Telemetry.CreateInstrumentName("ingestion", "count"), description: "Successful ingested audit message count");
+ readonly Histogram consecutiveBatchFailuresCounter = Telemetry.Meter.CreateHistogram(Telemetry.CreateInstrumentName("ingestion", "consecutive_batch_failures"), unit: "count", description: "Consecutive audit ingestion batch failure");
+ readonly Histogram ingestionDuration = Telemetry.Meter.CreateHistogram(Telemetry.CreateInstrumentName("ingestion", "duration"), unit: "ms", description: "Average incoming audit message processing duration");
readonly Watchdog watchdog;
readonly Task ingestionWorker;
readonly IHostApplicationLifetime applicationLifetime;
diff --git a/src/ServiceControl.Audit/Auditing/AuditIngestionFaultPolicy.cs b/src/ServiceControl.Audit/Auditing/AuditIngestionFaultPolicy.cs
index bd116678d4..d4d5d3b900 100644
--- a/src/ServiceControl.Audit/Auditing/AuditIngestionFaultPolicy.cs
+++ b/src/ServiceControl.Audit/Auditing/AuditIngestionFaultPolicy.cs
@@ -2,6 +2,7 @@
{
using System;
using System.Diagnostics;
+ using System.Diagnostics.Metrics;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
@@ -37,10 +38,13 @@ public async Task OnError(ErrorContext errorContext, Cancella
//Same as recoverability policy in NServiceBusFactory
if (errorContext.ImmediateProcessingFailures < 3)
{
+ retryCounter.Add(1);
return ErrorHandleResult.RetryRequired;
}
await StoreFailedMessageDocument(errorContext, cancellationToken);
+
+ failedCounter.Add(1);
return ErrorHandleResult.Handled;
}
@@ -100,6 +104,9 @@ void WriteToEventLog(string message)
EventLog.WriteEntry(EventSourceCreator.SourceName, message, EventLogEntryType.Error);
}
+ readonly Counter retryCounter = Telemetry.Meter.CreateCounter(Telemetry.CreateInstrumentName("ingestion", "retry"), description: "Audit ingestion retries count");
+ readonly Counter failedCounter = Telemetry.Meter.CreateCounter(Telemetry.CreateInstrumentName("ingestion", "failed"), description: "Audit ingestion failure count");
+
static readonly ILog log = LogManager.GetLogger();
}
}
\ No newline at end of file
diff --git a/src/ServiceControl.Audit/Auditing/AuditIngestor.cs b/src/ServiceControl.Audit/Auditing/AuditIngestor.cs
index 84d110a1f2..9885d5ba5c 100644
--- a/src/ServiceControl.Audit/Auditing/AuditIngestor.cs
+++ b/src/ServiceControl.Audit/Auditing/AuditIngestor.cs
@@ -2,7 +2,7 @@
{
using System;
using System.Collections.Generic;
- using System.Diagnostics;
+ using System.Diagnostics.Metrics;
using System.Linq;
using System.Threading.Tasks;
using Infrastructure.Settings;
@@ -14,13 +14,11 @@
using Persistence.UnitOfWork;
using Recoverability;
using SagaAudit;
- using ServiceControl.Infrastructure.Metrics;
using ServiceControl.Transports;
public class AuditIngestor
{
public AuditIngestor(
- Metrics metrics,
Settings settings,
IAuditIngestionUnitOfWorkFactory unitOfWorkFactory,
EndpointInstanceMonitoring endpointInstanceMonitoring,
@@ -32,50 +30,28 @@ ITransportCustomization transportCustomization
{
this.settings = settings;
this.messageDispatcher = messageDispatcher;
-
- var ingestedAuditMeter = metrics.GetCounter("Audit ingestion - ingested audit");
- var ingestedSagaAuditMeter = metrics.GetCounter("Audit ingestion - ingested saga audit");
- var auditBulkInsertDurationMeter = metrics.GetMeter("Audit ingestion - audit bulk insert duration", FrequencyInMilliseconds);
- var sagaAuditBulkInsertDurationMeter = metrics.GetMeter("Audit ingestion - saga audit bulk insert duration", FrequencyInMilliseconds);
- var bulkInsertCommitDurationMeter = metrics.GetMeter("Audit ingestion - bulk insert commit duration", FrequencyInMilliseconds);
-
- var enrichers = new IEnrichImportedAuditMessages[]
- {
- new MessageTypeEnricher(),
- new EnrichWithTrackingIds(),
- new ProcessingStatisticsEnricher(),
- new DetectNewEndpointsFromAuditImportsEnricher(endpointInstanceMonitoring),
- new DetectSuccessfulRetriesEnricher(),
- new SagaRelationshipsEnricher()
- }.Concat(auditEnrichers).ToArray();
+ var enrichers = new IEnrichImportedAuditMessages[] { new MessageTypeEnricher(), new EnrichWithTrackingIds(), new ProcessingStatisticsEnricher(), new DetectNewEndpointsFromAuditImportsEnricher(endpointInstanceMonitoring), new DetectSuccessfulRetriesEnricher(), new SagaRelationshipsEnricher() }.Concat(auditEnrichers).ToArray();
logQueueAddress = transportCustomization.ToTransportQualifiedQueueName(settings.AuditLogQueue);
- auditPersister = new AuditPersister(unitOfWorkFactory, enrichers, ingestedAuditMeter, ingestedSagaAuditMeter, auditBulkInsertDurationMeter, sagaAuditBulkInsertDurationMeter, bulkInsertCommitDurationMeter, messageSession, messageDispatcher);
+ auditPersister = new AuditPersister(
+ unitOfWorkFactory,
+ enrichers,
+ messageSession,
+ messageDispatcher
+ );
}
public async Task Ingest(List contexts)
{
- if (Log.IsDebugEnabled)
- {
- Log.Debug($"Ingesting {contexts.Count} message contexts");
- }
-
var stored = await auditPersister.Persist(contexts);
try
{
if (settings.ForwardAuditMessages)
{
- if (Log.IsDebugEnabled)
- {
- Log.Debug($"Forwarding {stored.Count} messages");
- }
await Forward(stored, logQueueAddress);
- if (Log.IsDebugEnabled)
- {
- Log.Debug("Forwarded messages");
- }
+ forwardedMessagesCounter.Add(stored.Count);
}
foreach (var context in contexts)
@@ -85,10 +61,7 @@ public async Task Ingest(List contexts)
}
catch (Exception e)
{
- if (Log.IsWarnEnabled)
- {
- Log.Warn("Forwarding messages failed", e);
- }
+ Log.Warn("Forwarding messages failed", e);
// making sure to rethrow so that all messages get marked as failed
throw;
@@ -158,8 +131,8 @@ public async Task VerifyCanReachForwardingAddress()
readonly Settings settings;
readonly Lazy messageDispatcher;
readonly string logQueueAddress;
+ readonly Counter forwardedMessagesCounter = Telemetry.Meter.CreateCounter(Telemetry.CreateInstrumentName("ingestion", "forwarded_count"), description: "Audit ingestion forwarded message count");
- static readonly long FrequencyInMilliseconds = Stopwatch.Frequency / 1000;
static readonly ILog Log = LogManager.GetLogger();
}
}
\ No newline at end of file
diff --git a/src/ServiceControl.Audit/Auditing/AuditPersister.cs b/src/ServiceControl.Audit/Auditing/AuditPersister.cs
index 0a5d0d9938..cca13fa470 100644
--- a/src/ServiceControl.Audit/Auditing/AuditPersister.cs
+++ b/src/ServiceControl.Audit/Auditing/AuditPersister.cs
@@ -2,7 +2,7 @@
{
using System;
using System.Collections.Generic;
- using System.Diagnostics;
+ using System.Diagnostics.Metrics;
using System.Text.Json;
using System.Threading.Tasks;
using Infrastructure;
@@ -15,43 +15,19 @@
using ServiceControl.Audit.Persistence.Monitoring;
using ServiceControl.EndpointPlugin.Messages.SagaState;
using ServiceControl.Infrastructure;
- using ServiceControl.Infrastructure.Metrics;
using ServiceControl.SagaAudit;
- class AuditPersister
+ class AuditPersister(IAuditIngestionUnitOfWorkFactory unitOfWorkFactory,
+ IEnrichImportedAuditMessages[] enrichers,
+ IMessageSession messageSession,
+ Lazy messageDispatcher)
{
- public AuditPersister(IAuditIngestionUnitOfWorkFactory unitOfWorkFactory,
- IEnrichImportedAuditMessages[] enrichers,
- Counter ingestedAuditMeter, Counter ingestedSagaAuditMeter, Meter auditBulkInsertDurationMeter,
- Meter sagaAuditBulkInsertDurationMeter, Meter bulkInsertCommitDurationMeter, IMessageSession messageSession,
- Lazy messageDispatcher)
- {
- this.unitOfWorkFactory = unitOfWorkFactory;
- this.enrichers = enrichers;
-
- this.ingestedAuditMeter = ingestedAuditMeter;
- this.ingestedSagaAuditMeter = ingestedSagaAuditMeter;
- this.auditBulkInsertDurationMeter = auditBulkInsertDurationMeter;
- this.sagaAuditBulkInsertDurationMeter = sagaAuditBulkInsertDurationMeter;
- this.bulkInsertCommitDurationMeter = bulkInsertCommitDurationMeter;
- this.messageSession = messageSession;
- this.messageDispatcher = messageDispatcher;
- }
-
public async Task> Persist(IReadOnlyList contexts)
{
- var stopwatch = Stopwatch.StartNew();
-
- if (Logger.IsDebugEnabled)
- {
- Logger.Debug($"Batch size {contexts.Count}");
- }
-
var storedContexts = new List(contexts.Count);
IAuditIngestionUnitOfWork unitOfWork = null;
try
{
-
// deliberately not using the using statement because we dispose async explicitly
unitOfWork = await unitOfWorkFactory.StartNew(contexts.Count);
var inserts = new List(contexts.Count);
@@ -84,31 +60,15 @@ public async Task> Persist(IReadOnlyList> Persist(IReadOnlyList> Persist(IReadOnlyList messageDispatcher;
+ readonly Counter storedAuditsCounter = Telemetry.Meter.CreateCounter(Telemetry.CreateInstrumentName("ingestion", "audits_count"), description: "Stored audit message count");
+ readonly Counter storedSagasCounter = Telemetry.Meter.CreateCounter(Telemetry.CreateInstrumentName("ingestion", "sagas_count"), description: "Stored saga state count");
+ readonly Histogram commitDuration = Telemetry.Meter.CreateHistogram(Telemetry.CreateInstrumentName("ingestion", "commit_duration"), unit: "ms", description: "Storage unit of work commit duration");
- readonly IEnrichImportedAuditMessages[] enrichers;
- readonly IAuditIngestionUnitOfWorkFactory unitOfWorkFactory;
static readonly ILog Logger = LogManager.GetLogger();
}
}
\ No newline at end of file
diff --git a/src/ServiceControl.Audit/HostApplicationBuilderExtensions.cs b/src/ServiceControl.Audit/HostApplicationBuilderExtensions.cs
index 8968565a50..255aa9f2f3 100644
--- a/src/ServiceControl.Audit/HostApplicationBuilderExtensions.cs
+++ b/src/ServiceControl.Audit/HostApplicationBuilderExtensions.cs
@@ -6,7 +6,6 @@ namespace ServiceControl.Audit;
using System.Threading.Tasks;
using Auditing;
using Infrastructure;
-using Infrastructure.Metrics;
using Infrastructure.Settings;
using Microsoft.AspNetCore.HttpLogging;
using Microsoft.Extensions.DependencyInjection;
@@ -20,6 +19,8 @@ namespace ServiceControl.Audit;
using NServiceBus.Transport;
using Persistence;
using Transports;
+using OpenTelemetry.Metrics;
+using OpenTelemetry.Resources;
static class HostApplicationBuilderExtensions
{
@@ -28,10 +29,11 @@ public static void AddServiceControlAudit(this IHostApplicationBuilder builder,
Settings settings,
EndpointConfiguration configuration)
{
+ var version = FileVersionInfo.GetVersionInfo(typeof(HostApplicationBuilderExtensions).Assembly.Location).ProductVersion;
var persistenceConfiguration = PersistenceConfigurationFactory.LoadPersistenceConfiguration(settings);
var persistenceSettings = persistenceConfiguration.BuildPersistenceSettings(settings);
- RecordStartup(settings, configuration, persistenceConfiguration);
+ RecordStartup(version, settings, configuration, persistenceConfiguration);
builder.Logging.ClearProviders();
builder.Logging.AddNLog();
@@ -61,13 +63,36 @@ public static void AddServiceControlAudit(this IHostApplicationBuilder builder,
// directly and to make things more complex of course the order of registration still matters ;)
services.AddSingleton(provider => new Lazy(provider.GetRequiredService));
- services.AddMetrics(settings.PrintMetrics);
-
services.AddPersistence(persistenceSettings, persistenceConfiguration);
NServiceBusFactory.Configure(settings, transportCustomization, transportSettings, onCriticalError, configuration);
builder.UseNServiceBus(configuration);
+ if (!string.IsNullOrEmpty(settings.OtlpEndpointUrl))
+ {
+ if (!Uri.TryCreate(settings.OtlpEndpointUrl, UriKind.Absolute, out var otelMetricsUri))
+ {
+ throw new UriFormatException($"Invalid OtlpEndpointUrl: {settings.OtlpEndpointUrl}");
+ }
+
+ builder.Services.AddOpenTelemetry()
+ .ConfigureResource(b => b.AddService(
+ serviceName: settings.InstanceName,
+ serviceVersion: version,
+ autoGenerateServiceInstanceId: true))
+ .WithMetrics(b =>
+ {
+ b.AddAuditIngestionMeters();
+ b.AddOtlpExporter(e =>
+ {
+ e.Endpoint = otelMetricsUri;
+ });
+ });
+
+ var logger = LogManager.GetLogger(typeof(HostApplicationBuilderExtensions));
+ logger.InfoFormat("OpenTelemetry metrics exporter enabled: {0}", settings.OtlpEndpointUrl);
+ }
+
// Configure after the NServiceBus hosted service to ensure NServiceBus is already started
if (settings.IngestAuditMessages)
{
@@ -84,10 +109,8 @@ public static void AddServiceControlAuditInstallers(this IHostApplicationBuilder
builder.Services.AddInstaller(persistenceSettings, persistenceConfiguration);
}
- static void RecordStartup(Settings settings, EndpointConfiguration endpointConfiguration, IPersistenceConfiguration persistenceConfiguration)
+ static void RecordStartup(string version, Settings settings, EndpointConfiguration endpointConfiguration, IPersistenceConfiguration persistenceConfiguration)
{
- var version = FileVersionInfo.GetVersionInfo(typeof(HostApplicationBuilderExtensions).Assembly.Location).ProductVersion;
-
var startupMessage = $@"
-------------------------------------------------------------
ServiceControl Audit Version: {version}
@@ -101,9 +124,6 @@ static void RecordStartup(Settings settings, EndpointConfiguration endpointConfi
var logger = LogManager.GetLogger(typeof(HostApplicationBuilderExtensions));
logger.Info(startupMessage);
- endpointConfiguration.GetSettings().AddStartupDiagnosticsSection("Startup", new
- {
- Settings = settings
- });
+ endpointConfiguration.GetSettings().AddStartupDiagnosticsSection("Startup", new { Settings = settings });
}
}
\ No newline at end of file
diff --git a/src/ServiceControl.Audit/Infrastructure/DurationRecorder.cs b/src/ServiceControl.Audit/Infrastructure/DurationRecorder.cs
new file mode 100644
index 0000000000..ebb5531555
--- /dev/null
+++ b/src/ServiceControl.Audit/Infrastructure/DurationRecorder.cs
@@ -0,0 +1,12 @@
+namespace ServiceControl.Audit;
+
+using System;
+using System.Diagnostics;
+using System.Diagnostics.Metrics;
+
+record DurationRecorder(Histogram Histogram) : IDisposable
+{
+ readonly Stopwatch sw = Stopwatch.StartNew();
+
+ public void Dispose() => Histogram.Record(sw.ElapsedMilliseconds);
+}
\ No newline at end of file
diff --git a/src/ServiceControl.Audit/Infrastructure/Metrics/MetricsReporterHostedService.cs b/src/ServiceControl.Audit/Infrastructure/Metrics/MetricsReporterHostedService.cs
deleted file mode 100644
index c378a8f4d6..0000000000
--- a/src/ServiceControl.Audit/Infrastructure/Metrics/MetricsReporterHostedService.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-namespace ServiceControl.Audit.Infrastructure.Metrics
-{
- using System;
- using System.Threading;
- using System.Threading.Tasks;
- using Microsoft.Extensions.Hosting;
- using NServiceBus.Logging;
- using ServiceControl.Infrastructure.Metrics;
-
- class MetricsReporterHostedService : IHostedService
- {
- readonly Metrics metrics;
- MetricsReporter reporter;
-
- public MetricsReporterHostedService(Metrics metrics) => this.metrics = metrics;
-
- public Task StartAsync(CancellationToken cancellationToken)
- {
- var metricsLog = LogManager.GetLogger("Metrics");
-
- reporter = new MetricsReporter(metrics, x => metricsLog.Info(x), TimeSpan.FromSeconds(5));
-
- reporter.Start();
-
- return Task.CompletedTask;
- }
-
- public Task StopAsync(CancellationToken cancellationToken) => reporter.Stop();
- }
-}
\ No newline at end of file
diff --git a/src/ServiceControl.Audit/Infrastructure/Metrics/MetricsServiceCollectionExtensions.cs b/src/ServiceControl.Audit/Infrastructure/Metrics/MetricsServiceCollectionExtensions.cs
deleted file mode 100644
index a7bc6b1dd5..0000000000
--- a/src/ServiceControl.Audit/Infrastructure/Metrics/MetricsServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace ServiceControl.Audit.Infrastructure.Metrics
-{
- using Microsoft.Extensions.DependencyInjection;
- using ServiceControl.Infrastructure.Metrics;
-
- static class MetricsServiceCollectionExtensions
- {
- public static void AddMetrics(this IServiceCollection services, bool printMetrics)
- {
- services.AddSingleton(new Metrics { Enabled = printMetrics });
- services.AddHostedService();
- }
- }
-}
\ No newline at end of file
diff --git a/src/ServiceControl.Audit/Infrastructure/Settings/Settings.cs b/src/ServiceControl.Audit/Infrastructure/Settings/Settings.cs
index 183b695555..c0b1dfb892 100644
--- a/src/ServiceControl.Audit/Infrastructure/Settings/Settings.cs
+++ b/src/ServiceControl.Audit/Infrastructure/Settings/Settings.cs
@@ -109,6 +109,7 @@ public string RootUrl
public int Port { get; set; }
public bool PrintMetrics => SettingsReader.Read(SettingsRootNamespace, "PrintMetrics");
+ public string OtlpEndpointUrl { get; set; } = SettingsReader.Read(SettingsRootNamespace, nameof(OtlpEndpointUrl));
public string Hostname { get; private set; }
public string VirtualDirectory => SettingsReader.Read(SettingsRootNamespace, "VirtualDirectory", string.Empty);
diff --git a/src/ServiceControl.Audit/Infrastructure/Telemetry.cs b/src/ServiceControl.Audit/Infrastructure/Telemetry.cs
new file mode 100644
index 0000000000..568bced7b2
--- /dev/null
+++ b/src/ServiceControl.Audit/Infrastructure/Telemetry.cs
@@ -0,0 +1,17 @@
+namespace ServiceControl.Audit;
+
+using System.Diagnostics.Metrics;
+using OpenTelemetry.Metrics;
+
+static class Telemetry
+{
+ const string MeterName = "Particular.ServiceControl.Audit";
+ public static readonly Meter Meter = new(MeterName, "0.1.0");
+
+ public static string CreateInstrumentName(string instrumentNamespace, string instrumentName) => $"sc.audit.{instrumentNamespace}.{instrumentName}".ToLower();
+
+ public static void AddAuditIngestionMeters(this MeterProviderBuilder builder)
+ {
+ builder.AddMeter(MeterName);
+ }
+}
\ No newline at end of file
diff --git a/src/ServiceControl.Audit/ServiceControl.Audit.csproj b/src/ServiceControl.Audit/ServiceControl.Audit.csproj
index 3303782e3f..8f41ba97b1 100644
--- a/src/ServiceControl.Audit/ServiceControl.Audit.csproj
+++ b/src/ServiceControl.Audit/ServiceControl.Audit.csproj
@@ -18,7 +18,6 @@
-
@@ -29,6 +28,8 @@
+
+