Skip to content

Commit edc335c

Browse files
committed
Ensure we fallback to logging section and event listener respects value
1 parent f3a6c82 commit edc335c

File tree

6 files changed

+174
-14
lines changed

6 files changed

+174
-14
lines changed

src/Elastic.OpenTelemetry/Configuration/ElasticOpenTelemetryOptions.cs

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
33
// See the LICENSE file in the project root for more information
44

5+
using Elastic.OpenTelemetry.Diagnostics.Logging;
56
using Microsoft.Extensions.Configuration;
67
using Microsoft.Extensions.Logging;
78

@@ -40,6 +41,8 @@ public class ElasticOpenTelemetryOptions
4041
private string? _enabledElasticDefaults;
4142
private ConfigSource _enabledElasticDefaultsSource = ConfigSource.Default;
4243

44+
private string? _loggingSectionLogLevel;
45+
4346
/// <summary>
4447
/// Creates a new instance of <see cref="ElasticOpenTelemetryOptions"/> with properties
4548
/// bound from environment variables.
@@ -62,14 +65,36 @@ public ElasticOpenTelemetryOptions()
6265
/// </summary>
6366
internal ElasticOpenTelemetryOptions(IConfiguration configuration) : this()
6467
{
65-
SetFromConfiguration(configuration, FileLogDirectoryConfigPropertyName, ref _fileLogDirectory,
66-
ref _fileLogDirectorySource, StringParser);
67-
SetFromConfiguration(configuration, FileLogLevelConfigPropertyName, ref _fileLogLevel,
68-
ref _fileLogLevelSource, StringParser);
69-
SetFromConfiguration(configuration, SkipOtlpExporterConfigPropertyName, ref _skipOtlpExporter,
70-
ref _skipOtlpExporterSource, BoolParser);
71-
SetFromConfiguration(configuration, EnabledElasticDefaultsConfigPropertyName, ref _enabledElasticDefaults,
72-
ref _enabledElasticDefaultsSource, StringParser);
68+
if (configuration is not null)
69+
{
70+
SetFromConfiguration(configuration, FileLogDirectoryConfigPropertyName, ref _fileLogDirectory,
71+
ref _fileLogDirectorySource, StringParser);
72+
SetFromConfiguration(configuration, FileLogLevelConfigPropertyName, ref _fileLogLevel,
73+
ref _fileLogLevelSource, StringParser);
74+
SetFromConfiguration(configuration, SkipOtlpExporterConfigPropertyName, ref _skipOtlpExporter,
75+
ref _skipOtlpExporterSource, BoolParser);
76+
SetFromConfiguration(configuration, EnabledElasticDefaultsConfigPropertyName, ref _enabledElasticDefaults,
77+
ref _enabledElasticDefaultsSource, StringParser);
78+
79+
BindFromLoggingSection(configuration);
80+
}
81+
82+
void BindFromLoggingSection(IConfiguration configuration)
83+
{
84+
// This will be used as a fallback if a more specific configuration is not provided.
85+
// We also store the logging level to use it within the logging event listener to determine the most verbose level to subscribe to.
86+
_loggingSectionLogLevel = configuration.GetValue<string>($"Logging:LogLevel:{CompositeLogger.LogCategory}");
87+
88+
// Fall back to the default logging level if the specific category is not configured.
89+
if (string.IsNullOrEmpty(_loggingSectionLogLevel))
90+
_loggingSectionLogLevel = configuration.GetValue<string>("Logging:LogLevel:Default");
91+
92+
if (!string.IsNullOrEmpty(_loggingSectionLogLevel) && _fileLogLevel is null)
93+
{
94+
_fileLogLevel = _loggingSectionLogLevel;
95+
_fileLogLevelSource = ConfigSource.IConfiguration;
96+
}
97+
}
7398
}
7499

75100
/// <summary>
@@ -151,6 +176,8 @@ public string EnableElasticDefaults
151176
}
152177
}
153178

179+
internal string? LoggingSectionLogLevel => _loggingSectionLogLevel;
180+
154181
internal EnabledElasticDefaults EnabledDefaults => _elasticDefaults ?? GetEnabledElasticDefaults();
155182

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

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

