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
6 changes: 0 additions & 6 deletions .github/workflows/bootstrap/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,6 @@ runs:
uses: actions/setup-dotnet@v5
with:
global-json-file: ./global.json
# 6.x is required for the release-notes tool.
# 7.x is required for the dotnet-project-licenses tool.
dotnet-version: |
6.x
7.x
8.x

- id: dotnet
shell: bash
Expand Down
43 changes: 19 additions & 24 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,50 @@
</PropertyGroup>
<ItemGroup>
<!-- OpenTelemetry -->
<PackageVersion Include="OpenTelemetry" Version="1.12.0" />
<PackageVersion Include="OpenTelemetry.AutoInstrumentation" Version="1.12.0" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.12.0" />
<PackageVersion Include="OpenTelemetry.Exporter.InMemory" Version="1.12.0" />
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.12.0" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.12.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.12.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.GrpcNetClient" Version="1.12.0-beta.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.12.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Process" Version="1.12.0-beta.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.12.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.SqlClient" Version="1.12.0-beta.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.StackExchangeRedis" Version="1.12.0-beta.1" />
<PackageVersion Include="OpenTelemetry.Resources.Host" Version="1.12.0-beta.1" />
<PackageVersion Include="OpenTelemetry.Resources.ProcessRuntime" Version="1.12.0-beta.1" />

<PackageVersion Include="OpenTelemetry" Version="1.14.0" />
<PackageVersion Include="OpenTelemetry.AutoInstrumentation" Version="1.13.0" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.14.0" />
<PackageVersion Include="OpenTelemetry.Exporter.InMemory" Version="1.14.0" />
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.14.0" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.14.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.14.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.GrpcNetClient" Version="1.14.0-beta.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.14.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Process" Version="1.14.0-beta.2" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.14.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.SqlClient" Version="1.14.0-beta.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.StackExchangeRedis" Version="1.14.0-beta.1" />
<PackageVersion Include="OpenTelemetry.Resources.Host" Version="1.14.0-beta.1" />
<PackageVersion Include="OpenTelemetry.Resources.ProcessRuntime" Version="1.14.0-beta.1" />
<!-- Microsoft Extensions -->
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.2" Condition="'$(TargetFramework)' == 'net8.0' OR '$(TargetFramework)' == 'net9.0'" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.0" Condition="'$(TargetFramework)' == 'net8.0' OR '$(TargetFramework)' == 'net9.0'" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery" Version="10.0.0" />

<!-- General -->
<PackageVersion Include="ConfigureAwaitChecker.Analyzer" Version="5.0.0.1" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="MinVer" Version="6.0.0" />
<PackageVersion Include="NetEscapades.EnumGenerators" Version="1.0.0-beta16" />
<PackageVersion Include="Polyfill" Version="9.3.0" />

<!-- Build -->
<PackageVersion Include="Argu" Version="6.2.5" />
<PackageVersion Include="Bullseye" Version="6.0.0" />
<PackageVersion Include="Octokit" Version="13.0.1" />
<PackageVersion Include="Octokit" Version="14.0.0" />
<PackageVersion Include="Proc.Fs" Version="0.11.0" />
<PackageVersion Include="Fake.Tools.Git" Version="5.23.1" />

<PackageVersion Include="Fake.Tools.Git" Version="6.1.4" />
<!-- Testing -->
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="JunitXml.TestLogger" Version="7.0.2" />
<PackageVersion Include="Nullean.VsTest.Pretty.TestLogger" Version="0.10.0" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="Testcontainers" Version="4.9.0" />
<PackageVersion Include="GitHubActionsTestLogger" Version="3.0.1" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.22" Condition="'$(TargetFramework)' == 'net8.0'" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.11" Condition="'$(TargetFramework)' == 'net9.0'" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.0" Condition="'$(TargetFramework)' == 'net10.0'" />

<!-- Examples -->
<PackageVersion Include="Aspire.Hosting.AppHost" Version="13.0.0" />
</ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion build/patch-dotnet-auto-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ else
fi

test -z "$OTEL_DOTNET_AUTO_HOME" && OTEL_DOTNET_AUTO_HOME="$HOME/.otel-dotnet-auto"
test -z "$VERSION" && VERSION="v1.9.0"
test -z "$VERSION" && VERSION="v1.13.0"

DOWNLOAD_DIR="${DOWNLOAD_DIR:=${TMPDIR:=$(mktemp -d)}}"

