Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add diagnostics, file logging and ILogger integration #34

Merged
merged 6 commits into from
Feb 13, 2024
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
1 change: 1 addition & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<UseArtifactsOutput>true</UseArtifactsOutput>
<SolutionRoot>$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), build.bat))</SolutionRoot>
<ArtifactsPath>$(MSBuildThisFileDirectory).artifacts</ArtifactsPath>
<ExposedPublicKey>002400000480000094000000060200000024000052534131000400000100010015b0fa59d868c7f3ea2ae67567b19e102465745f01b430a38a42b92fd41a0f5869bec1f2b33b589d78662af432fe6b789ef72d4738f7b1a86264d7aeb5185ed8995b2bb104e7c5c58845f1a618be829e410fa34a6bd7d714ece191ed68a66333a83ae7456ee32e9aeb54bc1d7410ae8c344367257e9001abb5e96ce1f1d97696</ExposedPublicKey>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MinVer" Version="4.3.0" PrivateAssets="all" />
Expand Down
5 changes: 3 additions & 2 deletions examples/Example.Elastic.OpenTelemetry.Worker/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
using Example.Elastic.OpenTelemetry.Worker;

var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();

builder.Services.AddElasticOpenTelemetry("CustomActivitySource");
builder.EnableElasticOpenTelemetry("CustomActivitySource");

builder.Services.AddHostedService<Worker>();

var host = builder.Build();
host.Run();
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
"Microsoft.Hosting.Lifetime": "Information",
"Elastic": "Trace"
}
}
}
218 changes: 133 additions & 85 deletions src/Elastic.OpenTelemetry/Agent.cs
Original file line number Diff line number Diff line change
@@ -1,97 +1,145 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// 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 OpenTelemetry;
using System.Reflection;
using Microsoft.Extensions.Logging;

using static Elastic.OpenTelemetry.Diagnostics.ElasticOpenTelemetryDiagnosticSource;

namespace Elastic.OpenTelemetry;

