diff --git a/src/Elastic.OpenTelemetry/Configuration/ElasticOpenTelemetryOptions.cs b/src/Elastic.OpenTelemetry/Configuration/ElasticOpenTelemetryOptions.cs
index 5f9a2ef..507b177 100644
--- a/src/Elastic.OpenTelemetry/Configuration/ElasticOpenTelemetryOptions.cs
+++ b/src/Elastic.OpenTelemetry/Configuration/ElasticOpenTelemetryOptions.cs
@@ -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;
@@ -40,6 +41,8 @@ public class ElasticOpenTelemetryOptions
private string? _enabledElasticDefaults;
private ConfigSource _enabledElasticDefaultsSource = ConfigSource.Default;
+ private string? _loggingSectionLogLevel;
+
///
/// Creates a new instance of with properties
/// bound from environment variables.
@@ -62,14 +65,36 @@ public ElasticOpenTelemetryOptions()
///
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($"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("Logging:LogLevel:Default");
+
+ if (!string.IsNullOrEmpty(_loggingSectionLogLevel) && _fileLogLevel is null)
+ {
+ _fileLogLevel = _loggingSectionLogLevel;
+ _fileLogLevelSource = ConfigSource.IConfiguration;
+ }
+ }
}
///
@@ -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);
@@ -173,7 +200,7 @@ private static void SetFromConfiguration(IConfiguration configuration, string
{
if (field is null)
{
- var logFileDirectory = configuration?.GetValue($"{ConfigurationSection}:{key}");
+ var logFileDirectory = configuration.GetValue($"{ConfigurationSection}:{key}");
var (success, value) = parser(logFileDirectory);
diff --git a/src/Elastic.OpenTelemetry/Diagnostics/Logging/CompositeLogger.cs b/src/Elastic.OpenTelemetry/Diagnostics/Logging/CompositeLogger.cs
index ce57f86..83ec309 100644
--- a/src/Elastic.OpenTelemetry/Diagnostics/Logging/CompositeLogger.cs
+++ b/src/Elastic.OpenTelemetry/Diagnostics/Logging/CompositeLogger.cs
@@ -16,6 +16,8 @@ namespace Elastic.OpenTelemetry.Diagnostics.Logging;
///
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;
diff --git a/src/Elastic.OpenTelemetry/Diagnostics/LoggingEventListener.cs b/src/Elastic.OpenTelemetry/Diagnostics/LoggingEventListener.cs
index 20a85cd..6dff1b8 100644
--- a/src/Elastic.OpenTelemetry/Diagnostics/LoggingEventListener.cs
+++ b/src/Elastic.OpenTelemetry/Diagnostics/LoggingEventListener.cs
@@ -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;
@@ -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
{
diff --git a/src/Elastic.OpenTelemetry/ElasticOpenTelemetryBuilder.cs b/src/Elastic.OpenTelemetry/ElasticOpenTelemetryBuilder.cs
index 33b2ecf..3fd7ef6 100644
--- a/src/Elastic.OpenTelemetry/ElasticOpenTelemetryBuilder.cs
+++ b/src/Elastic.OpenTelemetry/ElasticOpenTelemetryBuilder.cs
@@ -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));
diff --git a/src/Elastic.OpenTelemetry/Hosting/ElasticOpenTelemetryService.cs b/src/Elastic.OpenTelemetry/Hosting/ElasticOpenTelemetryService.cs
index c8c0c0c..203b1f4 100644
--- a/src/Elastic.OpenTelemetry/Hosting/ElasticOpenTelemetryService.cs
+++ b/src/Elastic.OpenTelemetry/Hosting/ElasticOpenTelemetryService.cs
@@ -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;
@@ -16,7 +17,7 @@ internal sealed class ElasticOpenTelemetryService(IServiceProvider serviceProvid
public Task StartingAsync(CancellationToken cancellationToken)
{
var loggerFactory = serviceProvider.GetService();
- var logger = loggerFactory?.CreateLogger($"{nameof(Elastic)}.{nameof(OpenTelemetry)}");
+ var logger = loggerFactory?.CreateLogger(CompositeLogger.LogCategory);
_lifeTime = serviceProvider.GetRequiredService().Build(logger, serviceProvider);
diff --git a/tests/Elastic.OpenTelemetry.Tests/Configuration/ElasticOpenTelemetryOptionsTests.cs b/tests/Elastic.OpenTelemetry.Tests/Configuration/ElasticOpenTelemetryOptionsTests.cs
index e053460..a351fd6 100644
--- a/tests/Elastic.OpenTelemetry.Tests/Configuration/ElasticOpenTelemetryOptionsTests.cs
+++ b/tests/Elastic.OpenTelemetry.Tests/Configuration/ElasticOpenTelemetryOptionsTests.cs
@@ -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";
@@ -109,6 +110,12 @@ public void ConfigurationCtor_LoadsConfigurationFromIConfiguration()
var json = $$"""
{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Elastic.OpenTelemetry": "{{loggingSectionLogLevel}}"
+ }
+ },
"Elastic": {
"OpenTelemetry": {
"FileLogDirectory": "C:\\Temp",
@@ -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);