Expand Down
9 changes: 7 additions & 2 deletions build/scripts/Packaging.fs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,11 @@ let stageArtifacts (assets:List<ReleaseAsset * FileInfo>) =

stagedZips |> List.iter (fun (asset, path) ->

injectPluginFiles asset path "netstandard2.1" "net"
// We inject net8.0 as the minimum supported TFM version
// Previously we used netstandard2.1, but this causes issues with adding a handler for the HttpClient
// used for OTLP export as the SDK prefers the `Send` rather than `SendAsync` method which is only available in net8.0+ TFM
// Whe using netstandard2.1 there is no handler code to run so the default user agent is sent.
injectPluginFiles asset path "net8.0" "net"
if asset.Name.EndsWith "-windows.zip" then
injectPluginFiles asset path "net462" "netfx"

Expand All @@ -207,7 +211,8 @@ let stageArtifacts (assets:List<ReleaseAsset * FileInfo>) =
stagedZips

let redistribute (arguments:ParseResults<Build>) =
exec { run "dotnet" "build" "src/Elastic.OpenTelemetry.AutoInstrumentation/Elastic.OpenTelemetry.AutoInstrumentation.csproj" "-f" "netstandard2.1" "-c" "release" }
// We build net8.0 as the minimum supported TFM version - See above for details
exec { run "dotnet" "build" "src/Elastic.OpenTelemetry.AutoInstrumentation/Elastic.OpenTelemetry.AutoInstrumentation.csproj" "-f" "net8.0" "-c" "release" }
exec { run "dotnet" "build" "src/Elastic.OpenTelemetry.AutoInstrumentation/Elastic.OpenTelemetry.AutoInstrumentation.csproj" "-f" "net462" "-c" "release" }
let assets = downloadArtifacts arguments
printfn ""
Expand Down
4 changes: 2 additions & 2 deletions examples/Example.AutoInstrumentation/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
ARG OTEL_VERSION=1.12.0
FROM mcr.microsoft.com/dotnet/runtime:9.0 AS base
ARG OTEL_VERSION=1.13.0
FROM mcr.microsoft.com/dotnet/runtime:10.0 AS base
ARG TARGETPLATFORM
ARG TARGETARCH
ARG TARGETVARIANT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
Expand Down
12 changes: 2 additions & 10 deletions examples/Example.AutoInstrumentation/distribution.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
ARG OTEL_VERSION=1.9.0
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG OTEL_VERSION=1.13.0
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0 AS build
ARG TARGETPLATFORM
ARG TARGETARCH
ARG TARGETVARIANT
Expand All @@ -8,27 +8,19 @@ ENV _PROJECT="Example.AutoInstrumentation"
ENV _PROJECTPATH="${_PROJECT}/${_PROJECT}.csproj"

RUN apt-get update && apt-get install -y unzip

WORKDIR /work

COPY ["examples/${_PROJECTPATH}", "examples/${_PROJECT}/"]
RUN dotnet restore -a $TARGETARCH "examples/${_PROJECT}"

COPY .git .git
COPY examples/${_PROJECT} examples/${_PROJECT}
WORKDIR "/work/examples/${_PROJECT}"
RUN dotnet publish "${_PROJECT}.csproj" -c Release -a $TARGETARCH --no-restore -o /app/example


FROM build AS final

COPY ".artifacts/elastic-distribution" /distro/elastic

COPY --from=build /app/example /app/example

ENV OTEL_DOTNET_AUTO_HOME="/app/otel"
# Use already downloaded release assets (call ./build.sh redistribute locally if you run this dockerfile manually)
RUN DOWNLOAD_DIR="/distro/elastic" sh /distro/elastic/elastic-dotnet-auto-install.sh

ENV OTEL_LOG_LEVEL=debug
ENTRYPOINT ["sh", "/app/otel/instrument.sh", "dotnet", "/app/example/Example.AutoInstrumentation.dll"]
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using Elastic.OpenTelemetry.Diagnostics;
using Elastic.OpenTelemetry.Exporters;
using Elastic.OpenTelemetry.Resources;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using OpenTelemetry;
using OpenTelemetry.Exporter;
Expand All @@ -31,6 +30,17 @@ public class AutoInstrumentationPlugin
/// <inheritdoc cref="AutoInstrumentationPlugin"/>
public AutoInstrumentationPlugin() => _components = ElasticOpenTelemetry.Bootstrap(SdkActivationMethod.AutoInstrumentation);

/// <summary>
/// Configure Resource Builder for Logs, Metrics and Traces
/// </summary>
/// <param name="builder"><see cref="ResourceBuilder"/> to configure</param>
/// <returns>Returns <see cref="ResourceBuilder"/> for chaining.</returns>
public ResourceBuilder ConfigureResource(ResourceBuilder builder)
{
builder.WithElasticDefaultsCore(_components, null, null);
return builder;
}

/// <summary>
/// To configure tracing SDK before Auto Instrumentation configured SDK.
/// </summary>
Expand All @@ -40,10 +50,7 @@ public TracerProviderBuilder BeforeConfigureTracerProvider(TracerProviderBuilder

try
{
builder.ConfigureResource(r => r.WithElasticDefaultsCore(_components, null, null));

builder.ConfigureServices(sc => sc.Configure<OtlpExporterOptions>(OtlpExporterDefaults.OtlpExporterOptions));
logger.LogConfiguredOtlpExporterOptions();
logger.LogInformation("Configuring Elastic Distribution of OpenTelemetry .NET defaults for tracing auto-instrumentation.");

ElasticTracerProviderBuilderExtensions.AddActivitySourceWithLogging(builder, logger, "Elastic.Transport", "<n/a>");
ElasticTracerProviderBuilderExtensions.AddElasticProcessorsCore(builder, null, _components, null);
Expand All @@ -62,42 +69,43 @@ public TracerProviderBuilder BeforeConfigureTracerProvider(TracerProviderBuilder
}

/// <summary>
/// To configure metrics SDK before Auto Instrumentation configured SDK.
/// Configure traces OTLP exporter options.
/// </summary>
public MeterProviderBuilder BeforeConfigureMeterProvider(MeterProviderBuilder builder)
{
var logger = _components.Logger;

try
{
builder.ConfigureResource(r => r.WithElasticDefaultsCore(_components, null, null));

builder.ConfigureServices(sc => sc
.Configure<OtlpExporterOptions>(OtlpExporterDefaults.OtlpExporterOptions)
.Configure<MetricReaderOptions>(o => o.TemporalityPreference = MetricReaderTemporalityPreference.Delta));
logger.LogConfiguredOtlpExporterOptions();
/// <param name="options">Otlp options.</param>
public void ConfigureTracesOptions(OtlpExporterOptions options) => ConfigureOtlpExporter(options, "traces");

logger.LogConfiguredSignalProvider(nameof(Signals.Metrics), nameof(MeterProviderBuilder), "<n/a>");

return builder;
}
catch (Exception ex)
{
logger.LogError(new EventId(521, "AutoInstrumentationTracerFailure"), ex,
"Failed to register EDOT defaults for metrics auto-instrumentation to the MeterProviderBuilder.");
}
/// <summary>
/// Configure metrics OTLP exporter options
/// </summary>
/// <param name="options">Otlp options</param>
public void ConfigureMetricsOptions(OtlpExporterOptions options) => ConfigureOtlpExporter(options, "metrics");

return builder;
/// <summary>
/// Configure metrics OTLP exporter options
/// </summary>
/// <param name="options">Otlp options</param>
public void ConfigureMetricsOptions(MetricReaderOptions options)
{
var logger = _components.Logger;
options.TemporalityPreference = MetricReaderTemporalityPreference.Delta;
logger.LogInformation("Configured Elastic Distribution of OpenTelemetry .NET defaults for logging auto-instrumentation.");
}

/// <summary>
/// To configure logs SDK (the method name is the same as for other logs options).
/// Configure logging OTLP exporter options.
/// </summary>
public void ConfigureLogsOptions(OpenTelemetryLoggerOptions options) => options.WithElasticDefaults(_components.Logger);
/// <param name="options">Otlp options.</param>
public void ConfigureLogsOptions(OtlpExporterOptions options) => ConfigureOtlpExporter(options, "logs");

/// <summary>
/// To configure Resource.
/// To configure logs SDK (the method name is the same as for other logs options).
/// </summary>
public ResourceBuilder ConfigureResource(ResourceBuilder builder) =>
builder.WithElasticDefaultsCore(_components, null, null);
public void ConfigureLogsOptions(OpenTelemetryLoggerOptions options) => options.WithElasticDefaults(_components.Logger);

private void ConfigureOtlpExporter(OtlpExporterOptions options, string signal)
{
var logger = _components.Logger;
options.ConfigureElasticUserAgent();
logger.LogConfiguredOtlpExporterOptions(signal);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Attempting to detect the runtime and use the native profiler from corresponding

case $(uname -m) in
x86_64) ARCHITECTURE="x64" ;;
aarch64) ARCHITECTURE="arm64" ;;
aarch64|arm64) ARCHITECTURE="arm64" ;;
esac

case "$ARCHITECTURE" in
Expand Down
2 changes: 1 addition & 1 deletion src/Elastic.OpenTelemetry.Core/Diagnostics/FileLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public FileLogger(CompositeElasticOpenTelemetryOptions options)
var process = Process.GetCurrentProcess();

// This naming resembles the naming structure for OpenTelemetry log files.
var logFileName = $"edot-dotnet-{process.Id}-{process.ProcessName}-{DateTimeOffset.UtcNow:yyyyMMdd-hhMMssfffZ}.log";
var logFileName = $"edot-dotnet-{process.Id}-{process.ProcessName}-{DateTimeOffset.UtcNow:yyyyMMdd-HHmmssfffZ}.log";
var logDirectory = options.LogDirectory;

LogFilePath = Path.Combine(logDirectory, logFileName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ internal static partial class LoggerMessages
"times across all {Target} instances.")]
public static partial void LogWithElasticDefaultsCallCount(this ILogger logger, int callCount, string target);

[LoggerMessage(EventId = 12, EventName = "ConfiguredOtlpExporterOptions", Level = LogLevel.Debug, Message = "The `OtlpExporterOptions` have been configured to use the" +
[LoggerMessage(EventId = 12, EventName = "ConfiguredOtlpExporterOptions", Level = LogLevel.Debug, Message = "The `OtlpExporterOptions` for {Signal} have been configured to use the" +
"`ElasticUserAgentHandler` to set the EDOT .NET user agent.")]
public static partial void LogConfiguredOtlpExporterOptions(this ILogger logger);
public static partial void LogConfiguredOtlpExporterOptions(this ILogger logger, string signal);



Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,51 @@
#if NETFRAMEWORK
using System.Net.Http;
#endif
#pragma warning disable IDE0130 // Namespace does not match folder structure

using Microsoft.Extensions.Logging;

namespace Elastic.OpenTelemetry.Exporters;
#pragma warning restore IDE0130 // Namespace does not match folder structure

internal class ElasticUserAgentHandler(string userAgent) : HttpClientHandler
/// <summary>
/// Sets a custom User-Agent header for outgoing HTTP requests.
/// Uses DelegatingHandler with SocketsHttpHandler for .NET, and HttpClientHandler for .NET Framework.
/// </summary>
internal class ElasticUserAgentHandler
#if NET
: DelegatingHandler
#else
: HttpClientHandler
#endif
{
private readonly string _userAgent = userAgent;
private readonly string _userAgent;

#if NET
public ElasticUserAgentHandler(string userAgent) : base(new SocketsHttpHandler()) => _userAgent = userAgent;
#else
public ElasticUserAgentHandler(string userAgent) => _userAgent = userAgent;
#endif

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
PrepareRequest(request);
return base.SendAsync(request, cancellationToken);
}

#if NET
// This method is only available in .NET targets and is crucial to override for synchronous calls.
// The upstream SDK prefers synchronous calls in many scenarios and without this override, the User-Agent header would not be set correctly.
protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken)
{
PrepareRequest(request);
return base.Send(request, cancellationToken);
}
#endif

private void PrepareRequest(HttpRequestMessage request)
{
request.Headers.Remove("User-Agent");
request.Headers.Add("User-Agent", _userAgent);

return base.SendAsync(request, cancellationToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,24 @@
using System.Net.Http;
#endif

#pragma warning disable IDE0130 // Namespace does not match folder structure
namespace Elastic.OpenTelemetry.Exporters;
#pragma warning restore IDE0130 // Namespace does not match folder structure

internal static class OtlpExporterDefaults
{
internal static readonly HttpMessageHandler Handler = new ElasticUserAgentHandler($"elastic-otlp-dotnet/{VersionHelper.InformationalVersion}");
private static string UserAgent => $"elastic-otlp-dotnet/{VersionHelper.InformationalVersion}";

public static void OtlpExporterOptions(OtlpExporterOptions options) =>
internal static void OtlpExporterOptions(OtlpExporterOptions options) =>
options.HttpClientFactory = () =>
{
var client = new HttpClient(Handler);
var client = new HttpClient(new ElasticUserAgentHandler(UserAgent));
return client;
};

internal static OtlpExporterOptions ConfigureElasticUserAgent(this OtlpExporterOptions options)
{
OtlpExporterOptions(options);
return options;
}
}
Loading