Skip to content

Commit

Permalink
[AzureMonitorOpenTelemetryDistro] AddAzureMonitorOpenTelemetry extens…
Browse files Browse the repository at this point in the history
…ion methods and AzureMonitorOpenTelemetryOptions (Azure#34198)

* Distro Public API

* PR feedback

* Update public API

* 4 Public APIs

* Update sdk/monitor/Azure.Monitor.OpenTelemetry/tests/Examples.AspNetCore/Examples.AspNetCore.csproj

* Fix build error for demo project.

* Fix error in sample

* Remove 461 packages

* 4 APIs in samples.

* Remove public api with parameters.

* PR feedback

* Update API

* Apply connectionstring from AzureMonitorOpenTelemetryOptions to AzureMonitorExporterOptions.

---------

Co-authored-by: Timothy Mothra <[email protected]>
  • Loading branch information
2 people authored and Harshit-Shrivastava committed Feb 24, 2023
1 parent 3433109 commit 6437341
Show file tree
Hide file tree
Showing 10 changed files with 337 additions and 0 deletions.
2 changes: 2 additions & 0 deletions eng/Packages.Data.props
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@

<!-- OpenTelemetry dependency approved for Azure.Monitor.OpenTelemetry.Exporter package only -->
<PackageReference Update="OpenTelemetry" Version="[1.4.0-rc.4]" Condition="'$(MSBuildProjectName)' == 'Azure.Monitor.OpenTelemetry.Exporter'" />
<PackageReference Update="OpenTelemetry.Extensions.Hosting" Version="1.4.0-rc.4" />
<PackageReference Update="OpenTelemetry.Instrumentation.AspNetCore" Version="1.0.0-rc9.13" />
<PackageReference Update="OpenTelemetry.Extensions.PersistentStorage" Version="1.0.0-beta.1" Condition="'$(MSBuildProjectName)' == 'Azure.Monitor.OpenTelemetry.Exporter'" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dependent Projects", "Depen
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Monitor.OpenTelemetry.Exporter", "..\Azure.Monitor.OpenTelemetry.Exporter\src\Azure.Monitor.OpenTelemetry.Exporter.csproj", "{4158B8A7-971D-4A57-B4C8-5077BFD10AE6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Monitor.OpenTelemetry.Demo", "tests\Azure.Monitor.OpenTelemetry.Demo\Azure.Monitor.OpenTelemetry.Demo.csproj", "{6F094CAA-F6BB-4A9C-9D1F-1165F0758C99}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -30,6 +32,10 @@ Global
{4158B8A7-971D-4A57-B4C8-5077BFD10AE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4158B8A7-971D-4A57-B4C8-5077BFD10AE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4158B8A7-971D-4A57-B4C8-5077BFD10AE6}.Release|Any CPU.Build.0 = Release|Any CPU
{6F094CAA-F6BB-4A9C-9D1F-1165F0758C99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6F094CAA-F6BB-4A9C-9D1F-1165F0758C99}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6F094CAA-F6BB-4A9C-9D1F-1165F0758C99}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6F094CAA-F6BB-4A9C-9D1F-1165F0758C99}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace Azure.Monitor.OpenTelemetry
{
public static partial class AzureMonitorOpenTelemetryExtensions
{
public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddAzureMonitorOpenTelemetry(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddAzureMonitorOpenTelemetry(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, Azure.Monitor.OpenTelemetry.AzureMonitorOpenTelemetryOptions options) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddAzureMonitorOpenTelemetry(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action<Azure.Monitor.OpenTelemetry.AzureMonitorOpenTelemetryOptions> configureAzureMonitorOpenTelemetry) { throw null; }
}
public partial class AzureMonitorOpenTelemetryOptions
{
public AzureMonitorOpenTelemetryOptions() { }
public Azure.Monitor.OpenTelemetry.Exporter.AzureMonitorExporterOptions AzureMonitorExporterOptions { get { throw null; } set { } }
public string ConnectionString { get { throw null; } set { } }
public bool EnableLogs { get { throw null; } set { } }
public bool EnableMetrics { get { throw null; } set { } }
public bool EnableTraces { get { throw null; } set { } }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
<IncludeOperationsSharedSource>true</IncludeOperationsSharedSource>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="OpenTelemetry.Extensions.Hosting" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\Azure.Monitor.OpenTelemetry.Exporter\src\Azure.Monitor.OpenTelemetry.Exporter.csproj" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using Microsoft.Extensions.DependencyInjection;

namespace Azure.Monitor.OpenTelemetry
{
/// <summary>
/// Extension methods for setting up Azure Monitor OpenTelemetry in an <see cref="IServiceCollection" />.
/// </summary>
public static class AzureMonitorOpenTelemetryExtensions
{
/// <summary>
/// Adds Azure Monitor OpenTelemetry into service collection.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
public static IServiceCollection AddAzureMonitorOpenTelemetry(this IServiceCollection services)
{
return services.AddAzureMonitorOpenTelemetry(o => o = new AzureMonitorOpenTelemetryOptions());
}

/// <summary>
/// Adds Azure Monitor OpenTelemetry into service collection.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
/// <param name="options">The <see cref="AzureMonitorOpenTelemetryOptions" /> instance for configuration.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
public static IServiceCollection AddAzureMonitorOpenTelemetry(this IServiceCollection services, AzureMonitorOpenTelemetryOptions options)
{
options ??= new AzureMonitorOpenTelemetryOptions();
return services.AddAzureMonitorOpenTelemetry(o => o.Clone(options));
}

/// <summary>
/// Adds Azure Monitor OpenTelemetry into service collection.
/// </summary>
/// <param name="services"><see cref="IServiceCollection"/>.</param>
/// <param name="configureAzureMonitorOpenTelemetry">Callback action for configuring <see cref="AzureMonitorOpenTelemetryOptions"/>.</param>
/// <returns><see cref="IServiceCollection"/>.</returns>
public static IServiceCollection AddAzureMonitorOpenTelemetry(this IServiceCollection services, Action<AzureMonitorOpenTelemetryOptions> configureAzureMonitorOpenTelemetry)
{
return AzureMonitorOpenTelemetryImplementations.AddAzureMonitorOpenTelemetryWithAction(services, configureAzureMonitorOpenTelemetry);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using Azure.Monitor.OpenTelemetry.Exporter;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;

namespace Azure.Monitor.OpenTelemetry
{
internal class AzureMonitorOpenTelemetryImplementations
{
internal static IServiceCollection AddAzureMonitorOpenTelemetryWithAction(IServiceCollection services, Action<AzureMonitorOpenTelemetryOptions> configureAzureMonitorOpenTelemetry)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}

if (configureAzureMonitorOpenTelemetry != null)
{
services.Configure(configureAzureMonitorOpenTelemetry);
}

var builder = services.AddOpenTelemetry();

builder.WithTracing(b => b
.AddAspNetCoreInstrumentation()
.AddAzureMonitorTraceExporter());

builder.WithMetrics(b => b
.AddAspNetCoreInstrumentation()
.AddAzureMonitorMetricExporter());

ServiceDescriptor? sdkTracerProviderServiceRegistration = null;
ServiceDescriptor? sdkMeterProviderServiceRegistration = null;

foreach (var service in services)
{
if (service.ServiceType == typeof(TracerProvider))
{
sdkTracerProviderServiceRegistration = service;
}
else if (service.ServiceType == typeof(MeterProvider))
{
sdkMeterProviderServiceRegistration = service;
}
}

if (sdkTracerProviderServiceRegistration?.ImplementationFactory == null ||
sdkMeterProviderServiceRegistration?.ImplementationFactory == null)
{
throw new InvalidOperationException("OpenTelemetry SDK has changed its registration mechanism.");
}

// We looped through the registered services so that we can take over
// the SDK registrations.

services.Remove(sdkTracerProviderServiceRegistration);
services.Remove(sdkMeterProviderServiceRegistration);

// Now we register our own services for TracerProvider & MeterProvider
// so that we can return no-op versions when it isn't enabled.

services.AddSingleton(sp =>
{
var options = sp.GetRequiredService<IOptionsMonitor<AzureMonitorOpenTelemetryOptions>>().Get("");
if (!options.EnableTraces)
{
return new NoopTracerProvider();
}
else
{
SetValueToExporterOptions(sp, options);
var sdkProviderWrapper = sp.GetRequiredService<SdkProviderWrapper>();
sdkProviderWrapper.SdkTracerProvider = (TracerProvider)sdkTracerProviderServiceRegistration.ImplementationFactory(sp);
return sdkProviderWrapper.SdkTracerProvider;
}
});

services.AddSingleton(sp =>
{
var options = sp.GetRequiredService<IOptionsMonitor<AzureMonitorOpenTelemetryOptions>>().Get("");
if (!options.EnableMetrics)
{
return new NoopMeterProvider();
}
else
{
SetValueToExporterOptions(sp, options);
var sdkProviderWrapper = sp.GetRequiredService<SdkProviderWrapper>();
sdkProviderWrapper.SdkMeterProvider = (MeterProvider)sdkMeterProviderServiceRegistration.ImplementationFactory(sp);
return sdkProviderWrapper.SdkMeterProvider;
}
});

// SdkProviderWrapper is here to make sure the SDK services get properly
// shutdown when the service provider is disposed.
services.AddSingleton<SdkProviderWrapper>();

return services;
}

private static void SetValueToExporterOptions(IServiceProvider sp, AzureMonitorOpenTelemetryOptions options)
{
var exporterOptions = sp.GetRequiredService<IOptionsMonitor<AzureMonitorExporterOptions>>().Get("");
var defaultOptions = new AzureMonitorExporterOptions();

if (ReferenceEquals(exporterOptions, defaultOptions))
{
exporterOptions.ConnectionString = options.AzureMonitorExporterOptions.ConnectionString;
exporterOptions.DisableOfflineStorage = options.AzureMonitorExporterOptions.DisableOfflineStorage;
exporterOptions.StorageDirectory = options.AzureMonitorExporterOptions.StorageDirectory;
}
else if (exporterOptions.ConnectionString == null)
{
exporterOptions.ConnectionString = options.AzureMonitorExporterOptions.ConnectionString;
}
}

private sealed class NoopTracerProvider : TracerProvider
{
}

private sealed class NoopMeterProvider : MeterProvider
{
}

private sealed class SdkProviderWrapper : IDisposable
{
public TracerProvider? SdkTracerProvider;
public MeterProvider? SdkMeterProvider;

public void Dispose()
{
this.SdkTracerProvider?.Dispose();
this.SdkMeterProvider?.Dispose();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#nullable disable

using System.Runtime.Serialization;
using Azure.Monitor.OpenTelemetry.Exporter;

namespace Azure.Monitor.OpenTelemetry
{
/// <summary>
/// Options that allow users to configure the Azure Monitor OpenTelemetry.
/// </summary>
public class AzureMonitorOpenTelemetryOptions
{
/// <summary>
/// Gets or sets a value of Azure Monitor Exporter Options.
/// </summary>
public AzureMonitorExporterOptions AzureMonitorExporterOptions { get; set; } = new AzureMonitorExporterOptions();

/// <summary>
/// The Connection String provides users with a single configuration setting to identify the Azure Monitor resource and endpoint.
/// </summary>
/// <remarks>
/// (https://docs.microsoft.com/azure/azure-monitor/app/sdk-connection-string).
/// </remarks>
public string ConnectionString
{
get
{
return AzureMonitorExporterOptions.ConnectionString;
}
set
{
AzureMonitorExporterOptions.ConnectionString = value;
}
}

/// <summary>
/// Gets or sets a value indicating whether Logs should be enabled.
/// </summary>
public bool EnableLogs { get; set; } = true;

/// <summary>
/// Gets or sets a value indicating whether Metrics should be enabled.
/// </summary>
public bool EnableMetrics { get; set; } = true;

/// <summary>
/// Gets or sets a value indicating whether Traces should be enabled.
/// </summary>
public bool EnableTraces { get; set; } = true;

internal AzureMonitorOpenTelemetryOptions Clone(AzureMonitorOpenTelemetryOptions options)
{
if (options != null)
{
AzureMonitorExporterOptions = options.AzureMonitorExporterOptions;
ConnectionString = options.ConnectionString;
EnableLogs = options.EnableLogs;
EnableMetrics = options.EnableMetrics;
EnableTraces = options.EnableTraces;
}

return this;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFrameworks>$(RequiredTargetFrameworks)</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Azure.Monitor.OpenTelemetry.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#if NET6_0_OR_GREATER
using System.Diagnostics;
using Azure.Monitor.OpenTelemetry;
using Microsoft.AspNetCore.Builder;

var builder = WebApplication.CreateBuilder(args);

// builder.Services.AddAzureMonitorOpenTelemetry();
builder.Services.AddAzureMonitorOpenTelemetry(o =>
{
o.ConnectionString = "InstrumentationKey=00000000-0000-0000-0000-000000000000";
});

var app = builder.Build();
app.MapGet("/", () => $"Hello World! OpenTelemetry Trace: {Activity.Current?.Id}");

app.Run();
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#if NET461

namespace Azure.Monitor.OpenTelemetry.Demo
{
public partial class Program
{
public static void Main(string[] args)
{
}
}
}

#endif

0 comments on commit 6437341

Please sign in to comment.