Skip to content

Commit

Permalink
Enable metrics collection via provider (#43)
Browse files Browse the repository at this point in the history
* Remove code handling ELASTIC_ environment variables

* Enable metrics provider
  • Loading branch information
stevejgordon authored Mar 4, 2024
1 parent 3284bfc commit 608332c
Show file tree
Hide file tree
Showing 12 changed files with 214 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.7.0" />
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion examples/Example.Elastic.OpenTelemetry.Worker/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

var builder = Host.CreateApplicationBuilder(args);

builder.AddElasticOpenTelemetry("CustomActivitySource");
builder.AddElasticOpenTelemetry("CustomActivitySource", "CustomMeter");

builder.Services.AddHostedService<Worker>();

Expand Down
44 changes: 24 additions & 20 deletions examples/Example.Elastic.OpenTelemetry.Worker/Worker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,44 @@
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information
using System.Diagnostics;
using System.Diagnostics.Metrics;

namespace Example.Elastic.OpenTelemetry.Worker;

public class Worker : BackgroundService
public class Worker(ILogger<Worker> logger) : BackgroundService
{
private readonly ILogger<Worker> _logger;
private readonly ILogger<Worker> _logger = logger;

private static readonly HttpClient HttpClient = new();
private const string ActivitySourceName = "CustomActivitySource";
private static readonly ActivitySource ActivitySource = new(ActivitySourceName, "1.0.0");

public Worker(ILogger<Worker> logger) => _logger = logger;
private static readonly Meter Meter = new("CustomMeter");
private static readonly Counter<int> Counter = Meter.CreateCounter<int>("invocations",
null, null, [KeyValuePair.Create<string, object?>("label1", "value1")]);

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Sending request... ");
_logger.LogInformation("Sending request... ");

using (var activity = ActivitySource.StartActivity("DoingStuff", ActivityKind.Internal))
{
activity?.SetTag("CustomTag", "TagValue");
using (var activity = ActivitySource.StartActivity("DoingStuff", ActivityKind.Internal))
{
activity?.SetTag("CustomTag", "TagValue");

if (Counter.Enabled)
Counter.Add(1);

await Task.Delay(100, stoppingToken);
var response = await HttpClient.GetAsync("http://elastic.co", stoppingToken);
await Task.Delay(50, stoppingToken);
_logger.LogInformation("Sending request... ");

if (response.StatusCode == System.Net.HttpStatusCode.OK)
activity?.SetStatus(ActivityStatusCode.Ok);
else
activity?.SetStatus(ActivityStatusCode.Error);
}
await Task.Delay(100, stoppingToken);
var response = await HttpClient.GetAsync("http://elastic.co", stoppingToken);
await Task.Delay(50, stoppingToken);

await Task.Delay(5000, stoppingToken);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
activity?.SetStatus(ActivityStatusCode.Ok);
else
activity?.SetStatus(ActivityStatusCode.Error);
}

await Task.Delay(5000, stoppingToken);
}
}
}
98 changes: 90 additions & 8 deletions src/Elastic.OpenTelemetry/AgentBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,9 @@ namespace Elastic.OpenTelemetry;
/// </summary>
public class AgentBuilder
{
private readonly MeterProviderBuilder _meterProvider =
Sdk.CreateMeterProviderBuilder()
.AddProcessInstrumentation()
.AddRuntimeInstrumentation()
.AddHttpClientInstrumentation();

private readonly string[] _activitySourceNames = [];
private Action<TracerProviderBuilder> _tracerProviderBuilderAction = tpb => { };
private Action<MeterProviderBuilder> _meterProviderBuilderAction = mpb => { };
private Action<ResourceBuilder>? _resourceBuilderAction = rb => { };
private Action<OtlpExporterOptions>? _otlpExporterConfiguration;
private string? _otlpExporterName;
Expand Down Expand Up @@ -144,6 +139,42 @@ public AgentBuilder ConfigureTracer(Action<ResourceBuilder> configureResourceBui
return this;
}

/// <summary>
/// TODO
/// </summary>
public AgentBuilder ConfigureMeter(params string[] activitySourceNames)
{
MeterInternal(null, activitySourceNames);
return this;
}

/// <summary>
/// TODO
/// </summary>
public AgentBuilder ConfigureMeter(Action<ResourceBuilder> configureResourceBuilder)
{
MeterInternal(configureResourceBuilder, null);
return this;
}

/// <summary>
/// TODO
/// </summary>
public AgentBuilder ConfigureMeter(Action<ResourceBuilder> configureResourceBuilder, params string[] activitySourceNames)
{
MeterInternal(configureResourceBuilder, activitySourceNames);
return this;
}

/// <summary>
/// TODO
/// </summary>
public AgentBuilder ConfigureMeter(Action<ResourceBuilder> configureResourceBuilder, string activitySourceName)
{
MeterInternal(configureResourceBuilder, [activitySourceName]);
return this;
}

/// <summary>
/// TODO
/// </summary>
Expand All @@ -161,6 +192,26 @@ public AgentBuilder ConfigureTracer(Action<TracerProviderBuilder> configure)
return this;
}

