diff --git a/src/Elastic.OpenTelemetry/Configuration/ElasticOpenTelemetryOptions.cs b/src/Elastic.OpenTelemetry/Configuration/ElasticOpenTelemetryOptions.cs
index 0b59964..714fa36 100644
--- a/src/Elastic.OpenTelemetry/Configuration/ElasticOpenTelemetryOptions.cs
+++ b/src/Elastic.OpenTelemetry/Configuration/ElasticOpenTelemetryOptions.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information
using System.Collections;
+using System.Diagnostics;
using System.Runtime.InteropServices;
using Elastic.OpenTelemetry.Diagnostics.Logging;
using Microsoft.Extensions.Configuration;
@@ -54,6 +55,9 @@ public class ElasticOpenTelemetryOptions
private readonly string? _enabledElasticDefaults;
private readonly ConfigSource _enabledElasticDefaultsSource = ConfigSource.Default;
+ private readonly bool? _runningInContainer;
+ private readonly ConfigSource _runningInContainerSource = ConfigSource.Default;
+
private string? _loggingSectionLogLevel;
private readonly string _defaultLogDirectory;
private readonly IDictionary _environmentVariables;
@@ -66,8 +70,10 @@ public ElasticOpenTelemetryOptions(IDictionary? environmentVariables = null)
{
_defaultLogDirectory = GetDefaultLogDirectory();
_environmentVariables = environmentVariables ?? GetEnvironmentVariables();
- SetFromEnvironment(ELASTIC_OTEL_LOG_DIRECTORY, ref _logDirectory, ref _logDirectorySource, StringParser);
- SetFromEnvironment(ELASTIC_OTEL_LOG_LEVEL, ref _logLevel, ref _logLevelSource, LogLevelParser);
+ SetFromEnvironment(DOTNET_RUNNING_IN_CONTAINER, ref _runningInContainer, ref _runningInContainerSource, BoolParser);
+
+ SetFromEnvironment(OTEL_DOTNET_AUTO_LOG_DIRECTORY, ref _logDirectory, ref _logDirectorySource, StringParser);
+ SetFromEnvironment(OTEL_LOG_LEVEL, ref _logLevel, ref _logLevelSource, LogLevelParser);
SetFromEnvironment(ELASTIC_OTEL_LOG_TARGETS, ref _logTargets, ref _logTargetsSource, LogTargetsParser);
SetFromEnvironment(ELASTIC_OTEL_SKIP_OTLP_EXPORTER, ref _skipOtlpExporter, ref _skipOtlpExporterSource, BoolParser);
SetFromEnvironment(ELASTIC_OTEL_ENABLE_ELASTIC_DEFAULTS, ref _enabledElasticDefaults, ref _enabledElasticDefaultsSource, StringParser);
@@ -116,7 +122,7 @@ public bool GlobalLogEnabled
{
get
{
- var isActive = _logLevel.HasValue || !string.IsNullOrWhiteSpace(_logDirectory) || _logTargets.HasValue;
+ var isActive = _logLevel is <= LogLevel.Debug || !string.IsNullOrWhiteSpace(_logDirectory) || _logTargets.HasValue;
if (!isActive)
return isActive;
@@ -193,7 +199,9 @@ public LogLevel LogLevel
/// >
public LogTargets LogTargets
{
- get => _logTargets ?? (GlobalLogEnabled ? LogTargets.File : LogTargets.None);
+ get => _logTargets ?? (GlobalLogEnabled ?
+ _runningInContainer.HasValue && _runningInContainer.Value ? LogTargets.StdOut : LogTargets.File
+ : LogTargets.None);
init
{
_logTargets = value;
@@ -275,7 +283,13 @@ bool IsSet(string k, string v)
private static (bool, string) StringParser(string? s) => !string.IsNullOrEmpty(s) ? (true, s) : (false, string.Empty);
- private static (bool, bool?) BoolParser(string? s) => bool.TryParse(s, out var boolValue) ? (true, boolValue) : (false, null);
+ private static (bool, bool?) BoolParser(string? s) =>
+ s switch
+ {
+ "1" => (true, true),
+ "0" => (true, false),
+ _ => bool.TryParse(s, out var boolValue) ? (true, boolValue) : (false, null)
+ };
private void SetFromEnvironment(string key, ref T field, ref ConfigSource configSourceField, Func parser)
{
diff --git a/src/Elastic.OpenTelemetry/Configuration/EnvironmentVariables.cs b/src/Elastic.OpenTelemetry/Configuration/EnvironmentVariables.cs
index 4417e5c..6827fa8 100644
--- a/src/Elastic.OpenTelemetry/Configuration/EnvironmentVariables.cs
+++ b/src/Elastic.OpenTelemetry/Configuration/EnvironmentVariables.cs
@@ -9,10 +9,13 @@ internal static class EnvironmentVariables
// ReSharper disable InconsistentNaming
public const string ELASTIC_OTEL_SKIP_OTLP_EXPORTER = nameof(ELASTIC_OTEL_SKIP_OTLP_EXPORTER);
- public const string ELASTIC_OTEL_LOG_DIRECTORY = nameof(ELASTIC_OTEL_LOG_DIRECTORY);
- public const string ELASTIC_OTEL_LOG_LEVEL = nameof(ELASTIC_OTEL_LOG_LEVEL);
- public const string ELASTIC_OTEL_LOG_TARGETS = nameof(ELASTIC_OTEL_LOG_TARGETS);
+ public const string OTEL_DOTNET_AUTO_LOG_DIRECTORY = nameof(OTEL_DOTNET_AUTO_LOG_DIRECTORY);
+ public const string OTEL_LOG_LEVEL = nameof(OTEL_LOG_LEVEL);
+
+ public const string ELASTIC_OTEL_LOG_TARGETS = nameof(ELASTIC_OTEL_LOG_TARGETS);
public const string ELASTIC_OTEL_ENABLE_ELASTIC_DEFAULTS = nameof(ELASTIC_OTEL_ENABLE_ELASTIC_DEFAULTS);
+
+ public const string DOTNET_RUNNING_IN_CONTAINER = nameof(DOTNET_RUNNING_IN_CONTAINER);
// ReSharper enable InconsistentNaming
}
diff --git a/src/Elastic.OpenTelemetry/Diagnostics/LoggerMessages.cs b/src/Elastic.OpenTelemetry/Diagnostics/LoggerMessages.cs
index a32ebe4..79f8751 100644
--- a/src/Elastic.OpenTelemetry/Diagnostics/LoggerMessages.cs
+++ b/src/Elastic.OpenTelemetry/Diagnostics/LoggerMessages.cs
@@ -14,23 +14,23 @@ internal static partial class LoggerMessages
#pragma warning disable SYSLIB1006 // Multiple logging methods cannot use the same event id within a class
// We explictly reuse the same event ID and this is the same log message, but with different types for the structured data
- [LoggerMessage(EventId = 100, Level = LogLevel.Trace, Message = "{ProcessorName} found `{AttributeName}` attribute with value '{AttributeValue}' on the span.")]
+ [LoggerMessage(EventId = 100, Level = LogLevel.Debug, Message = "{ProcessorName} found `{AttributeName}` attribute with value '{AttributeValue}' on the span.")]
internal static partial void FoundTag(this ILogger logger, string processorName, string attributeName, string attributeValue);
- [LoggerMessage(EventId = 100, Level = LogLevel.Trace, Message = "{ProcessorName} found `{AttributeName}` attribute with value '{AttributeValue}' on the span.")]
+ [LoggerMessage(EventId = 100, Level = LogLevel.Debug, Message = "{ProcessorName} found `{AttributeName}` attribute with value '{AttributeValue}' on the span.")]
internal static partial void FoundTag(this ILogger logger, string processorName, string attributeName, int attributeValue);
- [LoggerMessage(EventId = 101, Level = LogLevel.Trace, Message = "{ProcessorName} set `{AttributeName}` attribute with value '{AttributeValue}' on the span.")]
+ [LoggerMessage(EventId = 101, Level = LogLevel.Debug, Message = "{ProcessorName} set `{AttributeName}` attribute with value '{AttributeValue}' on the span.")]
internal static partial void SetTag(this ILogger logger, string processorName, string attributeName, string attributeValue);
- [LoggerMessage(EventId = 101, Level = LogLevel.Trace, Message = "{ProcessorName} set `{AttributeName}` attribute with value '{AttributeValue}' on the span.")]
+ [LoggerMessage(EventId = 101, Level = LogLevel.Debug, Message = "{ProcessorName} set `{AttributeName}` attribute with value '{AttributeValue}' on the span.")]
internal static partial void SetTag(this ILogger logger, string processorName, string attributeName, int attributeValue);
#pragma warning restore SYSLIB1006 // Multiple logging methods cannot use the same event id within a class
- [LoggerMessage(EventId = 20, Level = LogLevel.Trace, Message = "Added '{ProcessorTypeName}' processor to '{BuilderTypeName}'.")]
+ [LoggerMessage(EventId = 20, Level = LogLevel.Debug, Message = "Added '{ProcessorTypeName}' processor to '{BuilderTypeName}'.")]
public static partial void LogProcessorAdded(this ILogger logger, string processorTypeName, string builderTypeName);
- [LoggerMessage(EventId = 21, Level = LogLevel.Trace, Message = "Added '{MeterName}' meter to '{BuilderTypeName}'.")]
+ [LoggerMessage(EventId = 21, Level = LogLevel.Debug, Message = "Added '{MeterName}' meter to '{BuilderTypeName}'.")]
public static partial void LogMeterAdded(this ILogger logger, string meterName, string builderTypeName);
public static void LogAgentPreamble(this ILogger logger)
@@ -66,8 +66,8 @@ public static void LogAgentPreamble(this ILogger logger)
string[] environmentVariables =
[
- EnvironmentVariables.ELASTIC_OTEL_LOG_DIRECTORY,
- EnvironmentVariables.ELASTIC_OTEL_LOG_LEVEL
+ EnvironmentVariables.OTEL_DOTNET_AUTO_LOG_DIRECTORY,
+ EnvironmentVariables.OTEL_LOG_LEVEL
];
foreach (var variable in environmentVariables)
diff --git a/src/Elastic.OpenTelemetry/Diagnostics/Logging/LogLevelHelpers.cs b/src/Elastic.OpenTelemetry/Diagnostics/Logging/LogLevelHelpers.cs
index 82d43cd..9e1f939 100644
--- a/src/Elastic.OpenTelemetry/Diagnostics/Logging/LogLevelHelpers.cs
+++ b/src/Elastic.OpenTelemetry/Diagnostics/Logging/LogLevelHelpers.cs
@@ -18,8 +18,11 @@ internal static class LogLevelHelpers
public static LogLevel? ToLogLevel(string logLevelString)
{
+ //TRACE does not exist in OTEL_LOG_LEVEL ensure we parse it to next granularity
+ //debug, NOTE that OpenTelemetry treats this as invalid and will parse to 'Information'
+ //We treat Debug & Trace as a signal global file logging should be enabled.
if (logLevelString.Equals(Trace, StringComparison.OrdinalIgnoreCase))
- return LogLevel.Trace;
+ return LogLevel.Debug;
if (logLevelString.Equals(Debug, StringComparison.OrdinalIgnoreCase))
return LogLevel.Debug;
if (logLevelString.Equals("Info", StringComparison.OrdinalIgnoreCase))
diff --git a/src/Elastic.OpenTelemetry/ElasticOpenTelemetryBuilder.cs b/src/Elastic.OpenTelemetry/ElasticOpenTelemetryBuilder.cs
index cd7ee7d..858dc44 100644
--- a/src/Elastic.OpenTelemetry/ElasticOpenTelemetryBuilder.cs
+++ b/src/Elastic.OpenTelemetry/ElasticOpenTelemetryBuilder.cs
@@ -132,7 +132,7 @@ internal static partial class LoggerMessages
[LoggerMessage(EventId = 0, Level = LogLevel.Information, Message = "ElasticOpenTelemetryBuilder initialized{newline}{StackTrace}.")]
public static partial void LogElasticOpenTelemetryBuilderInitialized(this ILogger logger, string newline, StackTrace stackTrace);
- [LoggerMessage(EventId = 1, Level = LogLevel.Trace, Message = "ElasticOpenTelemetryBuilder configured {Signal} via the {Provider}.")]
+ [LoggerMessage(EventId = 1, Level = LogLevel.Debug, Message = "ElasticOpenTelemetryBuilder configured {Signal} via the {Provider}.")]
public static partial void LogConfiguredSignalProvider(this ILogger logger, string signal, string provider);
[LoggerMessage(EventId = 2, Level = LogLevel.Information, Message = "No Elastic defaults were enabled.")]
diff --git a/src/Elastic.OpenTelemetry/Extensions/OpenTelemetryBuilderExtensions.cs b/src/Elastic.OpenTelemetry/Extensions/OpenTelemetryBuilderExtensions.cs
index f472a93..95417db 100644
--- a/src/Elastic.OpenTelemetry/Extensions/OpenTelemetryBuilderExtensions.cs
+++ b/src/Elastic.OpenTelemetry/Extensions/OpenTelemetryBuilderExtensions.cs
@@ -73,6 +73,6 @@ internal static IInstrumentationLifetime Build(this IOpenTelemetryBuilder builde
internal static partial class LoggerMessages
{
- [LoggerMessage(EventId = 10, Level = LogLevel.Trace, Message = "ElasticOpenTelemetryBuilder built.")]
+ [LoggerMessage(EventId = 10, Level = LogLevel.Debug, Message = "ElasticOpenTelemetryBuilder built.")]
public static partial void LogElasticOpenTelemetryBuilderBuilt(this ILogger logger);
}
diff --git a/tests/Elastic.OpenTelemetry.Tests/Configuration/ElasticOpenTelemetryOptionsTests.cs b/tests/Elastic.OpenTelemetry.Tests/Configuration/ElasticOpenTelemetryOptionsTests.cs
index 570edf8..6a075ad 100644
--- a/tests/Elastic.OpenTelemetry.Tests/Configuration/ElasticOpenTelemetryOptionsTests.cs
+++ b/tests/Elastic.OpenTelemetry.Tests/Configuration/ElasticOpenTelemetryOptionsTests.cs
@@ -34,8 +34,8 @@ public void DefaultCtor_SetsExpectedDefaults_WhenNoEnvironmentVariablesAreConfig
{
var sut = new ElasticOpenTelemetryOptions(new Hashtable
{
- {ELASTIC_OTEL_LOG_DIRECTORY, null},
- {ELASTIC_OTEL_LOG_LEVEL, null},
+ {OTEL_DOTNET_AUTO_LOG_DIRECTORY, null},
+ {OTEL_LOG_LEVEL, null},
{ELASTIC_OTEL_ENABLE_ELASTIC_DEFAULTS, null},
{ELASTIC_OTEL_SKIP_OTLP_EXPORTER, null},
});
@@ -69,8 +69,8 @@ public void DefaultCtor_LoadsConfigurationFromEnvironmentVariables()
var sut = new ElasticOpenTelemetryOptions(new Hashtable
{
- {ELASTIC_OTEL_LOG_DIRECTORY, fileLogDirectory},
- {ELASTIC_OTEL_LOG_LEVEL, fileLogLevel},
+ {OTEL_DOTNET_AUTO_LOG_DIRECTORY, fileLogDirectory},
+ {OTEL_LOG_LEVEL, fileLogLevel},
{ELASTIC_OTEL_ENABLE_ELASTIC_DEFAULTS, enabledElasticDefaults},
{ELASTIC_OTEL_SKIP_OTLP_EXPORTER, "true"},
});
@@ -257,8 +257,8 @@ public void EnvironmentVariables_TakePrecedenceOver_ConfigValues()
var sut = new ElasticOpenTelemetryOptions(config, new Hashtable
{
- {ELASTIC_OTEL_LOG_DIRECTORY, fileLogDirectory},
- {ELASTIC_OTEL_LOG_LEVEL, fileLogLevel},
+ {OTEL_DOTNET_AUTO_LOG_DIRECTORY, fileLogDirectory},
+ {OTEL_LOG_LEVEL, fileLogLevel},
{ELASTIC_OTEL_ENABLE_ELASTIC_DEFAULTS, enabledElasticDefaults},
{ELASTIC_OTEL_SKIP_OTLP_EXPORTER, "true"},
});
@@ -279,8 +279,8 @@ public void InitializedProperties_TakePrecedenceOver_EnvironmentValues()
var sut = new ElasticOpenTelemetryOptions(new Hashtable
{
- {ELASTIC_OTEL_LOG_DIRECTORY, "C:\\Temp"},
- {ELASTIC_OTEL_LOG_LEVEL, "Information"},
+ {OTEL_DOTNET_AUTO_LOG_DIRECTORY, "C:\\Temp"},
+ {OTEL_LOG_LEVEL, "Information"},
{ELASTIC_OTEL_ENABLE_ELASTIC_DEFAULTS, "All"},
{ELASTIC_OTEL_SKIP_OTLP_EXPORTER, "true"},
})
diff --git a/tests/Elastic.OpenTelemetry.Tests/Configuration/GlobalLogConfigurationTests.cs b/tests/Elastic.OpenTelemetry.Tests/Configuration/GlobalLogConfigurationTests.cs
index 4fc5e42..6e36970 100644
--- a/tests/Elastic.OpenTelemetry.Tests/Configuration/GlobalLogConfigurationTests.cs
+++ b/tests/Elastic.OpenTelemetry.Tests/Configuration/GlobalLogConfigurationTests.cs
@@ -23,8 +23,8 @@ public void Check_Defaults()
//
[Theory]
- [InlineData(ELASTIC_OTEL_LOG_LEVEL, "Info")]
- [InlineData(ELASTIC_OTEL_LOG_DIRECTORY, "1")]
+ [InlineData(OTEL_LOG_LEVEL, "Info")]
+ [InlineData(OTEL_DOTNET_AUTO_LOG_DIRECTORY, "1")]
//only if explicitly specified to 'none' should we not default to file logging.
[InlineData(ELASTIC_OTEL_LOG_TARGETS, "file")]
public void CheckActivation(string environmentVariable, string value)
@@ -36,14 +36,14 @@ public void CheckActivation(string environmentVariable, string value)
//
[Theory]
- [InlineData(ELASTIC_OTEL_LOG_LEVEL, "none")]
+ [InlineData(OTEL_LOG_LEVEL, "none")]
//only if explicitly specified to 'none' should we not default to file logging.
[InlineData(ELASTIC_OTEL_LOG_TARGETS, "none")]
public void CheckDeactivation(string environmentVariable, string value)
{
var config = new ElasticOpenTelemetryOptions(new Hashtable
{
- { ELASTIC_OTEL_LOG_DIRECTORY, "" },
+ { OTEL_DOTNET_AUTO_LOG_DIRECTORY, "" },
{ environmentVariable, value }
});
config.GlobalLogEnabled.Should().BeFalse();
@@ -55,7 +55,7 @@ public void CheckDeactivation(string environmentVariable, string value)
//setting targets to none will result in no global trace logging
[InlineData(ELASTIC_OTEL_LOG_TARGETS, "None")]
//setting file log level to none will result in no global trace logging
- [InlineData(ELASTIC_OTEL_LOG_LEVEL, "None")]
+ [InlineData(OTEL_LOG_LEVEL, "None")]
public void CheckNonActivation(string environmentVariable, string value)
{
var config = new ElasticOpenTelemetryOptions(new Hashtable { { environmentVariable, value } });
@@ -63,23 +63,24 @@ public void CheckNonActivation(string environmentVariable, string value)
}
[Theory]
- [InlineData("trace", LogLevel.Trace)]
- [InlineData("Trace", LogLevel.Trace)]
- [InlineData("TraCe", LogLevel.Trace)]
- [InlineData("debug", LogLevel.Debug)]
- [InlineData("info", LogLevel.Information)]
- [InlineData("warn", LogLevel.Warning)]
- [InlineData("error", LogLevel.Error)]
- [InlineData("none", LogLevel.None)]
- public void Check_LogLevelValues_AreMappedCorrectly(string envVarValue, LogLevel logLevel)
+ [InlineData("trace", LogLevel.Debug, true)]
+ [InlineData("Trace", LogLevel.Debug, true)]
+ [InlineData("TraCe", LogLevel.Debug, true)]
+ [InlineData("debug", LogLevel.Debug, true)]
+ [InlineData("info", LogLevel.Information, false)]
+ [InlineData("warn", LogLevel.Warning, false)]
+ [InlineData("error", LogLevel.Error, false)]
+ [InlineData("none", LogLevel.None, false)]
+ public void Check_LogLevelValues_AreMappedCorrectly(string envVarValue, LogLevel logLevel, bool globalLogEnabled)
{
- Check(ELASTIC_OTEL_LOG_LEVEL, envVarValue, logLevel);
+ Check(OTEL_LOG_LEVEL, envVarValue, logLevel, globalLogEnabled);
return;
- static void Check(string key, string envVarValue, LogLevel level)
+ static void Check(string key, string envVarValue, LogLevel level, bool enabled)
{
var config = CreateConfig(key, envVarValue);
config.LogLevel.Should().Be(level, "{0}", key);
+ config.GlobalLogEnabled.Should().Be(enabled, "{0}", key);
}
}
@@ -90,7 +91,7 @@ static void Check(string key, string envVarValue, LogLevel level)
[InlineData("tracing")]
public void Check_InvalidLogLevelValues_AreMappedToDefaultWarn(string? envVarValue)
{
- Check(ELASTIC_OTEL_LOG_LEVEL, envVarValue);
+ Check(OTEL_LOG_LEVEL, envVarValue);
return;
static void Check(string key, string? envVarValue)
@@ -103,7 +104,7 @@ static void Check(string key, string? envVarValue)
[Fact]
public void Check_LogDir_IsEvaluatedCorrectly()
{
- Check(ELASTIC_OTEL_LOG_DIRECTORY, "/foo/bar");
+ Check(OTEL_DOTNET_AUTO_LOG_DIRECTORY, "/foo/bar");
return;
static void Check(string key, string envVarValue)
@@ -139,6 +140,27 @@ static void Check(string key, string? envVarValue, LogTargets? targets)
}
}
+ [Theory]
+ [InlineData(LogLevel.Debug, null, LogTargets.StdOut, true, true)]
+ [InlineData(LogLevel.Information, null, LogTargets.None, true, false)]
+ [InlineData(LogLevel.Debug, null, LogTargets.File, false, true)]
+ [InlineData(LogLevel.Information, null, LogTargets.None, false, false)]
+ //Ensure explicit loglevel config always takes precedence
+ [InlineData(LogLevel.Debug, "file", LogTargets.File, true, true)]
+ [InlineData(LogLevel.Information, "file", LogTargets.File, false, true)]
+ internal void LogTargetDefaultsToStandardOutIfRunningInContainerWithLogLevelDebug(LogLevel level, string? logTargetsEnvValue, LogTargets targets, bool inContainer, bool globalLogging)
+ {
+ var env = new Hashtable { { OTEL_LOG_LEVEL, level.ToString() } };
+ if (inContainer)
+ env.Add(DOTNET_RUNNING_IN_CONTAINER, "1");
+ if (!string.IsNullOrWhiteSpace(logTargetsEnvValue))
+ env.Add(ELASTIC_OTEL_LOG_TARGETS, logTargetsEnvValue);
+
+ var config = new ElasticOpenTelemetryOptions(env);
+ config.GlobalLogEnabled.Should().Be(globalLogging);
+ config.LogTargets.Should().Be(targets);
+ }
+
private static ElasticOpenTelemetryOptions CreateConfig(string key, string? envVarValue)
{
var environment = new Hashtable { { key, envVarValue } };