Skip to content

Commit

Permalink
Ensure we fallback to logging section and event listener respects value
Browse files Browse the repository at this point in the history
  • Loading branch information
stevejgordon committed May 2, 2024
1 parent f3a6c82 commit edc335c
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// 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 Elastic.OpenTelemetry.Diagnostics.Logging;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

Expand Down Expand Up @@ -40,6 +41,8 @@ public class ElasticOpenTelemetryOptions
private string? _enabledElasticDefaults;
private ConfigSource _enabledElasticDefaultsSource = ConfigSource.Default;

private string? _loggingSectionLogLevel;

/// <summary>
/// Creates a new instance of <see cref="ElasticOpenTelemetryOptions"/> with properties
/// bound from environment variables.
Expand All @@ -62,14 +65,36 @@ public ElasticOpenTelemetryOptions()
/// </summary>
internal ElasticOpenTelemetryOptions(IConfiguration configuration) : this()
{
SetFromConfiguration(configuration, FileLogDirectoryConfigPropertyName, ref _fileLogDirectory,
ref _fileLogDirectorySource, StringParser);
SetFromConfiguration(configuration, FileLogLevelConfigPropertyName, ref _fileLogLevel,
ref _fileLogLevelSource, StringParser);
SetFromConfiguration(configuration, SkipOtlpExporterConfigPropertyName, ref _skipOtlpExporter,
ref _skipOtlpExporterSource, BoolParser);
SetFromConfiguration(configuration, EnabledElasticDefaultsConfigPropertyName, ref _enabledElasticDefaults,
ref _enabledElasticDefaultsSource, StringParser);
if (configuration is not null)
{
SetFromConfiguration(configuration, FileLogDirectoryConfigPropertyName, ref _fileLogDirectory,
ref _fileLogDirectorySource, StringParser);
SetFromConfiguration(configuration, FileLogLevelConfigPropertyName, ref _fileLogLevel,
ref _fileLogLevelSource, StringParser);
SetFromConfiguration(configuration, SkipOtlpExporterConfigPropertyName, ref _skipOtlpExporter,
ref _skipOtlpExporterSource, BoolParser);
SetFromConfiguration(configuration, EnabledElasticDefaultsConfigPropertyName, ref _enabledElasticDefaults,
ref _enabledElasticDefaultsSource, StringParser);

BindFromLoggingSection(configuration);
}

void BindFromLoggingSection(IConfiguration configuration)
{
// This will be used as a fallback if a more specific configuration is not provided.
// We also store the logging level to use it within the logging event listener to determine the most verbose level to subscribe to.
_loggingSectionLogLevel = configuration.GetValue<string>($"Logging:LogLevel:{CompositeLogger.LogCategory}");

// Fall back to the default logging level if the specific category is not configured.
if (string.IsNullOrEmpty(_loggingSectionLogLevel))
_loggingSectionLogLevel = configuration.GetValue<string>("Logging:LogLevel:Default");

if (!string.IsNullOrEmpty(_loggingSectionLogLevel) && _fileLogLevel is null)
{
_fileLogLevel = _loggingSectionLogLevel;
_fileLogLevelSource = ConfigSource.IConfiguration;
}
}
}

/// <summary>
Expand Down Expand Up @@ -151,6 +176,8 @@ public string EnableElasticDefaults
}
}

internal string? LoggingSectionLogLevel => _loggingSectionLogLevel;

internal EnabledElasticDefaults EnabledDefaults => _elasticDefaults ?? GetEnabledElasticDefaults();