/// <summary>
/// TODO
/// </summary>
public AgentBuilder ConfigureMeter(Action<MeterProviderBuilder> configure)
{
ArgumentNullException.ThrowIfNull(configure);
_meterProviderBuilderAction += configure;
return this;
}

private AgentBuilder MeterInternal(Action<ResourceBuilder>? configureResourceBuilder = null, string[]? activitySourceNames = null)
{
_resourceBuilderAction = configureResourceBuilder;

if (activitySourceNames is not null)
_meterProviderBuilderAction += mpb => mpb.AddMeter(activitySourceNames);

return this;
}

private AgentBuilder TracerInternal(Action<ResourceBuilder>? configureResourceBuilder = null, string[]? activitySourceNames = null)
{
_resourceBuilderAction = configureResourceBuilder;
Expand All @@ -183,7 +234,13 @@ public IAgent Build()

Log(AgentBuilderBuiltTracerProviderEvent);

var agent = tracerProvider is not null ? new Agent(_diagnosticSourceSubscription, tracerProvider) : new Agent(_diagnosticSourceSubscription);
var meterProviderBuilder = Sdk.CreateMeterProviderBuilder();
MeterProviderBuilderAction.Invoke(meterProviderBuilder);
var meterProvider = meterProviderBuilder.Build();

Log(AgentBuilderBuiltMeterProviderEvent);

var agent = new Agent(_diagnosticSourceSubscription, tracerProvider, meterProvider);

Log(AgentBuilderBuiltAgentEvent);

Expand All @@ -207,7 +264,8 @@ public IServiceCollection Register(IServiceCollection serviceCollection)
.AddSingleton<IAgent>(new Agent(_diagnosticSourceSubscription))
.AddSingleton<LoggerResolver>()
.AddOpenTelemetry()
.WithTracing(TracerProviderBuilderAction);
.WithTracing(TracerProviderBuilderAction)
.WithMetrics(MeterProviderBuilderAction);

Log(AgentBuilderRegisteredDistroServicesEvent);

Expand Down Expand Up @@ -236,6 +294,30 @@ public IServiceCollection Register(IServiceCollection serviceCollection)
tracerProviderBuilder.AddOtlpExporter(_otlpExporterName, _otlpExporterConfiguration);
};

private Action<MeterProviderBuilder> MeterProviderBuilderAction =>
builder =>
{
foreach (var source in _activitySourceNames)
builder.LogAndAddMeter(source);
builder
.AddProcessInstrumentation()
.AddRuntimeInstrumentation()
.AddHttpClientInstrumentation();
var action = _resourceBuilderAction;
action += b => b.AddDistroAttributes();
builder.ConfigureResource(action);
_meterProviderBuilderAction?.Invoke(builder);
builder.AddOtlpExporter(_otlpExporterName, o =>
{
o.ExportProcessorType = ExportProcessorType.Simple;
o.Protocol = OtlpExportProtocol.HttpProtobuf;
});
};

/// <summary>
/// TODO
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information
using Elastic.OpenTelemetry;
using Microsoft.Extensions.Hosting;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;

namespace Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -32,6 +33,23 @@ public static IHostApplicationBuilder AddElasticOpenTelemetry(this IHostApplicat
return builder;
}

/// <summary>
/// TODO
/// </summary>
/// <param name="builder"></param>
/// <param name="configureTracerProvider"></param>
/// <param name="configureMeterProvider"></param>
/// <returns></returns>
public static IHostApplicationBuilder AddElasticOpenTelemetry(
this IHostApplicationBuilder builder,
Action<TracerProviderBuilder>? configureTracerProvider,
Action<MeterProviderBuilder>? configureMeterProvider)
{
builder.Services.AddElasticOpenTelemetry(configureTracerProvider, configureMeterProvider);
return builder;
}


