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 @@ + +