private static (bool, string) StringParser(string? s) => !string.IsNullOrEmpty(s) ? (true, s) : (false, string.Empty);
Expand All @@ -173,7 +200,7 @@ private static void SetFromConfiguration<T>(IConfiguration configuration, string
{
if (field is null)
{
var logFileDirectory = configuration?.GetValue<string>($"{ConfigurationSection}:{key}");
var logFileDirectory = configuration.GetValue<string>($"{ConfigurationSection}:{key}");

var (success, value) = parser(logFileDirectory);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ namespace Elastic.OpenTelemetry.Diagnostics.Logging;
/// </remarks>
internal sealed class CompositeLogger(ElasticOpenTelemetryBuilderOptions options) : IDisposable, IAsyncDisposable, ILogger
{
public const string LogCategory = "Elastic.OpenTelemetry";

public FileLogger FileLogger { get; } = new(options.DistroOptions);

private ILogger? _additionalLogger = options.Logger;
Expand Down
17 changes: 14 additions & 3 deletions src/Elastic.OpenTelemetry/Diagnostics/LoggingEventListener.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Text.RegularExpressions;
using Elastic.OpenTelemetry.Diagnostics.Logging;
using Microsoft.Extensions.Logging;
using Elastic.OpenTelemetry.Configuration;

namespace Elastic.OpenTelemetry.Diagnostics;

Expand All @@ -26,15 +27,25 @@ class LoggingEventListener : EventListener, IAsyncDisposable
[GeneratedRegex(TraceParentRegularExpressionString)]
private static partial Regex TraceParentRegex();
#else
private static readonly Regex _traceParentRegex = new Regex(TraceParentRegularExpressionString);
private static readonly Regex _traceParentRegex = new(TraceParentRegularExpressionString);
private static Regex TraceParentRegex() => _traceParentRegex;
#endif

public LoggingEventListener(ILogger logger)
public LoggingEventListener(ILogger logger, ElasticOpenTelemetryOptions options)
{
_logger = logger;

var eventLevel = AgentLoggingHelpers.GetElasticOtelLogLevelFromEnvironmentVariables();
// When both a file log level and a logging section log level are provided, the more verbose of the two is used.
// This insures we subscribes to the lowest level of events needed.
// The specific loggers will then determine if they should log the event based on their own log level.
var eventLevel = LogLevelHelpers.ToLogLevel(options.FileLogLevel);
if (!string.IsNullOrEmpty(options.LoggingSectionLogLevel))
{
var logLevel = LogLevelHelpers.ToLogLevel(options.LoggingSectionLogLevel);

if (logLevel < eventLevel)
eventLevel = logLevel;
}

_eventLevel = eventLevel switch
{
Expand Down
2 changes: 1 addition & 1 deletion src/Elastic.OpenTelemetry/ElasticOpenTelemetryBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public ElasticOpenTelemetryBuilder(ElasticOpenTelemetryBuilderOptions options)
Logger = new CompositeLogger(options);

// Enables logging of OpenTelemetry-SDK event source events
EventListener = new LoggingEventListener(Logger);
EventListener = new LoggingEventListener(Logger, options.DistroOptions);

Logger.LogAgentPreamble();
Logger.LogElasticOpenTelemetryBuilderInitialized(Environment.NewLine, new StackTrace(true));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// 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 Elastic.OpenTelemetry.Diagnostics.Logging;
using Elastic.OpenTelemetry.Extensions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
Expand All @@ -16,7 +17,7 @@ internal sealed class ElasticOpenTelemetryService(IServiceProvider serviceProvid
public Task StartingAsync(CancellationToken cancellationToken)
{
var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
var logger = loggerFactory?.CreateLogger($"{nameof(Elastic)}.{nameof(OpenTelemetry)}");
var logger = loggerFactory?.CreateLogger(CompositeLogger.LogCategory);

_lifeTime = serviceProvider.GetRequiredService<ElasticOpenTelemetryBuilder>().Build(logger, serviceProvider);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ public void DefaultCtor_LoadsConfigurationFromEnvironmentVariables()
[Fact]
public void ConfigurationCtor_LoadsConfigurationFromIConfiguration()
{
const string loggingSectionLogLevel = "Warning";
const string fileLogLevel = "Critical";
const string enabledElasticDefaults = "None";

Expand All @@ -109,6 +110,12 @@ public void ConfigurationCtor_LoadsConfigurationFromIConfiguration()

var json = $$"""
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Elastic.OpenTelemetry": "{{loggingSectionLogLevel}}"
}
},
"Elastic": {
"OpenTelemetry": {
"FileLogDirectory": "C:\\Temp",
Expand All @@ -131,6 +138,118 @@ public void ConfigurationCtor_LoadsConfigurationFromIConfiguration()
sut.EnableElasticDefaults.Should().Be(enabledElasticDefaults);
sut.EnabledDefaults.Should().Be(EnabledElasticDefaults.None);
sut.SkipOtlpExporter.Should().Be(true);
sut.LoggingSectionLogLevel.Should().Be(loggingSectionLogLevel);

var logger = new TestLogger(_output);

sut.LogConfigSources(logger);

logger.Messages.Count.Should().Be(4);
foreach (var message in logger.Messages)
{
message.Should().EndWith("from [IConfiguration]");
}

ResetEnvironmentVariables();
}

[Fact]
public void ConfigurationCtor_LoadsConfigurationFromIConfiguration_AndFallsBackToLoggingSection_WhenAvailable()
{
const string loggingSectionLogLevel = "Warning";
const string enabledElasticDefaults = "None";

// Remove all env vars
Environment.SetEnvironmentVariable(EnvironmentVariables.ElasticOtelFileLogDirectoryEnvironmentVariable, null);
Environment.SetEnvironmentVariable(EnvironmentVariables.ElasticOtelFileLogLevelEnvironmentVariable, null);
Environment.SetEnvironmentVariable(EnvironmentVariables.ElasticOtelEnableElasticDefaults, null);
Environment.SetEnvironmentVariable(EnvironmentVariables.ElasticOtelSkipOtlpExporter, null);

var json = $$"""
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Elastic.OpenTelemetry": "{{loggingSectionLogLevel}}"
}
},
"Elastic": {
"OpenTelemetry": {
"FileLogDirectory": "C:\\Temp",
"EnabledElasticDefaults": "{{enabledElasticDefaults}}",
"SkipOtlpExporter": true
}
}
}
""";

var config = new ConfigurationBuilder()
.AddJsonStream(new MemoryStream(Encoding.UTF8.GetBytes(json)))
.Build();

var sut = new ElasticOpenTelemetryOptions(config);

sut.FileLogDirectory.Should().Be(@"C:\Temp");
sut.FileLogLevel.Should().Be(loggingSectionLogLevel);
sut.EnableElasticDefaults.Should().Be(enabledElasticDefaults);
sut.EnabledDefaults.Should().Be(EnabledElasticDefaults.None);
sut.SkipOtlpExporter.Should().Be(true);
sut.LoggingSectionLogLevel.Should().Be(loggingSectionLogLevel);

var logger = new TestLogger(_output);

sut.LogConfigSources(logger);

logger.Messages.Count.Should().Be(4);
foreach (var message in logger.Messages)
{
message.Should().EndWith("from [IConfiguration]");
}

ResetEnvironmentVariables();
}

[Fact]
public void ConfigurationCtor_LoadsConfigurationFromIConfiguration_AndFallsBackToLoggingSectionDefault_WhenAvailable()
{
const string loggingSectionDefaultLogLevel = "Information";
const string enabledElasticDefaults = "None";

// Remove all env vars
Environment.SetEnvironmentVariable(EnvironmentVariables.ElasticOtelFileLogDirectoryEnvironmentVariable, null);
Environment.SetEnvironmentVariable(EnvironmentVariables.ElasticOtelFileLogLevelEnvironmentVariable, null);
Environment.SetEnvironmentVariable(EnvironmentVariables.ElasticOtelEnableElasticDefaults, null);
Environment.SetEnvironmentVariable(EnvironmentVariables.ElasticOtelSkipOtlpExporter, null);

var json = $$"""
{
"Logging": {
"LogLevel": {
"Default": "{{loggingSectionDefaultLogLevel}}"
}
},
"Elastic": {
"OpenTelemetry": {
"FileLogDirectory": "C:\\Temp",
"EnabledElasticDefaults": "{{enabledElasticDefaults}}",
"SkipOtlpExporter": true
}
}
}
""";

var config = new ConfigurationBuilder()
.AddJsonStream(new MemoryStream(Encoding.UTF8.GetBytes(json)))
.Build();

var sut = new ElasticOpenTelemetryOptions(config);

sut.FileLogDirectory.Should().Be(@"C:\Temp");
sut.FileLogLevel.Should().Be(loggingSectionDefaultLogLevel);
sut.EnableElasticDefaults.Should().Be(enabledElasticDefaults);
sut.EnabledDefaults.Should().Be(EnabledElasticDefaults.None);
sut.SkipOtlpExporter.Should().Be(true);
sut.LoggingSectionLogLevel.Should().Be(loggingSectionDefaultLogLevel);

var logger = new TestLogger(_output);

Expand Down

0 comments on commit edc335c

Please sign in to comment.