/// <summary>
/// Adds the Elastic OpenTelemetry distribution to an application via the <see cref="IServiceCollection"/>.
/// </summary>
Expand All @@ -54,7 +72,21 @@ public static IServiceCollection AddElasticOpenTelemetry(this IServiceCollection
/// </summary>
/// <param name="serviceCollection"></param>
/// <param name="configureTracerProvider"></param>
/// <param name="configureMeterProvider"></param>
/// <returns></returns>
public static IServiceCollection AddElasticOpenTelemetry(this IServiceCollection serviceCollection, Action<TracerProviderBuilder> configureTracerProvider) =>
new AgentBuilder().ConfigureTracer(configureTracerProvider).Register(serviceCollection);
public static IServiceCollection AddElasticOpenTelemetry(
this IServiceCollection serviceCollection,
Action<TracerProviderBuilder>? configureTracerProvider,
Action<MeterProviderBuilder>? configureMeterProvider)
{
var builder = new AgentBuilder();

if (configureTracerProvider is not null)
builder.ConfigureTracer(configureTracerProvider);

if (configureMeterProvider is not null)
builder.ConfigureMeter(configureMeterProvider);

return builder.Register(serviceCollection);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ public void OnNext(KeyValuePair<string, object?> data)
AgentBuilderBuiltTracerProvider(data);
break;

case ElasticOpenTelemetryDiagnostics.AgentBuilderBuiltMeterProviderEvent:
AgentBuilderBuiltMeterProvider(data);
break;

case ElasticOpenTelemetryDiagnostics.AgentBuilderBuiltAgentEvent:
AgentBuilderBuiltAgent(data);
break;
Expand Down Expand Up @@ -52,6 +56,10 @@ public void OnNext(KeyValuePair<string, object?> data)
SourceAdded(data);
break;

case ElasticOpenTelemetryDiagnostics.MeterAddedEvent:
MeterAdded(data);
break;

default:
if (data.Value is DiagnosticEvent diagnostic)
_logFileWriter.LogUnhandledEvent(data.Key, diagnostic);
Expand All @@ -70,6 +78,12 @@ void AgentBuilderBuiltTracerProvider(KeyValuePair<string, object?> data)
_logFileWriter.LogAgentBuilderBuiltTracerProvider(diagnostic);
}

void AgentBuilderBuiltMeterProvider(KeyValuePair<string, object?> data)
{
if (data.Value is DiagnosticEvent diagnostic)
_logFileWriter.LogAgentBuilderBuiltMeterProvider(diagnostic);
}

void AgentBuilderBuiltAgent(KeyValuePair<string, object?> data)
{
if (data.Value is DiagnosticEvent diagnostic)
Expand Down Expand Up @@ -111,6 +125,12 @@ void SourceAdded(KeyValuePair<string, object?> data)
if (data.Value is DiagnosticEvent<AddSourcePayload> diagnostic)
_logFileWriter.LogSourceAdded(diagnostic);
}

void MeterAdded(KeyValuePair<string, object?> data)
{
if (data.Value is DiagnosticEvent<AddSourcePayload> diagnostic)
_logFileWriter.LogMeterAdded(diagnostic);
}
}

public void OnCompleted() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public static void Log(string name, Func<DiagnosticEvent> createDiagnosticEvent)

public const string AgentBuilderBuiltTracerProviderEvent = "AgentBuilderBuiltTracerProvider";

public const string AgentBuilderBuiltMeterProviderEvent = "AgentBuilderBuiltMeterProvider";

public const string AgentBuilderRegisteredDistroServicesEvent = "RegisteredDistroServices";

public const string AgentBuilderBuiltAgentEvent = "AgentBuilderBuiltAgent";
Expand All @@ -49,6 +51,8 @@ public static void Log(string name, Func<DiagnosticEvent> createDiagnosticEvent)

public const string SourceAddedEvent = "SourceAdded";

public const string MeterAddedEvent = "MeterAdded";

public const string AgentBuildCalledMultipleTimesEvent = "AgentBuildCalledMultipleTimes";

public const string AgentSetAgentCalledMultipleTimesEvent = "AgentSetAgentCalledMultipleTimes";
Expand All @@ -65,6 +69,9 @@ public static void LogAgentBuilderInitialized(this LogFileWriter logFileWriter,
public static void LogAgentBuilderBuiltTracerProvider(this LogFileWriter logFileWriter, DiagnosticEvent diagnostic) =>
logFileWriter.WriteInfoLogLine(diagnostic, "AgentBuilder built TracerProvider.");

public static void LogAgentBuilderBuiltMeterProvider(this LogFileWriter logFileWriter, DiagnosticEvent diagnostic) =>
logFileWriter.WriteInfoLogLine(diagnostic, "AgentBuilder built MeterProvider.");

public static void LogAgentBuilderBuiltAgent(this LogFileWriter logFileWriter, DiagnosticEvent diagnostic) =>
logFileWriter.WriteInfoLogLine(diagnostic, "AgentBuilder built Agent.");

Expand All @@ -91,7 +98,13 @@ public static void LogProcessorAdded(this LogFileWriter logFileWriter, Diagnosti

public static void LogSourceAdded(this LogFileWriter logFileWriter, DiagnosticEvent<AddSourcePayload> diagnostic)
{
var message = $"Added '{diagnostic.Data.ActivitySourceName}' ActivitySource to '{diagnostic.Data.BuilderType.Name}'.";
var message = $"Added '{diagnostic.Data.Name}' ActivitySource to '{diagnostic.Data.BuilderType.Name}'.";
logFileWriter.WriteInfoLogLine(diagnostic, message);
}

public static void LogMeterAdded(this LogFileWriter logFileWriter, DiagnosticEvent<AddSourcePayload> diagnostic)
{
var message = $"Added '{diagnostic.Data.Name}' Meter to '{diagnostic.Data.BuilderType.Name}'.";
logFileWriter.WriteInfoLogLine(diagnostic, message);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
// See the LICENSE file in the project root for more information
namespace Elastic.OpenTelemetry.Diagnostics;

internal readonly record struct AddSourcePayload(string ActivitySourceName, Type BuilderType);
internal readonly record struct AddSourcePayload(string Name, Type BuilderType);

This file was deleted.

Loading

0 comments on commit 608332c

Please sign in to comment.