src/Elastic.OpenTelemetry/Diagnostics/Logging/CompositeLogger.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ namespace Elastic.OpenTelemetry.Diagnostics.Logging;
1616
/// </remarks>
1717
internal sealed class CompositeLogger(ElasticOpenTelemetryBuilderOptions options) : IDisposable, IAsyncDisposable, ILogger
1818
{
19+
public const string LogCategory = "Elastic.OpenTelemetry";
20+
1921
public FileLogger FileLogger { get; } = new(options.DistroOptions);
2022

2123
private ILogger? _additionalLogger = options.Logger;

src/Elastic.OpenTelemetry/Diagnostics/LoggingEventListener.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Text.RegularExpressions;
88
using Elastic.OpenTelemetry.Diagnostics.Logging;
99
using Microsoft.Extensions.Logging;
10+
using Elastic.OpenTelemetry.Configuration;
1011

1112
namespace Elastic.OpenTelemetry.Diagnostics;
1213

@@ -26,15 +27,25 @@ class LoggingEventListener : EventListener, IAsyncDisposable
2627
[GeneratedRegex(TraceParentRegularExpressionString)]
2728
private static partial Regex TraceParentRegex();
2829
#else
29-
private static readonly Regex _traceParentRegex = new Regex(TraceParentRegularExpressionString);
30+
private static readonly Regex _traceParentRegex = new(TraceParentRegularExpressionString);
3031
private static Regex TraceParentRegex() => _traceParentRegex;
3132
#endif
3233

33-
public LoggingEventListener(ILogger logger)
34+
public LoggingEventListener(ILogger logger, ElasticOpenTelemetryOptions options)
3435
{
3536
_logger = logger;
3637

37-
var eventLevel = AgentLoggingHelpers.GetElasticOtelLogLevelFromEnvironmentVariables();
38+
// When both a file log level and a logging section log level are provided, the more verbose of the two is used.
39+
// This insures we subscribes to the lowest level of events needed.
40+
// The specific loggers will then determine if they should log the event based on their own log level.
41+
var eventLevel = LogLevelHelpers.ToLogLevel(options.FileLogLevel);
42+
if (!string.IsNullOrEmpty(options.LoggingSectionLogLevel))
43+
{
44+
var logLevel = LogLevelHelpers.ToLogLevel(options.LoggingSectionLogLevel);
45+
46+
if (logLevel < eventLevel)
47+
eventLevel = logLevel;
48+
}
3849

3950
_eventLevel = eventLevel switch
4051
{

src/Elastic.OpenTelemetry/ElasticOpenTelemetryBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public ElasticOpenTelemetryBuilder(ElasticOpenTelemetryBuilderOptions options)
6464
Logger = new CompositeLogger(options);
6565

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

6969
Logger.LogAgentPreamble();
7070
Logger.LogElasticOpenTelemetryBuilderInitialized(Environment.NewLine, new StackTrace(true));

src/Elastic.OpenTelemetry/Hosting/ElasticOpenTelemetryService.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
33
// See the LICENSE file in the project root for more information
44

5+
using Elastic.OpenTelemetry.Diagnostics.Logging;
56
using Elastic.OpenTelemetry.Extensions;
67
using Microsoft.Extensions.DependencyInjection;
78
using Microsoft.Extensions.Hosting;
@@ -16,7 +17,7 @@ internal sealed class ElasticOpenTelemetryService(IServiceProvider serviceProvid
1617
public Task StartingAsync(CancellationToken cancellationToken)
1718
{
1819
var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
19-
var logger = loggerFactory?.CreateLogger($"{nameof(Elastic)}.{nameof(OpenTelemetry)}");
20+
var logger = loggerFactory?.CreateLogger(CompositeLogger.LogCategory);
2021

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

tests/Elastic.OpenTelemetry.Tests/Configuration/ElasticOpenTelemetryOptionsTests.cs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ public void DefaultCtor_LoadsConfigurationFromEnvironmentVariables()
9898
[Fact]
9999
public void ConfigurationCtor_LoadsConfigurationFromIConfiguration()
100100
{
101+
const string loggingSectionLogLevel = "Warning";
101102
const string fileLogLevel = "Critical";
102103
const string enabledElasticDefaults = "None";
103104

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

110111
var json = $$"""
111112
{
113+
"Logging": {
114+
"LogLevel": {
115+
"Default": "Information",
116+
"Elastic.OpenTelemetry": "{{loggingSectionLogLevel}}"
117+
}
118+
},
112119
"Elastic": {
113120
"OpenTelemetry": {
114121
"FileLogDirectory": "C:\\Temp",
@@ -131,6 +138,118 @@ public void ConfigurationCtor_LoadsConfigurationFromIConfiguration()
131138
sut.EnableElasticDefaults.Should().Be(enabledElasticDefaults);
132139
sut.EnabledDefaults.Should().Be(EnabledElasticDefaults.None);
133140
sut.SkipOtlpExporter.Should().Be(true);
141+
sut.LoggingSectionLogLevel.Should().Be(loggingSectionLogLevel);
142+
143+
var logger = new TestLogger(_output);
144+
145+
sut.LogConfigSources(logger);
146+
147+
logger.Messages.Count.Should().Be(4);
148+
foreach (var message in logger.Messages)
149+
{
150+
message.Should().EndWith("from [IConfiguration]");
151+
}
152+
153+
ResetEnvironmentVariables();
154+
}
155+
156+
[Fact]
157+
public void ConfigurationCtor_LoadsConfigurationFromIConfiguration_AndFallsBackToLoggingSection_WhenAvailable()
158+
{
159+
const string loggingSectionLogLevel = "Warning";
160+
const string enabledElasticDefaults = "None";
161+
162+
// Remove all env vars
163+
Environment.SetEnvironmentVariable(EnvironmentVariables.ElasticOtelFileLogDirectoryEnvironmentVariable, null);
164+
Environment.SetEnvironmentVariable(EnvironmentVariables.ElasticOtelFileLogLevelEnvironmentVariable, null);
165+
Environment.SetEnvironmentVariable(EnvironmentVariables.ElasticOtelEnableElasticDefaults, null);
166+
Environment.SetEnvironmentVariable(EnvironmentVariables.ElasticOtelSkipOtlpExporter, null);
167+
168+
var json = $$"""
169+
{
170+
"Logging": {
171+
"LogLevel": {
172+
"Default": "Information",
173+
"Elastic.OpenTelemetry": "{{loggingSectionLogLevel}}"
174+
}
175+
},
176+
"Elastic": {
177+
"OpenTelemetry": {
178+
"FileLogDirectory": "C:\\Temp",
179+
"EnabledElasticDefaults": "{{enabledElasticDefaults}}",
180+
"SkipOtlpExporter": true
181+
}
182+
}
183+
}
184+
""";
185+
186+
var config = new ConfigurationBuilder()
187+
.AddJsonStream(new MemoryStream(Encoding.UTF8.GetBytes(json)))
188+
.Build();
189+
190+
var sut = new ElasticOpenTelemetryOptions(config);
191+
192+
sut.FileLogDirectory.Should().Be(@"C:\Temp");
193+
sut.FileLogLevel.Should().Be(loggingSectionLogLevel);
194+
sut.EnableElasticDefaults.Should().Be(enabledElasticDefaults);
195+
sut.EnabledDefaults.Should().Be(EnabledElasticDefaults.None);
196+
sut.SkipOtlpExporter.Should().Be(true);
197+
sut.LoggingSectionLogLevel.Should().Be(loggingSectionLogLevel);
198+
199+
var logger = new TestLogger(_output);
200+
201+
sut.LogConfigSources(logger);
202+
203+
logger.Messages.Count.Should().Be(4);
204+
foreach (var message in logger.Messages)
205+
{
206+
message.Should().EndWith("from [IConfiguration]");
207+
}
208+
209+
ResetEnvironmentVariables();
210+
}
211+
212+
[Fact]
213+
public void ConfigurationCtor_LoadsConfigurationFromIConfiguration_AndFallsBackToLoggingSectionDefault_WhenAvailable()
214+
{
215+
const string loggingSectionDefaultLogLevel = "Information";
216+
const string enabledElasticDefaults = "None";
217+
218+
// Remove all env vars
219+
Environment.SetEnvironmentVariable(EnvironmentVariables.ElasticOtelFileLogDirectoryEnvironmentVariable, null);
220+
Environment.SetEnvironmentVariable(EnvironmentVariables.ElasticOtelFileLogLevelEnvironmentVariable, null);
221+
Environment.SetEnvironmentVariable(EnvironmentVariables.ElasticOtelEnableElasticDefaults, null);
222+
Environment.SetEnvironmentVariable(EnvironmentVariables.ElasticOtelSkipOtlpExporter, null);
223+
224+
var json = $$"""
225+
{
226+
"Logging": {
227+
"LogLevel": {
228+
"Default": "{{loggingSectionDefaultLogLevel}}"
229+
}
230+
},
231+
"Elastic": {
232+
"OpenTelemetry": {
233+
"FileLogDirectory": "C:\\Temp",
234+
"EnabledElasticDefaults": "{{enabledElasticDefaults}}",
235+
"SkipOtlpExporter": true
236+
}
237+
}
238+
}
239+
""";
240+
241+
var config = new ConfigurationBuilder()
242+
.AddJsonStream(new MemoryStream(Encoding.UTF8.GetBytes(json)))
243+
.Build();
244+
245+
var sut = new ElasticOpenTelemetryOptions(config);
246+
247+
sut.FileLogDirectory.Should().Be(@"C:\Temp");
248+
sut.FileLogLevel.Should().Be(loggingSectionDefaultLogLevel);
249+
sut.EnableElasticDefaults.Should().Be(enabledElasticDefaults);
250+
sut.EnabledDefaults.Should().Be(EnabledElasticDefaults.None);
251+
sut.SkipOtlpExporter.Should().Be(true);
252+
sut.LoggingSectionLogLevel.Should().Be(loggingSectionDefaultLogLevel);
134253

135254
var logger = new TestLogger(_output);
136255

0 commit comments

Comments
 (0)