Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions docs/telemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@

Instances can be configured to emit telemetry to aid in performance testing or troubleshooting performance-related issues.

Both the error and the audit instance report their ingestion the same way. Set `OtlpEndpointUrl`
in that instance's settings root to a valid [OTLP endpoint url](https://opentelemetry.io/docs/specs/otel/protocol/exporter/#configuration-options).
Only GRPC endpoints are supported at this stage.
Both the error and the audit instance report their ingestion the same way. Exporting is configured with the standard [OpenTelemetry environment variables](https://opentelemetry.io/docs/specs/otel/protocol/exporter/#configuration-options), not with instance settings, so the same variables that configure any other OpenTelemetry process apply here. Setting `OTEL_EXPORTER_OTLP_ENDPOINT` is enough to turn metrics on. Both gRPC and HTTP endpoints are supported, and `OTEL_EXPORTER_OTLP_PROTOCOL` selects between them.

The instruments differ only in their prefix and in the categories a message can fall into, so the
same dashboard works for both with the prefix swapped. What the batches being measured actually are
is covered in [ingestion-pipeline.md](ingestion-pipeline.md).
The signal-specific variables, `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` and its siblings, have no effect. The SDK only honours those under `UseOtlpExporter`, and instances use `AddOtlpExporter` so that OTLP applies to metrics without also being turned on for every other signal.

Logs are exported separately. Add `Otlp` to the instance's `LoggingProviders` setting, which is what turns the OTLP log exporter on, and it then reads the same environment variables for its endpoint.

The instruments differ only in their prefix and in the categories a message can fall into, so the same dashboard works for both with the prefix swapped. What the batches being measured actually are is covered in [ingestion-pipeline.md](ingestion-pipeline.md).

## Error

Meter `Particular.ServiceControl`, configured with `ServiceControl/OtlpEndpointUrl`.
Meter `Particular.ServiceControl`.

- `sc.error.ingestion.batch_duration_seconds` - Message batch processing duration in seconds
- `result` - Whether the batch was written at its configured size: `full`, `partial` or `failed`
Expand All @@ -29,7 +29,7 @@ Meter `Particular.ServiceControl`, configured with `ServiceControl/OtlpEndpointU

## Audit

Meter `Particular.ServiceControl.Audit`, configured with `ServiceControl.Audit/OtlpEndpointUrl`.
Meter `Particular.ServiceControl.Audit`.

- `sc.audit.ingestion.batch_duration_seconds` - Message batch processing duration in seconds
- `result` - Whether the batch was written at its configured size: `full`, `partial` or `failed`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
{
"LoggingSettings": {
"LogLevel": "Information",
"LogPath": "C:\\Logs"
Expand Down Expand Up @@ -47,7 +47,6 @@
"ApiUrl": "http://localhost:8888/api",
"Port": 8888,
"PrintMetrics": false,
"OtlpEndpointUrl": null,
"Hostname": "localhost",
"VirtualDirectory": "",
"TransportType": "LearningTransport",
Expand Down
10 changes: 6 additions & 4 deletions src/ServiceControl.Audit/App.config
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<!--
NOTE: Any settings in this file are not kept as part of packaging ServiceControl for a release.
These settings are only here so that we can debug ServiceControl while developing it.
Expand All @@ -22,9 +22,11 @@ These settings are only here so that we can debug ServiceControl while developin
<add key="ServiceControl.Audit/PersistenceType" value="InMemory" />
<!--<add key="ServiceControl.Audit/PersistenceType" value="RavenDB" />-->

<!-- options are any comma separated combination of NLog,Seq,Otlp -->
<add key="ServiceControl.Audit/LoggingProviders" value="NLog,Seq"/>
<add key="ServiceControl.Audit/SeqAddress" value="http://localhost:5341"/>
<!-- options are any comma separated combination of NLog,Seq,Otlp -->
<add key="ServiceControl.Audit/LoggingProviders" value="NLog,Seq"/>
<add key="ServiceControl.Audit/SeqAddress" value="http://localhost:5341"/>

<!-- OTLP logs and metrics are configured with the standard OpenTelemetry environment variables, e.g. OTEL_EXPORTER_OTLP_ENDPOINT -->

<!-- Authentication Settings (JWT with OpenID Connect) -->
<!-- Uncomment and configure to enable authentication -->
Expand Down
45 changes: 22 additions & 23 deletions src/ServiceControl.Audit/HostApplicationBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace ServiceControl.Audit;
namespace ServiceControl.Audit;

using System;
using System.Diagnostics;
Expand Down Expand Up @@ -97,31 +97,30 @@ public static void AddMetrics(this IHostApplicationBuilder builder, Settings set
{
builder.Services.AddSingleton<IngestionMetrics>();

if (!string.IsNullOrEmpty(settings.OtlpEndpointUrl))
var otlpEndpoint = OtlpEndpoint.Read(builder.Configuration);

if (otlpEndpoint is null)
{
if (!Uri.TryCreate(settings.OtlpEndpointUrl, UriKind.Absolute, out var otelMetricsUri))
return;
}

builder.Services.AddOpenTelemetry()
.ConfigureResource(b => b.AddService(
serviceName: settings.InstanceName,
serviceVersion: InstanceVersion,
autoGenerateServiceInstanceId: true))
.WithMetrics(b =>
{
throw new UriFormatException($"Invalid OtlpEndpointUrl: {settings.OtlpEndpointUrl}");
}

builder.Services.AddOpenTelemetry()
.ConfigureResource(b => b.AddService(
serviceName: settings.InstanceName,
serviceVersion: InstanceVersion,
autoGenerateServiceInstanceId: true))
.WithMetrics(b =>
b.AddIngestionMetrics();
b.AddOtlpExporter();
if (Debugger.IsAttached)
{
b.AddIngestionMetrics();
b.AddOtlpExporter(e => e.Endpoint = otelMetricsUri);
if (Debugger.IsAttached)
{
b.AddConsoleExporter();
}
});

var logger = LoggerUtil.CreateStaticLogger(typeof(HostApplicationBuilderExtensions), settings.LoggingSettings.LogLevel);
logger.LogInformation("OpenTelemetry metrics exporter enabled: {OtlpEndpointUrl}", settings.OtlpEndpointUrl);
}
b.AddConsoleExporter();
}
});

var logger = LoggerUtil.CreateStaticLogger(typeof(HostApplicationBuilderExtensions), settings.LoggingSettings.LogLevel);
logger.LogInformation("OpenTelemetry metrics exporter enabled: {OtlpEndpoint}", otlpEndpoint);
}

static void RecordStartup(Settings settings, EndpointConfiguration endpointConfiguration, IPersistenceConfiguration persistenceConfiguration)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,6 @@ public string RootUrl
public int Port { get; set; }

public bool PrintMetrics => SettingsReader.Read<bool>(SettingsRootNamespace, "PrintMetrics");
public string OtlpEndpointUrl { get; set; } = SettingsReader.Read<string>(SettingsRootNamespace, nameof(OtlpEndpointUrl));
Comment thread
warwickschroeder marked this conversation as resolved.
public string Hostname { get; private set; }
public string VirtualDirectory => SettingsReader.Read(SettingsRootNamespace, "VirtualDirectory", string.Empty);

Expand Down
28 changes: 28 additions & 0 deletions src/ServiceControl.Infrastructure/OtlpEndpoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace ServiceControl.Infrastructure;

using System;
using Microsoft.Extensions.Configuration;

public static class OtlpEndpoint
{
// This is the default environment variable name used by the OpenTelemetry .NET SDK to configure the OTLP exporter endpoint
// as specified in https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md.
const string EndpointKey = "OTEL_EXPORTER_OTLP_ENDPOINT";

public static Uri Read(IConfiguration configuration)
{
var configuredEndpoint = configuration[EndpointKey];

if (string.IsNullOrWhiteSpace(configuredEndpoint))
{
return null;
}

if (!Uri.TryCreate(configuredEndpoint, UriKind.Absolute, out var endpoint))
{
throw new UriFormatException($"Invalid {EndpointKey}: {configuredEndpoint}");
}

return endpoint;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
{
"LoggingSettings": {
"LogLevel": "Information",
"LogPath": "C:\\Logs"
Expand Down
10 changes: 6 additions & 4 deletions src/ServiceControl.Monitoring/App.config
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<!--
NOTE: Any settings in this file are not kept as part of packaging ServiceControl for a release.
These settings are only here so that we can debug ServiceControl while developing it.
Expand All @@ -19,9 +19,11 @@ These settings are only here so that we can debug ServiceControl while developin
<!--<add key="Monitoring/TransportType" value="RabbitMQ.QuorumConventionalRouting" />-->
<!--<add key="Monitoring/TransportType" value="SQLServer" />-->

<!-- options are any comma separated combination of NLog,Seq,Otlp -->
<add key="Monitoring/LoggingProviders" value="NLog,Seq"/>
<add key="Monitoring/SeqAddress" value="http://localhost:5341"/>
<!-- options are any comma separated combination of NLog,Seq,Otlp -->
<add key="Monitoring/LoggingProviders" value="NLog,Seq"/>
<add key="Monitoring/SeqAddress" value="http://localhost:5341"/>

<!-- OTLP logs and metrics are configured with the standard OpenTelemetry environment variables, e.g. OTEL_EXPORTER_OTLP_ENDPOINT -->
Comment thread
warwickschroeder marked this conversation as resolved.

<!-- Authentication Settings (JWT with OpenID Connect) -->
<!-- Uncomment and configure to enable authentication -->
Expand Down
2 changes: 1 addition & 1 deletion src/ServiceControl.Monitoring/Settings.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace ServiceControl.Monitoring
namespace ServiceControl.Monitoring
{
using System;
using System.Collections.Generic;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
{
"LoggingSettings": {
"LogLevel": "Information",
"LogPath": "C:\\Logs"
Expand Down Expand Up @@ -59,7 +59,6 @@
"Port": 8888,
"PersisterSpecificSettings": null,
"PrintMetrics": false,
"OtlpEndpointUrl": null,
"Hostname": "localhost",
"VirtualDirectory": "",
"HeartbeatGracePeriod": "00:00:40",
Expand Down
4 changes: 3 additions & 1 deletion src/ServiceControl/App.config
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<!--
NOTE: Any settings in this file are not kept as part of packaging ServiceControl for a release.
These settings are only here so that we can debug ServiceControl while developing it.
Expand Down Expand Up @@ -28,6 +28,8 @@ These settings are only here so that we can debug ServiceControl while developin
<!-- options are any comma separated combination of NLog,Seq,Otlp -->
<add key="ServiceControl/LoggingProviders" value="NLog,Seq"/>
<add key="ServiceControl/SeqAddress" value="http://localhost:5341"/>

<!-- OTLP logs and metrics are configured with the standard OpenTelemetry environment variables, e.g. OTEL_EXPORTER_OTLP_ENDPOINT -->

<!-- Authentication Settings (JWT with OpenID Connect) -->
<!-- Uncomment and configure to enable authentication -->
Expand Down
13 changes: 5 additions & 8 deletions src/ServiceControl/HostApplicationBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,14 +152,11 @@ public static void AddIngestionMetrics(this IHostApplicationBuilder hostBuilder,
{
hostBuilder.Services.AddSingleton<IngestionMetrics>();

if (string.IsNullOrEmpty(settings.OtlpEndpointUrl))
{
return;
}
var otlpEndpoint = OtlpEndpoint.Read(hostBuilder.Configuration);

if (!Uri.TryCreate(settings.OtlpEndpointUrl, UriKind.Absolute, out var otlpEndpoint))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you confirmed that the OpenTelemetry library does indeed fail an invalid URL?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It now fails if the URL is invalid, so it no longer defaults.

if (otlpEndpoint is null)
{
throw new UriFormatException($"Invalid OtlpEndpointUrl: {settings.OtlpEndpointUrl}");
return;
}

hostBuilder.Services.AddOpenTelemetry()
Expand All @@ -170,7 +167,7 @@ public static void AddIngestionMetrics(this IHostApplicationBuilder hostBuilder,
.WithMetrics(metrics =>
{
metrics.AddIngestionMetrics();
metrics.AddOtlpExporter(exporter => exporter.Endpoint = otlpEndpoint);
metrics.AddOtlpExporter();

if (Debugger.IsAttached)
{
Expand All @@ -179,7 +176,7 @@ public static void AddIngestionMetrics(this IHostApplicationBuilder hostBuilder,
});

LoggerUtil.CreateStaticLogger(typeof(HostApplicationBuilderExtensions), settings.LoggingSettings.LogLevel)
.LogInformation("OpenTelemetry metrics exporter enabled: {OtlpEndpointUrl}", settings.OtlpEndpointUrl);
.LogInformation("OpenTelemetry metrics exporter enabled: {OtlpEndpoint}", otlpEndpoint);
}

static void RecordStartup(Settings settings, EndpointConfiguration endpointConfiguration)
Expand Down
1 change: 0 additions & 1 deletion src/ServiceControl/Infrastructure/Settings/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,6 @@ public string InstanceId

public bool PrintMetrics => SettingsReader.Read<bool>(SettingsRootNamespace, "PrintMetrics");

public string OtlpEndpointUrl { get; set; } = SettingsReader.Read<string>(SettingsRootNamespace, nameof(OtlpEndpointUrl));
public string Hostname { get; private set; }
public string VirtualDirectory => SettingsReader.Read(SettingsRootNamespace, "VirtualDirectory", string.Empty);

Expand Down
Loading