/// <summary>
/// Supports building and accessing an <see cref="IAgent"/> which collects and ships observability signals.
/// </summary>
public static class Agent
public static partial class Agent
{
private static readonly object Lock = new();
private static IAgent? _current;

static Agent()
{
var assemblyInformationalVersion = typeof(Agent).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
InformationalVersion = ParseAssemblyInformationalVersion(assemblyInformationalVersion);
}

/// <summary>
/// Returns the singleton <see cref="IAgent"/> instance.
/// </summary>
/// <remarks>
/// If an instance is not already initialized, this will create and return a
/// default <see cref="IAgent"/> configured with recommended Elastic defaults.
/// </remarks>
public static IAgent Current
{
get
{
if (_current is not null) return _current;

lock (Lock)
{
// disable to satisfy double check lock pattern analyzer
// ReSharper disable once InvertIf
if (_current is null)
{
var agent = new AgentBuilder().Build();
_current = agent;
}
return _current;
}
}
}

internal static string InformationalVersion { get; }

/// <summary>
/// Builds an <see cref="IAgent"/>.
/// </summary>
/// <returns>An <see cref="IAgent"/> instance.</returns>
/// <exception cref="Exception">
/// An exception will be thrown if <see cref="Build"/>
/// is called more than once during the lifetime of an application.
/// </exception>
public static IAgent Build(Action<AgentBuilder>? configuration = null)
{
if (_current != null)
throw new Exception($"{nameof(Agent)}.{nameof(Build)} called twice or after {nameof(Agent)}.{nameof(Current)} was accessed.");

lock (Lock)
{
if (_current != null)
throw new Exception($"{nameof(Agent)}.{nameof(Build)} called twice or after {nameof(Agent)}.{nameof(Current)} was accessed.");

var agentBuilder = new AgentBuilder();
configuration?.Invoke(agentBuilder);
var agent = agentBuilder.Build();
_current = agent;
return _current;
}
}

internal static string ParseAssemblyInformationalVersion(string? informationalVersion)
{
if (string.IsNullOrWhiteSpace(informationalVersion))
{
informationalVersion = "1.0.0";
}

/*
* InformationalVersion will be in the following format:
* {majorVersion}.{minorVersion}.{patchVersion}.{pre-release label}.{pre-release version}.{gitHeight}+{Git SHA of current commit}
* Ex: 1.5.0-alpha.1.40+807f703e1b4d9874a92bd86d9f2d4ebe5b5d52e4
* The following parts are optional: pre-release label, pre-release version, git height, Git SHA of current commit
*/

var indexOfPlusSign = informationalVersion!.IndexOf('+');
return indexOfPlusSign > 0
? informationalVersion[..indexOfPlusSign]
: informationalVersion;
}
private static readonly object Lock = new();
private static IAgent? CurrentAgent;

static Agent()
{
var assemblyInformationalVersion = typeof(Agent).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
InformationalVersion = ParseAssemblyInformationalVersion(assemblyInformationalVersion);
}

/// <summary>
/// Returns the singleton <see cref="IAgent"/> instance.
/// </summary>
/// <remarks>
/// If an instance is not already initialized, this will create and return a
/// default <see cref="IAgent"/> configured with recommended Elastic defaults.
/// </remarks>
public static IAgent Current
{
get
{
if (CurrentAgent is not null)
return CurrentAgent;

lock (Lock)
{
// disable to satisfy double check lock pattern analyzer
// ReSharper disable once InvertIf
if (CurrentAgent is null)
{
var agent = new AgentBuilder().Build();
CurrentAgent = agent;
}
return CurrentAgent;
}
}
}

internal static string InformationalVersion { get; }

/// <summary>
/// Builds an <see cref="IAgent"/>.
/// </summary>
/// <returns>An <see cref="IAgent"/> instance.</returns>
/// <exception cref="Exception">
/// An exception will be thrown if <see cref="Build"/>
/// is called more than once during the lifetime of an application.
/// </exception>
public static IAgent Build(Action<AgentBuilder>? configuration = null)
{
CheckCurrent();

lock (Lock)
{
CheckCurrent();
var agentBuilder = new AgentBuilder();
configuration?.Invoke(agentBuilder);
var agent = agentBuilder.Build();
CurrentAgent = agent;
return CurrentAgent;
}

static void CheckCurrent()
{
if (CurrentAgent is not null)
{
Log(AgentBuildCalledMultipleTimesEvent);
throw new Exception();
}
}
}

internal const string BuildErrorMessage = $"{nameof(Agent)}.{nameof(Build)} called twice or after " +
$"{nameof(Agent)}.{nameof(Current)} was accessed.";

internal const string SetAgentErrorMessage = $"{nameof(Agent)}.{nameof(SetAgent)} called twice" +
$"or after {nameof(Agent)}.{nameof(Build)} or after {nameof(Agent)}.{nameof(Current)} was accessed.";

[LoggerMessage(EventId = 1, Level = LogLevel.Error, Message = SetAgentErrorMessage)]
internal static partial void SetAgentError(this ILogger logger);

/// <summary>
/// TODO
/// </summary>
/// <param name="agent"></param>
/// <param name="logger"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
internal static IAgent SetAgent(IAgent agent, ILogger logger)
{
CheckCurrent(logger);

lock (Lock)
{
CheckCurrent(logger);
logger.LogInformation($"Setting {nameof(CurrentAgent)}.");
CurrentAgent = agent;
return CurrentAgent;
}

static void CheckCurrent(ILogger logger)
{
if (CurrentAgent is not null)
{
Log(AgentSetAgentCalledMultipleTimesEvent);
logger.SetAgentError();
throw new Exception(SetAgentErrorMessage);
}
}
}

internal static string ParseAssemblyInformationalVersion(string? informationalVersion)
{
if (string.IsNullOrWhiteSpace(informationalVersion))
{
informationalVersion = "1.0.0";
}

/*
* InformationalVersion will be in the following format:
* {majorVersion}.{minorVersion}.{patchVersion}.{pre-release label}.{pre-release version}.{gitHeight}+{Git SHA of current commit}
* Ex: 1.5.0-alpha.1.40+807f703e1b4d9874a92bd86d9f2d4ebe5b5d52e4
* The following parts are optional: pre-release label, pre-release version, git height, Git SHA of current commit
*/

var indexOfPlusSign = informationalVersion!.IndexOf('+');
return indexOfPlusSign > 0
? informationalVersion[..indexOfPlusSign]
: informationalVersion;
}
}
Loading
Loading