From 9d167ca29f73fc47c26b587aa262216e25b4074c Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Mon, 3 Jun 2024 12:47:16 +0200 Subject: [PATCH 01/21] Ensure a globally enabled file logger can be specified --- src/Elastic.Apm/AgentComponents.cs | 52 +++--- src/Elastic.Apm/Config/FileLogConfig.cs | 14 ++ .../FullFrameworkDefaultImplementations.cs | 2 +- src/Elastic.Apm/Config/ProfilerLogConfig.cs | 166 +++++++++++------- ...ests.cs => GlobalLogConfigurationTests.cs} | 44 ++--- test/Elastic.Apm.Tests/LoggerTests.cs | 8 +- 6 files changed, 175 insertions(+), 111 deletions(-) create mode 100644 src/Elastic.Apm/Config/FileLogConfig.cs rename test/Elastic.Apm.Tests/Config/{ProfilerLogConfigTests.cs => GlobalLogConfigurationTests.cs} (60%) diff --git a/src/Elastic.Apm/AgentComponents.cs b/src/Elastic.Apm/AgentComponents.cs index 24e57e9bf..a7fcb6023 100644 --- a/src/Elastic.Apm/AgentComponents.cs +++ b/src/Elastic.Apm/AgentComponents.cs @@ -50,7 +50,7 @@ internal AgentComponents( { var config = CreateConfiguration(logger, configurationReader); - Logger = logger ?? CheckForProfilerLogger(DefaultLogger(null, configurationReader), config.LogLevel); + Logger = logger ?? GetGlobalLogger(DefaultLogger(null, configurationReader), config.LogLevel); ConfigurationStore = new ConfigurationStore(new RuntimeConfigurationSnapshot(config), Logger); Service = Service.GetDefaultService(config, Logger); @@ -147,7 +147,7 @@ private void ServerInfoCallback(bool success, IApmServerInfo serverInfo) return; #else if (!Configuration.OpenTelemetryBridgeEnabled) return; - + if (success) { if (serverInfo.Version >= new ElasticVersion(7, 16, 0, string.Empty)) @@ -197,38 +197,40 @@ private static IConfigurationReader CreateConfiguration(IApmLogger logger, IConf #endif } - - // - // This is the hooking point that checks for the existence of profiler-related - // logging settings. - // If no agent logging is configured but we detect profiler logging settings, those - // will be honoured. - // The finer-grained log-level (agent vs profiler) will be used. - // This has the benefit that users will also get agent logs in addition to profiler-only - // logs. - // - internal static IApmLogger CheckForProfilerLogger(IApmLogger fallbackLogger, LogLevel agentLogLevel, IDictionary environmentVariables = null) + /// + /// This ensures agents will log respect externally provided loggers. + /// If the agent is started as part of profiling it should adhere to profiling configuration + /// If file logging environment variables are set we should always log to that location + /// + /// + /// + /// + /// + internal static IApmLogger GetGlobalLogger(IApmLogger fallbackLogger, LogLevel agentLogLevel, IDictionary environmentVariables = null) { try { - var profilerLogConfig = ProfilerLogConfig.Check(environmentVariables); - if (profilerLogConfig.IsActive) + var fileLogConfig = GlobalLogConfiguration.FromEnvironment(environmentVariables); + if (!fileLogConfig.IsActive) { - var effectiveLogLevel = LogLevelUtils.GetFinest(agentLogLevel, profilerLogConfig.LogLevel); + fallbackLogger.Info()?.Log("No system wide logging configured, defaulting to fallback logger"); + return fallbackLogger; + } - if ((profilerLogConfig.LogTargets & ProfilerLogTarget.File) == ProfilerLogTarget.File) - TraceLogger.TraceSource.Listeners.Add(new TextWriterTraceListener(profilerLogConfig.LogFilePath)); - if ((profilerLogConfig.LogTargets & ProfilerLogTarget.StdOut) == ProfilerLogTarget.StdOut) - TraceLogger.TraceSource.Listeners.Add(new TextWriterTraceListener(Console.Out)); + var effectiveLogLevel = LogLevelUtils.GetFinest(agentLogLevel, fileLogConfig.LogLevel); - var logger = new TraceLogger(effectiveLogLevel); - logger.Info()?.Log($"{nameof(ProfilerLogConfig)} - {profilerLogConfig}"); - return logger; - } + if ((fileLogConfig.LogTargets & GlobalLogTarget.File) == GlobalLogTarget.File) + TraceLogger.TraceSource.Listeners.Add(new TextWriterTraceListener(fileLogConfig.LogFilePath)); + if ((fileLogConfig.LogTargets & GlobalLogTarget.StdOut) == GlobalLogTarget.StdOut) + TraceLogger.TraceSource.Listeners.Add(new TextWriterTraceListener(Console.Out)); + + var logger = new TraceLogger(effectiveLogLevel); + logger.Info()?.Log($"{nameof(fileLogConfig)} - {fileLogConfig}"); + return logger; } catch (Exception e) { - fallbackLogger.Warning()?.LogException(e, "Error in CheckForProfilerLogger"); + fallbackLogger.Warning()?.LogException(e, "Error in GetGlobalLogger"); } return fallbackLogger; } diff --git a/src/Elastic.Apm/Config/FileLogConfig.cs b/src/Elastic.Apm/Config/FileLogConfig.cs new file mode 100644 index 000000000..dc74cf802 --- /dev/null +++ b/src/Elastic.Apm/Config/FileLogConfig.cs @@ -0,0 +1,14 @@ +// 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 System; +using System.Collections; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using Elastic.Apm.Logging; + +namespace Elastic.Apm.Config +{ +} diff --git a/src/Elastic.Apm/Config/Net4FullFramework/FullFrameworkDefaultImplementations.cs b/src/Elastic.Apm/Config/Net4FullFramework/FullFrameworkDefaultImplementations.cs index 888d652cb..73b79054e 100644 --- a/src/Elastic.Apm/Config/Net4FullFramework/FullFrameworkDefaultImplementations.cs +++ b/src/Elastic.Apm/Config/Net4FullFramework/FullFrameworkDefaultImplementations.cs @@ -22,7 +22,7 @@ internal static IApmLogger CreateDefaultLogger(LogLevel? configuredDefault) if (!string.IsNullOrEmpty(logLevel)) Enum.TryParse(logLevel, true, out level); - return AgentComponents.CheckForProfilerLogger(new TraceLogger(level), level); + return AgentComponents.GetGlobalLogger(new TraceLogger(level), level); } /// diff --git a/src/Elastic.Apm/Config/ProfilerLogConfig.cs b/src/Elastic.Apm/Config/ProfilerLogConfig.cs index 5af70ca99..f59bd9b44 100644 --- a/src/Elastic.Apm/Config/ProfilerLogConfig.cs +++ b/src/Elastic.Apm/Config/ProfilerLogConfig.cs @@ -6,48 +6,27 @@ using System.Collections; using System.Diagnostics; using System.IO; +using System.Linq; using System.Runtime.InteropServices; using Elastic.Apm.Logging; -namespace Elastic.Apm.Config -{ - internal readonly struct ProfilerLogConfig - { - private ProfilerLogConfig(bool isActive, LogLevel logLevel, ProfilerLogTarget logTarget, string logFilePath) : this() - { - IsActive = isActive; - LogLevel = logLevel; - LogTargets = logTarget; - LogFilePath = logFilePath; - } - - internal bool IsActive { get; } - internal ProfilerLogTarget LogTargets { get; } - internal string LogFilePath { get; } - internal LogLevel LogLevel { get; } - - public override string ToString() => - $"IsActive: '{IsActive}', LogLevel: '{LogLevel}', LogTargets: '{LogTargets}', LogFilePath: '{LogFilePath}'"; - - internal static string GetDefaultProfilerLogDirectory() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) - ? Path.Combine(Environment.GetEnvironmentVariable("PROGRAMDATA"), "elastic", "apm-agent-dotnet", "logs") - : "/var/log/elastic/apm-agent-dotnet"; - - internal static ProfilerLogConfig Check(IDictionary environmentVariables = null) - { - environmentVariables ??= Environment.GetEnvironmentVariables(); - - string GetSafeEnvironmentVariable(string key) - { - var value = environmentVariables.Contains(key) ? environmentVariables[key]?.ToString() : null; - return value ?? string.Empty; - } +namespace Elastic.Apm.Config; - var v = GetSafeEnvironmentVariable("ELASTIC_APM_PROFILER_LOG"); +internal class EnvironmentLoggingConfiguration(IDictionary environmentVariables = null) +{ + public IDictionary EnvironmentVariables { get; } = environmentVariables ?? Environment.GetEnvironmentVariables(); - var isActive = !string.IsNullOrEmpty(v); + public string GetSafeEnvironmentVariable(string key) + { + var value = EnvironmentVariables.Contains(key) ? EnvironmentVariables[key]?.ToString() : null; + return value ?? string.Empty; + } - var logLevel = v.ToLowerInvariant() switch + public LogLevel GetLogLevel(params string[] keys) + { + var level = keys + .Select(k => GetSafeEnvironmentVariable(k)) + .Select(v => v.ToLowerInvariant() switch { "trace" => LogLevel.Trace, "debug" => LogLevel.Debug, @@ -55,36 +34,105 @@ string GetSafeEnvironmentVariable(string key) "warn" => LogLevel.Warning, "error" => LogLevel.Error, "none" => LogLevel.None, - _ => LogLevel.Warning, - }; + _ => null + }) + .FirstOrDefault(l => l != null); + return level ?? LogLevel.Warning; + } - var logFilePath = GetSafeEnvironmentVariable("ELASTIC_APM_PROFILER_LOG_DIR"); - if (string.IsNullOrEmpty(logFilePath)) - logFilePath = GetDefaultProfilerLogDirectory(); - var process = Process.GetCurrentProcess(); - var logFileName = Path.Combine(logFilePath, $"{process.ProcessName}_{process.Id}_{Environment.TickCount}.agent.log"); + public string GetLogFilePath(params string[] keys) + { + var path = keys + .Select(k => GetSafeEnvironmentVariable(k)) + .FirstOrDefault(p => !string.IsNullOrEmpty(p)); - var logTargets = ProfilerLogTarget.None; - foreach (var target in GetSafeEnvironmentVariable("ELASTIC_APM_PROFILER_LOG_TARGETS").Split(';')) - { - if (target.Equals("stdout", StringComparison.InvariantCultureIgnoreCase)) - logTargets |= ProfilerLogTarget.StdOut; - else if (target.Equals("file", StringComparison.InvariantCultureIgnoreCase)) - logTargets |= ProfilerLogTarget.File; - } + return path ?? GetDefaultLogDirectory(); + } + + public bool AnyConfigured(params string[] keys) => + keys + .Select(k => GetSafeEnvironmentVariable(k)) + .Any(p => !string.IsNullOrEmpty(p)); + + public string CreateLogFileName(string logFilePath) + { + var process = Process.GetCurrentProcess(); + var logFileName = Path.Combine(logFilePath, $"{process.ProcessName}_{process.Id}_{Environment.TickCount}.agent.log"); + return logFileName; + } + + public GlobalLogTarget ParseLogTargets(params string[] keys) + { - if (logTargets == ProfilerLogTarget.None) - logTargets = ProfilerLogTarget.File; + var targets = keys + .Select(k => GetSafeEnvironmentVariable(k)) + .FirstOrDefault(p => !string.IsNullOrEmpty(p)); + if (string.IsNullOrWhiteSpace(targets)) + return GlobalLogTarget.File; - return new(isActive, logLevel, logTargets, logFileName); + var logTargets = GlobalLogTarget.None; + foreach (var target in targets.Split(new [] {';'}, StringSplitOptions.RemoveEmptyEntries)) + { + if (target.Trim().Equals("stdout", StringComparison.InvariantCultureIgnoreCase)) + logTargets |= GlobalLogTarget.StdOut; + else if (target.Trim().Equals("file", StringComparison.InvariantCultureIgnoreCase)) + logTargets |= GlobalLogTarget.File; } + if (logTargets == GlobalLogTarget.None) + logTargets = GlobalLogTarget.File; + return logTargets; + } - [Flags] - internal enum ProfilerLogTarget + internal static string GetDefaultLogDirectory() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? Path.Combine(Environment.GetEnvironmentVariable("PROGRAMDATA")!, "elastic", "apm-agent-dotnet", "logs") + : "/var/log/elastic/apm-agent-dotnet"; + +} + +[Flags] +internal enum GlobalLogTarget +{ + None = 0, + File = 1, + StdOut = 2 +} + +internal readonly struct GlobalLogConfiguration +{ + private GlobalLogConfiguration(bool isActive, LogLevel logLevel, GlobalLogTarget logTarget, string logFilePath) : this() + { + IsActive = isActive; + LogLevel = logLevel; + LogTargets = logTarget; + LogFilePath = logFilePath; + } + + internal bool IsActive { get; } + internal string LogFilePath { get; } + internal LogLevel LogLevel { get; } + internal GlobalLogTarget LogTargets { get; } + + public override string ToString() => $"LogLevel: '{LogLevel}', LogFilePath: '{LogFilePath}'"; + + internal static GlobalLogConfiguration FromEnvironment(IDictionary environmentVariables = null) { - None = 0, - File = 1, - StdOut = 2 + var config = new EnvironmentLoggingConfiguration(environmentVariables); + var logLevel = config.GetLogLevel("ELASTIC_OTEL_FILE_LOG_LEVEL", "ELASTIC_APM_LOG_LEVEL", "ELASTIC_APM_PROFILER_LOG"); + var logFileDirectory = config.GetLogFilePath("ELASTIC_OTEL_FILE_LOG_DIRECTORY", "ELASTIC_APM_LOG_DIRECTORY", "ELASTIC_APM_PROFILER_LOG_DIR"); + var logFilePath = config.CreateLogFileName(logFileDirectory); + var logTarget = config.ParseLogTargets("ELASTIC_APM_PROFILER_LOG_TARGETS"); + + var isActive = config.AnyConfigured( + "ELASTIC_OTEL_FILE_LOG_LEVEL", + "ELASTIC_OTEL_FILE_LOG_DIRECTORY", + "ELASTIC_APM_LOG_DIRECTORY", + "ELASTIC_APM_PROFILER_LOG", + "ELASTIC_APM_PROFILER_LOG_DIR", + "ELASTIC_APM_PROFILER_LOG_TARGETS" + ) && logTarget != GlobalLogTarget.None; + + return new(isActive, logLevel, logTarget, logFilePath); } } + diff --git a/test/Elastic.Apm.Tests/Config/ProfilerLogConfigTests.cs b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs similarity index 60% rename from test/Elastic.Apm.Tests/Config/ProfilerLogConfigTests.cs rename to test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs index 42420941a..57a98e356 100644 --- a/test/Elastic.Apm.Tests/Config/ProfilerLogConfigTests.cs +++ b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs @@ -10,17 +10,17 @@ namespace Elastic.Apm.Tests.Config { - public class ProfilerLogConfigTests + public class GlobalLogConfigurationTests { [Fact] public void Check_Defaults() { - var config = ProfilerLogConfig.Check(new Hashtable()); + var config = GlobalLogConfiguration.FromEnvironment(new Hashtable()); config.IsActive.Should().BeFalse(); config.LogLevel.Should().Be(LogLevel.Warning); - config.LogFilePath.Should().StartWith(ProfilerLogConfig.GetDefaultProfilerLogDirectory()); + config.LogFilePath.Should().StartWith(EnvironmentLoggingConfiguration.GetDefaultLogDirectory()); config.LogFilePath.Should().EndWith(".agent.log"); - config.LogTargets.Should().Be(ProfilerLogTarget.File); + config.LogTargets.Should().Be(GlobalLogTarget.File); } [Theory] @@ -35,7 +35,7 @@ public void Check_Defaults() public void Check_LogLevelValues_AreMappedCorrectly(string envVarValue, LogLevel logLevel) { var environment = new Hashtable { { "ELASTIC_APM_PROFILER_LOG", envVarValue } }; - var config = ProfilerLogConfig.Check(environment); + var config = GlobalLogConfiguration.FromEnvironment(environment); config.IsActive.Should().BeTrue(); config.LogLevel.Should().Be(logLevel); } @@ -48,7 +48,7 @@ public void Check_LogLevelValues_AreMappedCorrectly(string envVarValue, LogLevel public void Check_InvalidLogLevelValues_AreMappedToDefaultWarn(string envVarValue, bool isActive) { var environment = new Hashtable { { "ELASTIC_APM_PROFILER_LOG", envVarValue } }; - var config = ProfilerLogConfig.Check(environment); + var config = GlobalLogConfiguration.FromEnvironment(environment); config.LogLevel.Should().Be(LogLevel.Warning); config.IsActive.Should().Be(isActive); } @@ -57,29 +57,29 @@ public void Check_InvalidLogLevelValues_AreMappedToDefaultWarn(string envVarValu public void Check_LogDir_IsEvaluatedCorrectly() { var environment = new Hashtable { { "ELASTIC_APM_PROFILER_LOG_DIR", "/foo/bar" } }; - var config = ProfilerLogConfig.Check(environment); + var config = GlobalLogConfiguration.FromEnvironment(environment); config.LogFilePath.Should().StartWith("/foo/bar"); config.LogFilePath.Should().EndWith(".agent.log"); } [Theory] - [InlineData(null, ProfilerLogTarget.File)] - [InlineData("", ProfilerLogTarget.File)] - [InlineData("foo", ProfilerLogTarget.File)] - [InlineData("foo,bar", ProfilerLogTarget.File)] - [InlineData("foo;bar", ProfilerLogTarget.File)] - [InlineData("file;foo;bar", ProfilerLogTarget.File)] - [InlineData("file", ProfilerLogTarget.File)] - [InlineData("stdout", ProfilerLogTarget.StdOut)] - [InlineData("StdOut", ProfilerLogTarget.StdOut)] - [InlineData("file;stdout", ProfilerLogTarget.File | ProfilerLogTarget.StdOut)] - [InlineData("FILE;StdOut", ProfilerLogTarget.File | ProfilerLogTarget.StdOut)] - [InlineData("file;stdout;file", ProfilerLogTarget.File | ProfilerLogTarget.StdOut)] - [InlineData("FILE;StdOut;stdout", ProfilerLogTarget.File | ProfilerLogTarget.StdOut)] - internal void Check_LogTargets_AreEvaluatedCorrectly(string envVarValue, ProfilerLogTarget targets) + [InlineData(null, GlobalLogTarget.File)] + [InlineData("", GlobalLogTarget.File)] + [InlineData("foo", GlobalLogTarget.File)] + [InlineData("foo,bar", GlobalLogTarget.File)] + [InlineData("foo;bar", GlobalLogTarget.File)] + [InlineData("file;foo;bar", GlobalLogTarget.File)] + [InlineData("file", GlobalLogTarget.File)] + [InlineData("stdout", GlobalLogTarget.StdOut)] + [InlineData("StdOut", GlobalLogTarget.StdOut)] + [InlineData("file;stdout", GlobalLogTarget.File | GlobalLogTarget.StdOut)] + [InlineData("FILE;StdOut", GlobalLogTarget.File | GlobalLogTarget.StdOut)] + [InlineData("file;stdout;file", GlobalLogTarget.File | GlobalLogTarget.StdOut)] + [InlineData("FILE;StdOut;stdout", GlobalLogTarget.File | GlobalLogTarget.StdOut)] + internal void Check_LogTargets_AreEvaluatedCorrectly(string envVarValue, GlobalLogTarget targets) { var environment = new Hashtable { { "ELASTIC_APM_PROFILER_LOG_TARGETS", envVarValue } }; - var config = ProfilerLogConfig.Check(environment); + var config = GlobalLogConfiguration.FromEnvironment(environment); config.LogTargets.Should().Be(targets); } } diff --git a/test/Elastic.Apm.Tests/LoggerTests.cs b/test/Elastic.Apm.Tests/LoggerTests.cs index 54c05c334..5952a3713 100644 --- a/test/Elastic.Apm.Tests/LoggerTests.cs +++ b/test/Elastic.Apm.Tests/LoggerTests.cs @@ -26,22 +26,22 @@ public void CheckForProfilerLogger_ReturnsExpectedLoggers() { var fallbackLogger = new NoopLogger(); - var logger = AgentComponents.CheckForProfilerLogger(fallbackLogger, LogLevel.Trace, new Hashtable()); + var logger = AgentComponents.GetGlobalLogger(fallbackLogger, LogLevel.Trace, new Hashtable()); logger.Should().NotBeNull(); logger.Should().Be(fallbackLogger); logger.IsEnabled(LogLevel.Trace).Should().BeFalse(); - logger = AgentComponents.CheckForProfilerLogger(fallbackLogger, LogLevel.Trace, new Hashtable { { "ELASTIC_APM_PROFILER_LOG", "trace" } }); + logger = AgentComponents.GetGlobalLogger(fallbackLogger, LogLevel.Trace, new Hashtable { { "ELASTIC_APM_PROFILER_LOG", "trace" } }); logger.Should().NotBeNull(); logger.Should().NotBe(fallbackLogger); logger.IsEnabled(LogLevel.Trace).Should().BeTrue(); - logger = AgentComponents.CheckForProfilerLogger(fallbackLogger, LogLevel.Error, new Hashtable { { "ELASTIC_APM_PROFILER_LOG", "trace" } }); + logger = AgentComponents.GetGlobalLogger(fallbackLogger, LogLevel.Error, new Hashtable { { "ELASTIC_APM_PROFILER_LOG", "trace" } }); logger.Should().NotBeNull(); logger.Should().NotBe(fallbackLogger); logger.IsEnabled(LogLevel.Trace).Should().BeTrue(); - logger = AgentComponents.CheckForProfilerLogger(fallbackLogger, LogLevel.Error, new Hashtable { { "ELASTIC_APM_PROFILER_LOG", "warn" } }); + logger = AgentComponents.GetGlobalLogger(fallbackLogger, LogLevel.Error, new Hashtable { { "ELASTIC_APM_PROFILER_LOG", "warn" } }); logger.Should().NotBeNull(); logger.Should().NotBe(fallbackLogger); logger.IsEnabled(LogLevel.Trace).Should().BeFalse(); From 404c74783da2b32d1c1b23ba7c45495d2f6fa5bf Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Mon, 3 Jun 2024 13:42:27 +0200 Subject: [PATCH 02/21] Share globalconfiguration logic with managed loader without taking a new dependency --- src/Elastic.Apm/AgentComponents.cs | 2 +- src/Elastic.Apm/Config/FileLogConfig.cs | 14 --- .../GlobalLogConfiguration.cs} | 90 +++++++++++++----- src/Elastic.Apm/Logging/LogLevel.cs | 91 ++++++++++--------- ...Elastic.Apm.Profiler.Managed.Loader.csproj | 5 + .../Logger.cs | 45 ++------- .../Startup.cs | 2 +- 7 files changed, 127 insertions(+), 122 deletions(-) delete mode 100644 src/Elastic.Apm/Config/FileLogConfig.cs rename src/Elastic.Apm/{Config/ProfilerLogConfig.cs => Logging/GlobalLogConfiguration.cs} (54%) diff --git a/src/Elastic.Apm/AgentComponents.cs b/src/Elastic.Apm/AgentComponents.cs index a7fcb6023..34a53bdfd 100644 --- a/src/Elastic.Apm/AgentComponents.cs +++ b/src/Elastic.Apm/AgentComponents.cs @@ -220,7 +220,7 @@ internal static IApmLogger GetGlobalLogger(IApmLogger fallbackLogger, LogLevel a var effectiveLogLevel = LogLevelUtils.GetFinest(agentLogLevel, fileLogConfig.LogLevel); if ((fileLogConfig.LogTargets & GlobalLogTarget.File) == GlobalLogTarget.File) - TraceLogger.TraceSource.Listeners.Add(new TextWriterTraceListener(fileLogConfig.LogFilePath)); + TraceLogger.TraceSource.Listeners.Add(new TextWriterTraceListener(fileLogConfig.AgentLogFilePath)); if ((fileLogConfig.LogTargets & GlobalLogTarget.StdOut) == GlobalLogTarget.StdOut) TraceLogger.TraceSource.Listeners.Add(new TextWriterTraceListener(Console.Out)); diff --git a/src/Elastic.Apm/Config/FileLogConfig.cs b/src/Elastic.Apm/Config/FileLogConfig.cs deleted file mode 100644 index dc74cf802..000000000 --- a/src/Elastic.Apm/Config/FileLogConfig.cs +++ /dev/null @@ -1,14 +0,0 @@ -// 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 System; -using System.Collections; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; -using Elastic.Apm.Logging; - -namespace Elastic.Apm.Config -{ -} diff --git a/src/Elastic.Apm/Config/ProfilerLogConfig.cs b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs similarity index 54% rename from src/Elastic.Apm/Config/ProfilerLogConfig.cs rename to src/Elastic.Apm/Logging/GlobalLogConfiguration.cs index f59bd9b44..379b09eda 100644 --- a/src/Elastic.Apm/Config/ProfilerLogConfig.cs +++ b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs @@ -8,9 +8,16 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; -using Elastic.Apm.Logging; -namespace Elastic.Apm.Config; +#if APM_LOADER +using static Elastic.Apm.Profiler.Managed.Loader.LogEnvironmentVariables; + +namespace Elastic.Apm.Profiler.Managed.Loader; +#else +using static Elastic.Apm.Logging.LogEnvironmentVariables; + +namespace Elastic.Apm.Logging; +#endif internal class EnvironmentLoggingConfiguration(IDictionary environmentVariables = null) { @@ -54,13 +61,6 @@ public bool AnyConfigured(params string[] keys) => .Select(k => GetSafeEnvironmentVariable(k)) .Any(p => !string.IsNullOrEmpty(p)); - public string CreateLogFileName(string logFilePath) - { - var process = Process.GetCurrentProcess(); - var logFileName = Path.Combine(logFilePath, $"{process.ProcessName}_{process.Id}_{Environment.TickCount}.agent.log"); - return logFileName; - } - public GlobalLogTarget ParseLogTargets(params string[] keys) { @@ -84,7 +84,8 @@ public GlobalLogTarget ParseLogTargets(params string[] keys) } - internal static string GetDefaultLogDirectory() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + private static string GetDefaultLogDirectory() => + Environment.OSVersion.Platform == PlatformID.Win32NT ? Path.Combine(Environment.GetEnvironmentVariable("PROGRAMDATA")!, "elastic", "apm-agent-dotnet", "logs") : "/var/log/elastic/apm-agent-dotnet"; @@ -98,41 +99,80 @@ internal enum GlobalLogTarget StdOut = 2 } +public static class LogEnvironmentVariables +{ + // ReSharper disable once InconsistentNaming + public const string ELASTIC_OTEL_FILE_LOG_LEVEL = nameof(ELASTIC_OTEL_FILE_LOG_LEVEL); + public const string ELASTIC_OTEL_FILE_LOG_DIRECTORY = nameof(ELASTIC_OTEL_FILE_LOG_DIRECTORY); + public const string ELASTIC_OTEL_LOG_TARGETS = nameof(ELASTIC_OTEL_LOG_TARGETS); + + public const string ELASTIC_APM_LOG_LEVEL = nameof(ELASTIC_APM_LOG_LEVEL); + public const string ELASTIC_APM_LOG_DIRECTORY = nameof(ELASTIC_APM_LOG_DIRECTORY); + + + // profiler logs are deprecated in favor of ELASTIC_OTEL_FILE_* + public const string ELASTIC_APM_PROFILER_LOG = nameof(ELASTIC_APM_PROFILER_LOG); + public const string ELASTIC_APM_PROFILER_LOG_DIR = nameof(ELASTIC_APM_PROFILER_LOG_DIR); + public const string ELASTIC_APM_PROFILER_LOG_TARGETS = nameof(ELASTIC_APM_PROFILER_LOG_TARGETS); + + // ReSharper enable once InconsistentNaming +} + internal readonly struct GlobalLogConfiguration { - private GlobalLogConfiguration(bool isActive, LogLevel logLevel, GlobalLogTarget logTarget, string logFilePath) : this() + private GlobalLogConfiguration(bool isActive, LogLevel logLevel, GlobalLogTarget logTarget, string logFileDirectory, string logFilePrefix) : this() { IsActive = isActive; LogLevel = logLevel; LogTargets = logTarget; - LogFilePath = logFilePath; + LogFileDirectory = logFileDirectory; + LogFilePrefix = logFilePrefix; + + AgentLogFilePath = CreateLogFileName(); } internal bool IsActive { get; } - internal string LogFilePath { get; } + internal string AgentLogFilePath { get; } internal LogLevel LogLevel { get; } internal GlobalLogTarget LogTargets { get; } - public override string ToString() => $"LogLevel: '{LogLevel}', LogFilePath: '{LogFilePath}'"; + internal string LogFileDirectory { get; } + internal string LogFilePrefix { get; } + + public override string ToString() => $"IsActive: '{IsActive}', Targets: '{LogTargets}', Level: '{LogLevel}', FilePath: '{AgentLogFilePath}'"; internal static GlobalLogConfiguration FromEnvironment(IDictionary environmentVariables = null) { var config = new EnvironmentLoggingConfiguration(environmentVariables); - var logLevel = config.GetLogLevel("ELASTIC_OTEL_FILE_LOG_LEVEL", "ELASTIC_APM_LOG_LEVEL", "ELASTIC_APM_PROFILER_LOG"); - var logFileDirectory = config.GetLogFilePath("ELASTIC_OTEL_FILE_LOG_DIRECTORY", "ELASTIC_APM_LOG_DIRECTORY", "ELASTIC_APM_PROFILER_LOG_DIR"); - var logFilePath = config.CreateLogFileName(logFileDirectory); - var logTarget = config.ParseLogTargets("ELASTIC_APM_PROFILER_LOG_TARGETS"); + var logLevel = config.GetLogLevel(ELASTIC_OTEL_FILE_LOG_LEVEL, ELASTIC_APM_LOG_LEVEL, ELASTIC_APM_PROFILER_LOG); + var logFileDirectory = config.GetLogFilePath(ELASTIC_OTEL_FILE_LOG_DIRECTORY, ELASTIC_APM_LOG_DIRECTORY, ELASTIC_APM_PROFILER_LOG_DIR); + var logFilePrefix = GetLogFilePrefix(); + var logTarget = config.ParseLogTargets(ELASTIC_OTEL_LOG_TARGETS, ELASTIC_APM_PROFILER_LOG_TARGETS); var isActive = config.AnyConfigured( - "ELASTIC_OTEL_FILE_LOG_LEVEL", - "ELASTIC_OTEL_FILE_LOG_DIRECTORY", - "ELASTIC_APM_LOG_DIRECTORY", - "ELASTIC_APM_PROFILER_LOG", - "ELASTIC_APM_PROFILER_LOG_DIR", - "ELASTIC_APM_PROFILER_LOG_TARGETS" + ELASTIC_OTEL_FILE_LOG_LEVEL, + ELASTIC_OTEL_FILE_LOG_DIRECTORY, + ELASTIC_APM_LOG_DIRECTORY, + ELASTIC_APM_PROFILER_LOG, + ELASTIC_APM_PROFILER_LOG_DIR, + ELASTIC_OTEL_LOG_TARGETS, + ELASTIC_APM_PROFILER_LOG_TARGETS ) && logTarget != GlobalLogTarget.None; - return new(isActive, logLevel, logTarget, logFilePath); + return new(isActive, logLevel, logTarget, logFileDirectory, logFilePrefix); + } + + private static string GetLogFilePrefix() + { + var process = Process.GetCurrentProcess(); + return $"{process.ProcessName}_{process.Id}_{Environment.TickCount}"; + } + + public string CreateLogFileName(string applicationName = "agent") + { + var logFileName = Path.Combine(LogFileDirectory, $"{LogFilePrefix}.{applicationName}.log"); + return logFileName; } + } diff --git a/src/Elastic.Apm/Logging/LogLevel.cs b/src/Elastic.Apm/Logging/LogLevel.cs index f0d012d3b..d3a2f881d 100644 --- a/src/Elastic.Apm/Logging/LogLevel.cs +++ b/src/Elastic.Apm/Logging/LogLevel.cs @@ -2,49 +2,52 @@ // 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 -namespace Elastic.Apm.Logging +#if APM_LOADER +namespace Elastic.Apm.Profiler.Managed.Loader; +#else +namespace Elastic.Apm.Logging; +#endif + +// https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.loglevel?view=aspnetcore-2.2 +public enum LogLevel { - // https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.loglevel?view=aspnetcore-2.2 - public enum LogLevel - { - /// - /// Logs that contain the most detailed messages. These messages may contain sensitive application data. These messages are - /// disabled by default and should never be enabled in a production environment. - /// - Trace = 0, - - /// - /// Logs that are used for interactive investigation during development. These logs should primarily contain information - /// useful for debugging and have no long-term value. - /// - Debug = 1, - - /// - /// Logs that track the general flow of the application. These logs should have long-term value. - /// - Information = 2, - - /// - /// Logs that highlight an abnormal or unexpected event in the application flow, but do not otherwise cause the application - /// execution to stop. - /// - Warning = 3, - - /// - /// Logs that highlight when the current flow of execution is stopped due to a failure. These should indicate a failure - /// in the current activity, not an application-wide failure. - /// - Error = 4, - - /// - /// Logs that describe an unrecoverable application or system crash, or a catastrophic failure that requires immediate - /// attention. - /// - Critical = 5, - - /// - /// Not used for writing log messages. Specifies that a logging category should not write any messages. - /// - None = 6 - } + /// + /// Logs that contain the most detailed messages. These messages may contain sensitive application data. These messages are + /// disabled by default and should never be enabled in a production environment. + /// + Trace = 0, + + /// + /// Logs that are used for interactive investigation during development. These logs should primarily contain information + /// useful for debugging and have no long-term value. + /// + Debug = 1, + + /// + /// Logs that track the general flow of the application. These logs should have long-term value. + /// + Information = 2, + + /// + /// Logs that highlight an abnormal or unexpected event in the application flow, but do not otherwise cause the application + /// execution to stop. + /// + Warning = 3, + + /// + /// Logs that highlight when the current flow of execution is stopped due to a failure. These should indicate a failure + /// in the current activity, not an application-wide failure. + /// + Error = 4, + + /// + /// Logs that describe an unrecoverable application or system crash, or a catastrophic failure that requires immediate + /// attention. + /// + Critical = 5, + + /// + /// Not used for writing log messages. Specifies that a logging category should not write any messages. + /// + None = 6 } diff --git a/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Elastic.Apm.Profiler.Managed.Loader.csproj b/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Elastic.Apm.Profiler.Managed.Loader.csproj index e826eeee6..f336602a1 100644 --- a/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Elastic.Apm.Profiler.Managed.Loader.csproj +++ b/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Elastic.Apm.Profiler.Managed.Loader.csproj @@ -5,11 +5,16 @@ net462;netcoreapp2.0 false false + APM_LOADER portable + + + + diff --git a/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Logger.cs b/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Logger.cs index 06f351bca..0003292a5 100644 --- a/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Logger.cs +++ b/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Logger.cs @@ -11,31 +11,22 @@ namespace Elastic.Apm.Profiler.Managed.Loader { - // match the log levels of the profiler logger - internal enum LogLevel - { - Trace = 0, - Debug = 1, - Info = 2, - Warn = 3, - Error = 4, - Off = 5, - } - internal static class Logger { static Logger() { - Level = GetLogLevel(LogLevel.Warn); - var logDirectory = GetLogDirectory(); - LogFile = GetLogFile(logDirectory); + var config = GlobalLogConfiguration.FromEnvironment(Environment.GetEnvironmentVariables()); + + Level = config.LogLevel; + LogFile = config.CreateLogFileName("managed_loader"); Levels = new Dictionary { - [LogLevel.Off] = "OFF ", + [LogLevel.None] = "OFF ", [LogLevel.Error] = "ERROR", - [LogLevel.Warn] = "WARN ", - [LogLevel.Info] = "INFO ", + [LogLevel.Warning] = "WARN ", + [LogLevel.Information] = "INFO ", [LogLevel.Debug] = "DEBUG", + [LogLevel.Critical] = "CRITICAL", [LogLevel.Trace] = "TRACE", }; } @@ -88,26 +79,6 @@ public static void Log(LogLevel level, string message, params object[] args) } } - private static string GetLogFile(string logDirectory) - { - if (logDirectory is null) - return null; - - var process = Process.GetCurrentProcess(); - return Path.Combine(logDirectory, $"Elastic.Apm.Profiler.Managed.Loader_{process.ProcessName}_{process.Id}.log"); - } - - private static LogLevel GetLogLevel(LogLevel defaultLevel) - { - var level = Environment.GetEnvironmentVariable("ELASTIC_APM_PROFILER_LOG"); - if (string.IsNullOrEmpty(level)) - return defaultLevel; - - return Enum.TryParse(level, true, out var parsedLevel) - ? parsedLevel - : defaultLevel; - } - private static string GetLogDirectory() { try diff --git a/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Startup.cs b/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Startup.cs index e3cf22fff..1c1e47713 100644 --- a/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Startup.cs +++ b/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Startup.cs @@ -16,7 +16,7 @@ public partial class Startup { static Startup() { - Logger.Log(LogLevel.Info, "Elastic.Apm.Profiler.Managed.Loader.Startup: Invoked "); + Logger.Log(LogLevel.Information, "Elastic.Apm.Profiler.Managed.Loader.Startup: Invoked "); Directory = ResolveDirectory(); try From 42cd9096ca36e7e0f0f494f013829e8c91733db3 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Mon, 3 Jun 2024 14:16:56 +0200 Subject: [PATCH 03/21] Startup hooks now listen to global log configuration --- .../Logging/GlobalLogConfiguration.cs | 13 ++++++++-- src/Elastic.Apm/Logging/LogLevel.cs | 2 ++ .../Elastic.Apm.StartupHook.Loader.csproj | 6 ----- .../Elastic.Apm.StartupHook.Loader/Loader.cs | 24 +++++-------------- .../ElasticApmAgentStartupHook.csproj | 5 ++++ .../StartupHookLogger.cs | 9 +++---- .../Config/GlobalLogConfigurationTests.cs | 8 +++---- 7 files changed, 31 insertions(+), 36 deletions(-) diff --git a/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs index 379b09eda..064a38888 100644 --- a/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs +++ b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs @@ -13,6 +13,10 @@ using static Elastic.Apm.Profiler.Managed.Loader.LogEnvironmentVariables; namespace Elastic.Apm.Profiler.Managed.Loader; +#elif STARTUP_HOOKS +using static ElasticApmStartupHook.LogEnvironmentVariables; + +namespace ElasticApmStartupHook; #else using static Elastic.Apm.Logging.LogEnvironmentVariables; @@ -84,7 +88,7 @@ public GlobalLogTarget ParseLogTargets(params string[] keys) } - private static string GetDefaultLogDirectory() => + internal static string GetDefaultLogDirectory() => Environment.OSVersion.Platform == PlatformID.Win32NT ? Path.Combine(Environment.GetEnvironmentVariable("PROGRAMDATA")!, "elastic", "apm-agent-dotnet", "logs") : "/var/log/elastic/apm-agent-dotnet"; @@ -115,6 +119,9 @@ public static class LogEnvironmentVariables public const string ELASTIC_APM_PROFILER_LOG_DIR = nameof(ELASTIC_APM_PROFILER_LOG_DIR); public const string ELASTIC_APM_PROFILER_LOG_TARGETS = nameof(ELASTIC_APM_PROFILER_LOG_TARGETS); + // deprected startup hooks logging configuration, we still listen to it to enable logging + public const string ELASTIC_APM_STARTUP_HOOKS_LOGGING = nameof(ELASTIC_APM_STARTUP_HOOKS_LOGGING); + // ReSharper enable once InconsistentNaming } @@ -156,7 +163,9 @@ internal static GlobalLogConfiguration FromEnvironment(IDictionary environmentVa ELASTIC_APM_PROFILER_LOG, ELASTIC_APM_PROFILER_LOG_DIR, ELASTIC_OTEL_LOG_TARGETS, - ELASTIC_APM_PROFILER_LOG_TARGETS + ELASTIC_APM_PROFILER_LOG_TARGETS, + ELASTIC_APM_STARTUP_HOOKS_LOGGING + ) && logTarget != GlobalLogTarget.None; return new(isActive, logLevel, logTarget, logFileDirectory, logFilePrefix); diff --git a/src/Elastic.Apm/Logging/LogLevel.cs b/src/Elastic.Apm/Logging/LogLevel.cs index d3a2f881d..c1220b988 100644 --- a/src/Elastic.Apm/Logging/LogLevel.cs +++ b/src/Elastic.Apm/Logging/LogLevel.cs @@ -4,6 +4,8 @@ #if APM_LOADER namespace Elastic.Apm.Profiler.Managed.Loader; +#elif STARTUP_HOOKS +namespace ElasticApmStartupHook; #else namespace Elastic.Apm.Logging; #endif diff --git a/src/startuphook/Elastic.Apm.StartupHook.Loader/Elastic.Apm.StartupHook.Loader.csproj b/src/startuphook/Elastic.Apm.StartupHook.Loader/Elastic.Apm.StartupHook.Loader.csproj index 19d9afedc..60984f437 100644 --- a/src/startuphook/Elastic.Apm.StartupHook.Loader/Elastic.Apm.StartupHook.Loader.csproj +++ b/src/startuphook/Elastic.Apm.StartupHook.Loader/Elastic.Apm.StartupHook.Loader.csproj @@ -16,10 +16,4 @@ - - - StartupHookLogger.cs - - - diff --git a/src/startuphook/Elastic.Apm.StartupHook.Loader/Loader.cs b/src/startuphook/Elastic.Apm.StartupHook.Loader/Loader.cs index 2a81154b6..fc87c735f 100644 --- a/src/startuphook/Elastic.Apm.StartupHook.Loader/Loader.cs +++ b/src/startuphook/Elastic.Apm.StartupHook.Loader/Loader.cs @@ -13,7 +13,7 @@ using Elastic.Apm.Extensions.Hosting; using Elastic.Apm.GrpcClient; using Elastic.Apm.Instrumentations.SqlClient; -using ElasticApmStartupHook; +using Elastic.Apm.Logging; namespace Elastic.Apm.StartupHook.Loader { @@ -22,26 +22,15 @@ namespace Elastic.Apm.StartupHook.Loader /// internal class Loader { - /// - /// The directory in which the executing assembly is located - /// - private static string AssemblyDirectory - { - get - { - var location = Assembly.GetExecutingAssembly().Location; - return Path.GetDirectoryName(location); - } - } - /// /// Initializes and starts the agent /// public static void Initialize() { - Agent.Setup(new AgentComponents()); + var agentComponents = new AgentComponents(); + Agent.Setup(agentComponents); - var logger = StartupHookLogger.Create(); + var logger = agentComponents.Logger; LoadDiagnosticSubscriber(new HttpDiagnosticsSubscriber(), logger); LoadDiagnosticSubscriber(new AspNetCoreDiagnosticSubscriber(), logger); LoadDiagnosticSubscriber(new EfCoreDiagnosticsSubscriber(), logger); @@ -51,7 +40,7 @@ public static void Initialize() HostBuilderExtensions.UpdateServiceInformation(Agent.Instance.Service); - static void LoadDiagnosticSubscriber(IDiagnosticsSubscriber diagnosticsSubscriber, StartupHookLogger logger) + static void LoadDiagnosticSubscriber(IDiagnosticsSubscriber diagnosticsSubscriber, IApmLogger logger) { try { @@ -59,8 +48,7 @@ static void LoadDiagnosticSubscriber(IDiagnosticsSubscriber diagnosticsSubscribe } catch (Exception e) { - logger.WriteLine($"Failed subscribing to {diagnosticsSubscriber.GetType().Name}, " + - $"Exception type: {e.GetType().Name}, message: {e.Message}"); + logger.Error()?.LogException(e, $"Failed subscribing to {diagnosticsSubscriber.GetType().Name}"); } } } diff --git a/src/startuphook/ElasticApmAgentStartupHook/ElasticApmAgentStartupHook.csproj b/src/startuphook/ElasticApmAgentStartupHook/ElasticApmAgentStartupHook.csproj index 5c79c06d5..f41a2b57a 100644 --- a/src/startuphook/ElasticApmAgentStartupHook/ElasticApmAgentStartupHook.csproj +++ b/src/startuphook/ElasticApmAgentStartupHook/ElasticApmAgentStartupHook.csproj @@ -4,7 +4,12 @@ ElasticApmAgentStartupHook false ElasticApmStartupHook + STARTUP_HOOKS + + + + diff --git a/src/startuphook/ElasticApmAgentStartupHook/StartupHookLogger.cs b/src/startuphook/ElasticApmAgentStartupHook/StartupHookLogger.cs index 7bbf532ef..69ea6fd0a 100644 --- a/src/startuphook/ElasticApmAgentStartupHook/StartupHookLogger.cs +++ b/src/startuphook/ElasticApmAgentStartupHook/StartupHookLogger.cs @@ -35,13 +35,10 @@ private StartupHookLogger(string logPath, bool enabled) /// public static StartupHookLogger Create() { - var startupHookEnvVar = Environment.GetEnvironmentVariable("DOTNET_STARTUP_HOOKS"); + var config = GlobalLogConfiguration.FromEnvironment(Environment.GetEnvironmentVariables()); + var path = config.CreateLogFileName("startup_hook"); - var startupHookDirectory = Path.GetDirectoryName(startupHookEnvVar); - var startupHookLoggingEnvVar = Environment.GetEnvironmentVariable("ELASTIC_APM_STARTUP_HOOKS_LOGGING"); - - return new StartupHookLogger(Path.Combine(startupHookDirectory, "ElasticApmAgentStartupHook.log"), - !string.IsNullOrEmpty(startupHookLoggingEnvVar)); + return new StartupHookLogger(path, config.IsActive); } public void WriteLine(string message) diff --git a/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs index 57a98e356..3cb6dab72 100644 --- a/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs +++ b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs @@ -18,8 +18,8 @@ public void Check_Defaults() var config = GlobalLogConfiguration.FromEnvironment(new Hashtable()); config.IsActive.Should().BeFalse(); config.LogLevel.Should().Be(LogLevel.Warning); - config.LogFilePath.Should().StartWith(EnvironmentLoggingConfiguration.GetDefaultLogDirectory()); - config.LogFilePath.Should().EndWith(".agent.log"); + config.AgentLogFilePath.Should().StartWith(EnvironmentLoggingConfiguration.GetDefaultLogDirectory()); + config.AgentLogFilePath.Should().EndWith(".agent.log"); config.LogTargets.Should().Be(GlobalLogTarget.File); } @@ -58,8 +58,8 @@ public void Check_LogDir_IsEvaluatedCorrectly() { var environment = new Hashtable { { "ELASTIC_APM_PROFILER_LOG_DIR", "/foo/bar" } }; var config = GlobalLogConfiguration.FromEnvironment(environment); - config.LogFilePath.Should().StartWith("/foo/bar"); - config.LogFilePath.Should().EndWith(".agent.log"); + config.AgentLogFilePath.Should().StartWith("/foo/bar"); + config.AgentLogFilePath.Should().EndWith(".agent.log"); } [Theory] From 6e205e882afaabb72ca58e4f6be9716e64ad4723 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Mon, 3 Jun 2024 15:21:42 +0200 Subject: [PATCH 04/21] Add more tests --- .../Logging/GlobalLogConfiguration.cs | 26 +++-- .../GlobalLogConfigurationPrecedenceTests.cs | 81 +++++++++++++ .../Config/GlobalLogConfigurationTests.cs | 109 +++++++++++++++--- 3 files changed, 188 insertions(+), 28 deletions(-) create mode 100644 test/Elastic.Apm.Tests/Config/GlobalLogConfigurationPrecedenceTests.cs diff --git a/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs index 064a38888..c475fac27 100644 --- a/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs +++ b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs @@ -75,17 +75,25 @@ public GlobalLogTarget ParseLogTargets(params string[] keys) return GlobalLogTarget.File; var logTargets = GlobalLogTarget.None; + var found = false; + foreach (var target in targets.Split(new [] {';'}, StringSplitOptions.RemoveEmptyEntries)) { - if (target.Trim().Equals("stdout", StringComparison.InvariantCultureIgnoreCase)) + if (IsSet(target, "stdout")) logTargets |= GlobalLogTarget.StdOut; - else if (target.Trim().Equals("file", StringComparison.InvariantCultureIgnoreCase)) + else if (IsSet(target, "file")) logTargets |= GlobalLogTarget.File; + else if (IsSet(target, "none")) + logTargets |= GlobalLogTarget.None; } - if (logTargets == GlobalLogTarget.None) - logTargets = GlobalLogTarget.File; - return logTargets; + return !found ? GlobalLogTarget.File : logTargets; + bool IsSet(string k, string v) + { + var b = k.Trim().Equals(v, StringComparison.InvariantCultureIgnoreCase); + if (b) found = true; + return b; + } } internal static string GetDefaultLogDirectory() => @@ -151,22 +159,22 @@ private GlobalLogConfiguration(bool isActive, LogLevel logLevel, GlobalLogTarget internal static GlobalLogConfiguration FromEnvironment(IDictionary environmentVariables = null) { var config = new EnvironmentLoggingConfiguration(environmentVariables); - var logLevel = config.GetLogLevel(ELASTIC_OTEL_FILE_LOG_LEVEL, ELASTIC_APM_LOG_LEVEL, ELASTIC_APM_PROFILER_LOG); - var logFileDirectory = config.GetLogFilePath(ELASTIC_OTEL_FILE_LOG_DIRECTORY, ELASTIC_APM_LOG_DIRECTORY, ELASTIC_APM_PROFILER_LOG_DIR); + var logLevel = config.GetLogLevel(ELASTIC_OTEL_FILE_LOG_LEVEL, ELASTIC_APM_PROFILER_LOG, ELASTIC_APM_LOG_LEVEL); + var logFileDirectory = config.GetLogFilePath(ELASTIC_OTEL_FILE_LOG_DIRECTORY, ELASTIC_APM_PROFILER_LOG_DIR, ELASTIC_APM_LOG_DIRECTORY); var logFilePrefix = GetLogFilePrefix(); var logTarget = config.ParseLogTargets(ELASTIC_OTEL_LOG_TARGETS, ELASTIC_APM_PROFILER_LOG_TARGETS); var isActive = config.AnyConfigured( ELASTIC_OTEL_FILE_LOG_LEVEL, ELASTIC_OTEL_FILE_LOG_DIRECTORY, + ELASTIC_OTEL_LOG_TARGETS, ELASTIC_APM_LOG_DIRECTORY, ELASTIC_APM_PROFILER_LOG, ELASTIC_APM_PROFILER_LOG_DIR, - ELASTIC_OTEL_LOG_TARGETS, ELASTIC_APM_PROFILER_LOG_TARGETS, ELASTIC_APM_STARTUP_HOOKS_LOGGING - ) && logTarget != GlobalLogTarget.None; + ) && logTarget != GlobalLogTarget.None && logLevel != LogLevel.None; return new(isActive, logLevel, logTarget, logFileDirectory, logFilePrefix); } diff --git a/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationPrecedenceTests.cs b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationPrecedenceTests.cs new file mode 100644 index 000000000..e63d04a41 --- /dev/null +++ b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationPrecedenceTests.cs @@ -0,0 +1,81 @@ +// 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 System.Collections; +using Elastic.Apm.Logging; +using FluentAssertions; +using Xunit; +using static Elastic.Apm.Logging.LogEnvironmentVariables; + +namespace Elastic.Apm.Tests.Config; + +public class GlobalLogConfigurationPrecedenceTests +{ + [Fact] + public void CheckLogLevelPrecedence() + { + var config = CreateConfig([ + (ELASTIC_OTEL_FILE_LOG_LEVEL, "trace"), + (ELASTIC_APM_PROFILER_LOG, "info"), + (ELASTIC_APM_LOG_LEVEL, "error"), + ]); + config.LogLevel.Should().Be(LogLevel.Trace); + + config = CreateConfig([ + (ELASTIC_APM_PROFILER_LOG, "info"), + (ELASTIC_APM_LOG_LEVEL, "error"), + ]); + config.LogLevel.Should().Be(LogLevel.Information); + + config = CreateConfig([ + (ELASTIC_APM_LOG_LEVEL, "error"), + ]); + config.LogLevel.Should().Be(LogLevel.Error); + } + + [Fact] + public void CheckLogDirPrecedence() + { + var config = CreateConfig([ + (ELASTIC_OTEL_FILE_LOG_DIRECTORY, nameof(ELASTIC_OTEL_FILE_LOG_DIRECTORY)), + (ELASTIC_APM_PROFILER_LOG_DIR, nameof(ELASTIC_APM_PROFILER_LOG_DIR)), + (ELASTIC_APM_LOG_DIRECTORY, nameof(ELASTIC_APM_LOG_DIRECTORY)), + ]); + config.LogFileDirectory.Should().Be(nameof(ELASTIC_OTEL_FILE_LOG_DIRECTORY)); + + config = CreateConfig([ + (ELASTIC_APM_PROFILER_LOG_DIR, nameof(ELASTIC_APM_PROFILER_LOG_DIR)), + (ELASTIC_APM_LOG_DIRECTORY, nameof(ELASTIC_APM_LOG_DIRECTORY)), + ]); + config.LogFileDirectory.Should().Be(nameof(ELASTIC_APM_PROFILER_LOG_DIR)); + + config = CreateConfig([ + (ELASTIC_APM_LOG_DIRECTORY, nameof(ELASTIC_APM_LOG_DIRECTORY)), + ]); + config.LogFileDirectory.Should().Be(nameof(ELASTIC_APM_LOG_DIRECTORY)); + } + + [Fact] + public void CheckLogTargetsPrecedence() + { + var config = CreateConfig([ + (ELASTIC_OTEL_LOG_TARGETS, "stdout"), + (ELASTIC_APM_PROFILER_LOG_TARGETS, "stdout;file"), + ]); + config.LogTargets.Should().Be(GlobalLogTarget.StdOut); + + config = CreateConfig([ + (ELASTIC_APM_PROFILER_LOG_TARGETS, "stdout;file"), + ]); + config.LogTargets.Should().Be(GlobalLogTarget.StdOut | GlobalLogTarget.File); + } + + private static GlobalLogConfiguration CreateConfig(params (string key, string v)[] values) + { + var environment = new Hashtable(); + foreach (var kv in values) environment.Add(kv.key, kv.v); + var config = GlobalLogConfiguration.FromEnvironment(environment); + return config; + } +} diff --git a/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs index 3cb6dab72..aae2f8cba 100644 --- a/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs +++ b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs @@ -7,6 +7,7 @@ using Elastic.Apm.Logging; using FluentAssertions; using Xunit; +using static Elastic.Apm.Logging.LogEnvironmentVariables; namespace Elastic.Apm.Tests.Config { @@ -22,6 +23,44 @@ public void Check_Defaults() config.AgentLogFilePath.Should().EndWith(".agent.log"); config.LogTargets.Should().Be(GlobalLogTarget.File); } + // + [Theory] + [InlineData(ELASTIC_OTEL_FILE_LOG_LEVEL, "Info")] + [InlineData(ELASTIC_APM_PROFILER_LOG, "Info")] + //only if explicitly specified to 'none' should we not default to file logging. + [InlineData(ELASTIC_OTEL_FILE_LOG_LEVEL, "BadEnumValue")] + [InlineData(ELASTIC_APM_PROFILER_LOG, "BadEnumValue")] + + [InlineData(ELASTIC_OTEL_FILE_LOG_DIRECTORY, "1")] + [InlineData(ELASTIC_APM_LOG_DIRECTORY, "1")] + [InlineData(ELASTIC_APM_PROFILER_LOG_DIR, "1")] + [InlineData(ELASTIC_APM_STARTUP_HOOKS_LOGGING, "1")] + //only if explicitly specified to 'none' should we not default to file logging. + [InlineData(ELASTIC_OTEL_LOG_TARGETS, "file")] + [InlineData(ELASTIC_OTEL_LOG_TARGETS, "BadEnumValue")] + [InlineData(ELASTIC_APM_PROFILER_LOG_TARGETS, "file")] + [InlineData(ELASTIC_APM_PROFILER_LOG_TARGETS, "BadEnumValue")] + public void CheckActivation(string environmentVariable, string value) + { + var config = GlobalLogConfiguration.FromEnvironment(new Hashtable { { environmentVariable, value}}); + config.IsActive.Should().BeTrue(); + } + + [Theory] + //only specifying apm_log_level not sufficient, needs explicit directory configuration + [InlineData(ELASTIC_APM_LOG_LEVEL, "Warning")] + //setting targets to none will result in no global trace logging + [InlineData(ELASTIC_OTEL_LOG_TARGETS, "None")] + [InlineData(ELASTIC_APM_PROFILER_LOG_TARGETS, "None")] + //setting file log level to none will result in no global trace logging + [InlineData(ELASTIC_OTEL_FILE_LOG_LEVEL, "None")] + //setting profiler log level to none will result in no global trace logging + [InlineData(ELASTIC_APM_PROFILER_LOG, "None")] + public void CheckNonActivation(string environmentVariable, string value) + { + var config = GlobalLogConfiguration.FromEnvironment(new Hashtable { { environmentVariable , value}}); + config.IsActive.Should().BeFalse(); + } [Theory] [InlineData("trace", LogLevel.Trace)] @@ -34,32 +73,51 @@ public void Check_Defaults() [InlineData("none", LogLevel.None)] public void Check_LogLevelValues_AreMappedCorrectly(string envVarValue, LogLevel logLevel) { - var environment = new Hashtable { { "ELASTIC_APM_PROFILER_LOG", envVarValue } }; - var config = GlobalLogConfiguration.FromEnvironment(environment); - config.IsActive.Should().BeTrue(); - config.LogLevel.Should().Be(logLevel); + Check(ELASTIC_APM_PROFILER_LOG, envVarValue, logLevel); + Check(ELASTIC_APM_LOG_LEVEL, envVarValue, logLevel); + Check(ELASTIC_OTEL_FILE_LOG_LEVEL, envVarValue, logLevel); + return; + + static void Check(string key, string envVarValue, LogLevel level) + { + var config = CreateConfig(key, envVarValue); + config.LogLevel.Should().Be(level, "{0}", key); + } } [Theory] - [InlineData(null, false)] - [InlineData("", false)] - [InlineData("foo", true)] - [InlineData("tracing", true)] - public void Check_InvalidLogLevelValues_AreMappedToDefaultWarn(string envVarValue, bool isActive) + [InlineData(null)] + [InlineData("")] + [InlineData("foo")] + [InlineData("tracing")] + public void Check_InvalidLogLevelValues_AreMappedToDefaultWarn(string envVarValue) { - var environment = new Hashtable { { "ELASTIC_APM_PROFILER_LOG", envVarValue } }; - var config = GlobalLogConfiguration.FromEnvironment(environment); - config.LogLevel.Should().Be(LogLevel.Warning); - config.IsActive.Should().Be(isActive); + Check(ELASTIC_APM_PROFILER_LOG, envVarValue); + Check(ELASTIC_APM_LOG_LEVEL, envVarValue); + Check(ELASTIC_OTEL_FILE_LOG_LEVEL, envVarValue); + return; + + static void Check(string key, string envVarValue) + { + var config = CreateConfig(key, envVarValue); + config.LogLevel.Should().Be(LogLevel.Warning, "{0}", key); + } } [Fact] public void Check_LogDir_IsEvaluatedCorrectly() { - var environment = new Hashtable { { "ELASTIC_APM_PROFILER_LOG_DIR", "/foo/bar" } }; - var config = GlobalLogConfiguration.FromEnvironment(environment); - config.AgentLogFilePath.Should().StartWith("/foo/bar"); - config.AgentLogFilePath.Should().EndWith(".agent.log"); + Check(ELASTIC_APM_PROFILER_LOG_DIR, "/foo/bar"); + Check(ELASTIC_APM_LOG_DIRECTORY, "/foo/bar"); + Check(ELASTIC_OTEL_FILE_LOG_DIRECTORY, "/foo/bar"); + return; + + static void Check(string key, string envVarValue) + { + var config = CreateConfig(key, envVarValue); + config.AgentLogFilePath.Should().StartWith("/foo/bar", "{0}", key); + config.AgentLogFilePath.Should().EndWith(".agent.log", "{0}", key); + } } [Theory] @@ -78,9 +136,22 @@ public void Check_LogDir_IsEvaluatedCorrectly() [InlineData("FILE;StdOut;stdout", GlobalLogTarget.File | GlobalLogTarget.StdOut)] internal void Check_LogTargets_AreEvaluatedCorrectly(string envVarValue, GlobalLogTarget targets) { - var environment = new Hashtable { { "ELASTIC_APM_PROFILER_LOG_TARGETS", envVarValue } }; + Check(ELASTIC_APM_PROFILER_LOG_TARGETS, envVarValue, targets); + Check(ELASTIC_OTEL_LOG_TARGETS, envVarValue, targets); + return; + + static void Check(string key, string envVarValue, GlobalLogTarget targets) + { + var config = CreateConfig(key, envVarValue); + config.LogTargets.Should().Be(targets, "{0}", key); + } + } + + private static GlobalLogConfiguration CreateConfig(string key, string envVarValue) + { + var environment = new Hashtable { { key, envVarValue } }; var config = GlobalLogConfiguration.FromEnvironment(environment); - config.LogTargets.Should().Be(targets); + return config; } } } From ec2cd25c714ac2225b57f742d30713dc09303e25 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Mon, 3 Jun 2024 15:40:10 +0200 Subject: [PATCH 05/21] Share logger between loader and managed profiler --- .../Logging/GlobalLogConfiguration.cs | 4 + src/Elastic.Apm/Logging/LogLevel.cs | 2 + ...Elastic.Apm.Profiler.Managed.Loader.csproj | 2 +- .../Logger.cs | 142 ++++++++--------- .../CallTarget/Handlers/IntegrationOptions.cs | 4 +- .../Elastic.Apm.Profiler.Managed.csproj | 8 + .../Elastic.Apm.Profiler.Managed/Logger.cs | 144 ------------------ .../Reflection/MethodBuilder.cs | 19 ++- .../Reflection/ModuleLookup.cs | 3 +- .../ElasticApmAgentStartupHook.csproj | 2 +- 10 files changed, 92 insertions(+), 238 deletions(-) delete mode 100644 src/profiler/Elastic.Apm.Profiler.Managed/Logger.cs diff --git a/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs index c475fac27..63047466a 100644 --- a/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs +++ b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs @@ -17,6 +17,10 @@ namespace Elastic.Apm.Profiler.Managed.Loader; using static ElasticApmStartupHook.LogEnvironmentVariables; namespace ElasticApmStartupHook; +#elif PROFILER_MANAGED +using static Elastic.Apm.Profiler.Managed.LogEnvironmentVariables; + +namespace Elastic.Apm.Profiler.Managed; #else using static Elastic.Apm.Logging.LogEnvironmentVariables; diff --git a/src/Elastic.Apm/Logging/LogLevel.cs b/src/Elastic.Apm/Logging/LogLevel.cs index c1220b988..ac8563021 100644 --- a/src/Elastic.Apm/Logging/LogLevel.cs +++ b/src/Elastic.Apm/Logging/LogLevel.cs @@ -6,6 +6,8 @@ namespace Elastic.Apm.Profiler.Managed.Loader; #elif STARTUP_HOOKS namespace ElasticApmStartupHook; +#elif PROFILER_MANAGED +namespace Elastic.Apm.Profiler.Managed; #else namespace Elastic.Apm.Logging; #endif diff --git a/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Elastic.Apm.Profiler.Managed.Loader.csproj b/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Elastic.Apm.Profiler.Managed.Loader.csproj index f336602a1..ec878909e 100644 --- a/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Elastic.Apm.Profiler.Managed.Loader.csproj +++ b/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Elastic.Apm.Profiler.Managed.Loader.csproj @@ -5,7 +5,7 @@ net462;netcoreapp2.0 false false - APM_LOADER + $(DefineConstants);APM_LOADER diff --git a/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Logger.cs b/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Logger.cs index 0003292a5..c0dd622de 100644 --- a/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Logger.cs +++ b/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Logger.cs @@ -5,105 +5,91 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Text; -namespace Elastic.Apm.Profiler.Managed.Loader +#if PROFILER_MANAGED +namespace Elastic.Apm.Profiler.Managed; +#else +namespace Elastic.Apm.Profiler.Managed.Loader; +#endif + +internal static class Logger { - internal static class Logger + static Logger() { - static Logger() + var config = GlobalLogConfiguration.FromEnvironment(Environment.GetEnvironmentVariables()); + + Level = config.LogLevel; + IsActive = config.IsActive; +#if PROFILER_MANAGED + LogFile = config.CreateLogFileName("profiler_managed"); +#else + LogFile = config.CreateLogFileName("profiler_managed_loader"); +#endif + Levels = new Dictionary { - var config = GlobalLogConfiguration.FromEnvironment(Environment.GetEnvironmentVariables()); + [LogLevel.None] = "OFF ", + [LogLevel.Error] = "ERROR", + [LogLevel.Warning] = "WARN ", + [LogLevel.Information] = "INFO ", + [LogLevel.Debug] = "DEBUG", + [LogLevel.Critical] = "CRITI", + [LogLevel.Trace] = "TRACE", + }; + } - Level = config.LogLevel; - LogFile = config.CreateLogFileName("managed_loader"); - Levels = new Dictionary - { - [LogLevel.None] = "OFF ", - [LogLevel.Error] = "ERROR", - [LogLevel.Warning] = "WARN ", - [LogLevel.Information] = "INFO ", - [LogLevel.Debug] = "DEBUG", - [LogLevel.Critical] = "CRITICAL", - [LogLevel.Trace] = "TRACE", - }; - } + private static readonly bool IsActive; + private static readonly LogLevel Level; + private static readonly string LogFile; + private static readonly Dictionary Levels; - private static readonly LogLevel Level; - private static readonly string LogFile; - private static readonly Dictionary Levels; + public static void Warn(string message, params object[] args) => Log(LogLevel.Warning, message, args); - public static void Log(LogLevel level, Exception exception, string message, params object[] args) - { - if (Level > level) - return; + public static void Debug(string message, params object[] args) => Log(LogLevel.Debug, message, args); - Log(level, $"{message}{Environment.NewLine}{exception}", args); - } + public static void Error(Exception exception, string message, params object[] args) => Log(LogLevel.Error, exception, message, args); - public static void Log(LogLevel level, string message, params object[] args) - { - if (Level > level) - return; + public static void Error(string message, params object[] args) => Log(LogLevel.Error, message, args); + + public static void Log(LogLevel level, Exception exception, string message, params object[] args) + { + if (!IsActive || Level > level) + return; + + Log(level, $"{message}{Environment.NewLine}{exception}", args); + } + public static void Log(LogLevel level, string message, params object[] args) + { + if (!IsActive || Level > level) + return; + + if (string.IsNullOrWhiteSpace(LogFile)) return; + + try + { try { - if (LogFile != null) - { - try - { - using (var stream = File.Open(LogFile, FileMode.Append, FileAccess.Write, FileShare.Read)) - using (var writer = new StreamWriter(stream, new UTF8Encoding(false))) - { - writer.Write($"[{DateTimeOffset.Now:O}] [{Levels[level]}] "); - writer.WriteLine(message, args); - writer.Flush(); - stream.Flush(true); - } - - return; - } - catch - { - // ignore - } - } - - Console.Error.WriteLine($"[{DateTimeOffset.Now:O}] [{Levels[level]}] {message}", args); + using var stream = File.Open(LogFile, FileMode.Append, FileAccess.Write, FileShare.Read); + using var writer = new StreamWriter(stream, new UTF8Encoding(false)); + writer.Write($"[{DateTimeOffset.Now:O}] [{Levels[level]}] "); + writer.WriteLine(message, args); + writer.Flush(); + stream.Flush(true); + + return; } catch { // ignore } - } - private static string GetLogDirectory() + Console.Error.WriteLine($"[{DateTimeOffset.Now:O}] [{Levels[level]}] {message}", args); + } + catch { - try - { - var logDirectory = Environment.GetEnvironmentVariable("ELASTIC_APM_PROFILER_LOG_DIR"); - if (string.IsNullOrEmpty(logDirectory)) - { - if (Environment.OSVersion.Platform == PlatformID.Win32NT) - { - var programData = Environment.GetEnvironmentVariable("PROGRAMDATA"); - logDirectory = !string.IsNullOrEmpty(programData) - ? Path.Combine(programData, "elastic", "apm-agent-dotnet", "logs") - : "."; - } - else - logDirectory = "/var/log/elastic/apm-agent-dotnet"; - } - - Directory.CreateDirectory(logDirectory); - return logDirectory; - } - catch - { - return null; - } + // ignore } } } diff --git a/src/profiler/Elastic.Apm.Profiler.Managed/CallTarget/Handlers/IntegrationOptions.cs b/src/profiler/Elastic.Apm.Profiler.Managed/CallTarget/Handlers/IntegrationOptions.cs index f80f0f42f..cea0679aa 100644 --- a/src/profiler/Elastic.Apm.Profiler.Managed/CallTarget/Handlers/IntegrationOptions.cs +++ b/src/profiler/Elastic.Apm.Profiler.Managed/CallTarget/Handlers/IntegrationOptions.cs @@ -32,14 +32,14 @@ internal static void LogException(Exception exception, string message = null) if (exception is DuckTypeException) { - Logger.Log(LogLevel.Warn, "DuckTypeException has been detected, the integration <{0}, {1}> will be disabled.", + Logger.Warn("DuckTypeException has been detected, the integration <{0}, {1}> will be disabled.", typeof(TIntegration).FullName, typeof(TTarget).FullName); _disableIntegration = true; } else if (exception is CallTargetInvokerException) { - Logger.Log(LogLevel.Warn, "CallTargetInvokerException has been detected, the integration <{0}, {1}> will be disabled.", + Logger.Warn("CallTargetInvokerException has been detected, the integration <{0}, {1}> will be disabled.", typeof(TIntegration).FullName, typeof(TTarget).FullName); _disableIntegration = true; diff --git a/src/profiler/Elastic.Apm.Profiler.Managed/Elastic.Apm.Profiler.Managed.csproj b/src/profiler/Elastic.Apm.Profiler.Managed/Elastic.Apm.Profiler.Managed.csproj index b63d15a15..cbe3fc9e8 100644 --- a/src/profiler/Elastic.Apm.Profiler.Managed/Elastic.Apm.Profiler.Managed.csproj +++ b/src/profiler/Elastic.Apm.Profiler.Managed/Elastic.Apm.Profiler.Managed.csproj @@ -3,8 +3,16 @@ net462;netstandard2.0;netstandard2.1 false + $(DefineConstants);PROFILER_MANAGED + + + + + + + diff --git a/src/profiler/Elastic.Apm.Profiler.Managed/Logger.cs b/src/profiler/Elastic.Apm.Profiler.Managed/Logger.cs deleted file mode 100644 index a7f16666d..000000000 --- a/src/profiler/Elastic.Apm.Profiler.Managed/Logger.cs +++ /dev/null @@ -1,144 +0,0 @@ -// 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 System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Text; - -namespace Elastic.Apm.Profiler.Managed -{ - // match the log levels of the profiler logger - internal enum LogLevel - { - Trace = 0, - Debug = 1, - Info = 2, - Warn = 3, - Error = 4, - Off = 5, - } - - internal static class Logger - { - static Logger() - { - Level = GetLogLevel(LogLevel.Warn); - var logDirectory = GetLogDirectory(); - LogFile = GetLogFile(logDirectory); - Levels = new Dictionary - { - [LogLevel.Off] = "OFF ", - [LogLevel.Error] = "ERROR", - [LogLevel.Warn] = "WARN ", - [LogLevel.Info] = "INFO ", - [LogLevel.Debug] = "DEBUG", - [LogLevel.Trace] = "TRACE", - }; - } - - private static readonly LogLevel Level; - private static readonly string LogFile; - private static readonly Dictionary Levels; - - public static void Log(LogLevel level, Exception exception, string message, params object[] args) - { - if (Level > level) - return; - - Log(level, $"{message}{Environment.NewLine}{exception}", args); - } - - public static void Debug(string message, params object[] args) => Log(LogLevel.Debug, message, args); - - public static void Error(Exception exception, string message, params object[] args) => Log(LogLevel.Error, exception, message, args); - - public static void Error(string message, params object[] args) => Log(LogLevel.Error, message, args); - - public static void Log(LogLevel level, string message, params object[] args) - { - if (Level > level) - return; - - try - { - if (LogFile != null) - { - try - { - using (var stream = File.Open(LogFile, FileMode.Append, FileAccess.Write, FileShare.Read)) - using (var writer = new StreamWriter(stream, new UTF8Encoding(false))) - { - writer.Write($"[{DateTimeOffset.Now:O}] [{Levels[level]}] "); - writer.WriteLine(message, args); - writer.Flush(); - stream.Flush(true); - } - - return; - } - catch - { - // ignore - } - } - - Console.Error.WriteLine($"[{DateTimeOffset.Now:O}] [{Levels[level]}] {message}", args); - } - catch - { - // ignore - } - } - - private static string GetLogFile(string logDirectory) - { - if (logDirectory is null) - return null; - - var process = Process.GetCurrentProcess(); - return Path.Combine(logDirectory, $"Elastic.Apm.Profiler.Managed_{process.ProcessName}_{process.Id}.log"); - } - - private static LogLevel GetLogLevel(LogLevel defaultLevel) - { - var level = Environment.GetEnvironmentVariable("ELASTIC_APM_PROFILER_LOG"); - if (string.IsNullOrEmpty(level)) - return defaultLevel; - - return Enum.TryParse(level, true, out var parsedLevel) - ? parsedLevel - : defaultLevel; - } - - private static string GetLogDirectory() - { - try - { - var logDirectory = Environment.GetEnvironmentVariable("ELASTIC_APM_PROFILER_LOG_DIR"); - if (string.IsNullOrEmpty(logDirectory)) - { - if (Environment.OSVersion.Platform == PlatformID.Win32NT) - { - var programData = Environment.GetEnvironmentVariable("PROGRAMDATA"); - logDirectory = !string.IsNullOrEmpty(programData) - ? Path.Combine(programData, "elastic", "apm-agent-dotnet", "logs") - : "."; - } - else - logDirectory = "/var/log/elastic/apm-agent-dotnet"; - } - - Directory.CreateDirectory(logDirectory); - return logDirectory; - } - catch - { - return null; - } - } - } -} diff --git a/src/profiler/Elastic.Apm.Profiler.Managed/Reflection/MethodBuilder.cs b/src/profiler/Elastic.Apm.Profiler.Managed/Reflection/MethodBuilder.cs index 542157c46..13e43b309 100644 --- a/src/profiler/Elastic.Apm.Profiler.Managed/Reflection/MethodBuilder.cs +++ b/src/profiler/Elastic.Apm.Profiler.Managed/Reflection/MethodBuilder.cs @@ -237,7 +237,7 @@ private TDelegate EmitDelegate() } } else - Logger.Log(LogLevel.Warn, "Unable to resolve module version id {0}. Using method builder fallback.", _moduleVersionId); + Logger.Warn("Unable to resolve module version id {0}. Using method builder fallback.", _moduleVersionId); MethodInfo methodInfo = null; @@ -384,26 +384,25 @@ private MethodInfo VerifyMethodFromToken(MethodInfo methodInfo) if (!string.Equals(_methodName, methodInfo.Name)) { - Logger.Log(LogLevel.Warn, $"Method name mismatch: {detailMessage}"); + Logger.Warn($"Method name mismatch: {detailMessage}"); return null; } if (!GenericsAreViable(methodInfo)) { - Logger.Log(LogLevel.Warn, $"Generics not viable: {detailMessage}"); + Logger.Warn($"Generics not viable: {detailMessage}"); return null; } if (!ParametersAreViable(methodInfo)) { - Logger.Log(LogLevel.Warn, $"Parameters not viable: {detailMessage}"); + Logger.Warn($"Parameters not viable: {detailMessage}"); return null; } if (!methodInfo.IsStatic && !methodInfo.ReflectedType.IsAssignableFrom(_concreteType)) { - Logger.Log(LogLevel.Warn, - $"{_concreteType} cannot be assigned to the type containing the MethodInfo representing the instance method: {detailMessage}"); + Logger.Warn($"{_concreteType} cannot be assigned to the type containing the MethodInfo representing the instance method: {detailMessage}"); return null; } @@ -453,7 +452,7 @@ private MethodInfo TryFindMethod() { var logDetail = $"mdToken {_mdToken} on {_concreteTypeName}.{_methodName} in {_resolutionModule?.FullyQualifiedName ?? "NULL"}, {_resolutionModule?.ModuleVersionId ?? _moduleVersionId}"; - Logger.Log(LogLevel.Warn, $"Using fallback method matching ({logDetail})"); + Logger.Warn($"Using fallback method matching ({logDetail})"); var methods = _concreteType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); @@ -495,7 +494,7 @@ private MethodInfo TryFindMethod() if (methods.Length == 1) { - Logger.Log(LogLevel.Info, $"Resolved by name and namespaceName filters ({logDetail})"); + Logger.Warn($"Resolved by name and namespaceName filters ({logDetail})"); return methods[0]; } @@ -506,7 +505,7 @@ private MethodInfo TryFindMethod() if (methods.Length == 1) { - Logger.Log(LogLevel.Info, $"Resolved by viable parameters ({logDetail})"); + Logger.Warn($"Resolved by viable parameters ({logDetail})"); return methods[0]; } @@ -517,7 +516,7 @@ private MethodInfo TryFindMethod() if (methods.Length == 1) { - Logger.Log(LogLevel.Info, $"Resolved by viable generics ({logDetail})"); + Logger.Warn($"Resolved by viable generics ({logDetail})"); return methods[0]; } diff --git a/src/profiler/Elastic.Apm.Profiler.Managed/Reflection/ModuleLookup.cs b/src/profiler/Elastic.Apm.Profiler.Managed/Reflection/ModuleLookup.cs index cb206ba12..32f86659e 100644 --- a/src/profiler/Elastic.Apm.Profiler.Managed/Reflection/ModuleLookup.cs +++ b/src/profiler/Elastic.Apm.Profiler.Managed/Reflection/ModuleLookup.cs @@ -55,8 +55,7 @@ public static Module Get(Guid moduleVersionId) { if (_shortCircuitLogicHasLogged) return null; - Logger.Log(LogLevel.Warn, - "Elastic APM is unable to continue attempting module lookups for this AppDomain. Falling back to legacy method lookups."); + Logger.Warn("Elastic APM is unable to continue attempting module lookups for this AppDomain. Falling back to legacy method lookups."); _shortCircuitLogicHasLogged = true; } diff --git a/src/startuphook/ElasticApmAgentStartupHook/ElasticApmAgentStartupHook.csproj b/src/startuphook/ElasticApmAgentStartupHook/ElasticApmAgentStartupHook.csproj index f41a2b57a..f75da942b 100644 --- a/src/startuphook/ElasticApmAgentStartupHook/ElasticApmAgentStartupHook.csproj +++ b/src/startuphook/ElasticApmAgentStartupHook/ElasticApmAgentStartupHook.csproj @@ -4,7 +4,7 @@ ElasticApmAgentStartupHook false ElasticApmStartupHook - STARTUP_HOOKS + $(DefineConstants);STARTUP_HOOKS From cd548ea04889203dc9bdf6f4e5ebcb72d821ee67 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Mon, 3 Jun 2024 15:41:44 +0200 Subject: [PATCH 06/21] remove namespace --- src/Elastic.Apm/Logging/GlobalLogConfiguration.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs index 63047466a..d9d21c1c5 100644 --- a/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs +++ b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs @@ -7,7 +7,6 @@ using System.Diagnostics; using System.IO; using System.Linq; -using System.Runtime.InteropServices; #if APM_LOADER using static Elastic.Apm.Profiler.Managed.Loader.LogEnvironmentVariables; From 33e57fa4a349663bad973173debbf4811f375d34 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Mon, 3 Jun 2024 15:44:36 +0200 Subject: [PATCH 07/21] clearer compile constants --- src/Elastic.Apm/Logging/GlobalLogConfiguration.cs | 12 ++++-------- src/Elastic.Apm/Logging/LogLevel.cs | 6 +++--- .../Elastic.Apm.Profiler.Managed.Loader.csproj | 2 +- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs index d9d21c1c5..b1b84996d 100644 --- a/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs +++ b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs @@ -8,21 +8,17 @@ using System.IO; using System.Linq; -#if APM_LOADER +#if PROFILER_MANAGED_LOADER using static Elastic.Apm.Profiler.Managed.Loader.LogEnvironmentVariables; - namespace Elastic.Apm.Profiler.Managed.Loader; -#elif STARTUP_HOOKS -using static ElasticApmStartupHook.LogEnvironmentVariables; - -namespace ElasticApmStartupHook; #elif PROFILER_MANAGED using static Elastic.Apm.Profiler.Managed.LogEnvironmentVariables; - namespace Elastic.Apm.Profiler.Managed; +#elif STARTUP_HOOKS +using static ElasticApmStartupHook.LogEnvironmentVariables; +namespace ElasticApmStartupHook; #else using static Elastic.Apm.Logging.LogEnvironmentVariables; - namespace Elastic.Apm.Logging; #endif diff --git a/src/Elastic.Apm/Logging/LogLevel.cs b/src/Elastic.Apm/Logging/LogLevel.cs index ac8563021..05bab8a8f 100644 --- a/src/Elastic.Apm/Logging/LogLevel.cs +++ b/src/Elastic.Apm/Logging/LogLevel.cs @@ -2,12 +2,12 @@ // 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 -#if APM_LOADER +#if PROFILER_MANAGED_LOADER namespace Elastic.Apm.Profiler.Managed.Loader; -#elif STARTUP_HOOKS -namespace ElasticApmStartupHook; #elif PROFILER_MANAGED namespace Elastic.Apm.Profiler.Managed; +#elif STARTUP_HOOKS +namespace ElasticApmStartupHook; #else namespace Elastic.Apm.Logging; #endif diff --git a/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Elastic.Apm.Profiler.Managed.Loader.csproj b/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Elastic.Apm.Profiler.Managed.Loader.csproj index ec878909e..373ae5b73 100644 --- a/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Elastic.Apm.Profiler.Managed.Loader.csproj +++ b/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Elastic.Apm.Profiler.Managed.Loader.csproj @@ -5,7 +5,7 @@ net462;netcoreapp2.0 false false - $(DefineConstants);APM_LOADER + $(DefineConstants);PROFILER_MANAGED_LOADER From b9fccc959ecc7ea39389ba5dedd22efbb7ef4e93 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Mon, 3 Jun 2024 16:15:13 +0200 Subject: [PATCH 08/21] Introduce new global environment configs to profiler --- .../elastic_apm_profiler/src/profiler/env.rs | 77 ++++++++++++------- 1 file changed, 51 insertions(+), 26 deletions(-) diff --git a/src/profiler/elastic_apm_profiler/src/profiler/env.rs b/src/profiler/elastic_apm_profiler/src/profiler/env.rs index bb8aea6cd..6d630765f 100644 --- a/src/profiler/elastic_apm_profiler/src/profiler/env.rs +++ b/src/profiler/elastic_apm_profiler/src/profiler/env.rs @@ -23,28 +23,33 @@ use log4rs::{ use once_cell::sync::Lazy; use std::time::SystemTime; use std::{collections::HashSet, fs::File, io::BufReader, path::PathBuf, str::FromStr}; +use serde::__private; const APP_POOL_ID_ENV_VAR: &str = "APP_POOL_ID"; const DOTNET_CLI_TELEMETRY_PROFILE_ENV_VAR: &str = "DOTNET_CLI_TELEMETRY_PROFILE"; const COMPLUS_LOADEROPTIMIZATION: &str = "COMPLUS_LOADEROPTIMIZATION"; -const ELASTIC_APM_PROFILER_CALLTARGET_ENABLED_ENV_VAR: &str = - "ELASTIC_APM_PROFILER_CALLTARGET_ENABLED"; -const ELASTIC_APM_PROFILER_DISABLE_OPTIMIZATIONS_ENV_VAR: &str = - "ELASTIC_APM_PROFILER_DISABLE_OPTIMIZATIONS"; +const ELASTIC_APM_PROFILER_CALLTARGET_ENABLED_ENV_VAR: &str = "ELASTIC_APM_PROFILER_CALLTARGET_ENABLED"; +const ELASTIC_APM_PROFILER_DISABLE_OPTIMIZATIONS_ENV_VAR: &str = "ELASTIC_APM_PROFILER_DISABLE_OPTIMIZATIONS"; const ELASTIC_APM_PROFILER_ENABLE_INLINING_ENV_VAR: &str = "ELASTIC_APM_PROFILER_ENABLE_INLINING"; -const ELASTIC_APM_PROFILER_EXCLUDE_INTEGRATIONS_ENV_VAR: &str = - "ELASTIC_APM_PROFILER_EXCLUDE_INTEGRATIONS"; -const ELASTIC_APM_PROFILER_EXCLUDE_PROCESSES_ENV_VAR: &str = - "ELASTIC_APM_PROFILER_EXCLUDE_PROCESSES"; -const ELASTIC_APM_PROFILER_EXCLUDE_SERVICE_NAMES_ENV_VAR: &str = - "ELASTIC_APM_PROFILER_EXCLUDE_SERVICE_NAMES"; +const ELASTIC_APM_PROFILER_EXCLUDE_INTEGRATIONS_ENV_VAR: &str = "ELASTIC_APM_PROFILER_EXCLUDE_INTEGRATIONS"; +const ELASTIC_APM_PROFILER_EXCLUDE_PROCESSES_ENV_VAR: &str = "ELASTIC_APM_PROFILER_EXCLUDE_PROCESSES"; +const ELASTIC_APM_PROFILER_EXCLUDE_SERVICE_NAMES_ENV_VAR: &str = "ELASTIC_APM_PROFILER_EXCLUDE_SERVICE_NAMES"; const ELASTIC_APM_PROFILER_HOME_ENV_VAR: &str = "ELASTIC_APM_PROFILER_HOME"; const ELASTIC_APM_PROFILER_INTEGRATIONS_ENV_VAR: &str = "ELASTIC_APM_PROFILER_INTEGRATIONS"; +const ELASTIC_APM_PROFILER_LOG_IL_ENV_VAR: &str = "ELASTIC_APM_PROFILER_LOG_IL"; + +const ELASTIC_APM_PROFILER_LOG_TARGETS_ENV_VAR: &str = "ELASTIC_APM_PROFILER_LOG_TARGETS"; +const ELASTIC_OTEL_LOG_TARGETS_ENV_VAR: &str = "ELASTIC_OTEL_LOG_TARGETS"; + const ELASTIC_APM_PROFILER_LOG_DIR_ENV_VAR: &str = "ELASTIC_APM_PROFILER_LOG_DIR"; const ELASTIC_APM_PROFILER_LOG_ENV_VAR: &str = "ELASTIC_APM_PROFILER_LOG"; -const ELASTIC_APM_PROFILER_LOG_TARGETS_ENV_VAR: &str = "ELASTIC_APM_PROFILER_LOG_TARGETS"; -const ELASTIC_APM_PROFILER_LOG_IL_ENV_VAR: &str = "ELASTIC_APM_PROFILER_LOG_IL"; + +const ELASTIC_OTEL_FILE_LOG_LEVEL_ENV_VAR: &str = "ELASTIC_OTEL_FILE_LOG_LEVEL"; +const ELASTIC_OTEL_FILE_LOG_DIRECTORY_ENV_VAR: &str = "ELASTIC_OTEL_FILE_LOG_DIRECTORY"; + +const ELASTIC_APM_LOG_LEVEL_ENV_VAR: &str = "ELASTIC_APM_LOG_LEVEL"; +const ELASTIC_APM_LOG_DIRECTORY_ENV_VAR: &str = "ELASTIC_APM_LOG_DIRECTORY"; const ELASTIC_APM_SERVICE_NAME_ENV_VAR: &str = "ELASTIC_APM_SERVICE_NAME"; @@ -173,16 +178,24 @@ pub fn enable_inlining(default: bool) -> bool { read_bool_env_var(ELASTIC_APM_PROFILER_ENABLE_INLINING_ENV_VAR, default) } +fn to_target(value: String) -> HashSet { + value + .split(';') + .into_iter() + .filter_map(|s| match s.to_lowercase().as_str() { + out if out == "file" || out == "stdout" => Some(out.into()), + _ => None, + }) + .collect() +} + fn read_log_targets_from_env_var() -> HashSet { - let mut set = match std::env::var(ELASTIC_APM_PROFILER_LOG_TARGETS_ENV_VAR) { - Ok(value) => value - .split(';') - .into_iter() - .filter_map(|s| match s.to_lowercase().as_str() { - out if out == "file" || out == "stdout" => Some(out.into()), - _ => None, - }) - .collect(), + let mut set = match ( + std::env::var(ELASTIC_OTEL_LOG_TARGETS_ENV_VAR), + std::env::var(ELASTIC_APM_PROFILER_LOG_TARGETS_ENV_VAR) + ) { + (Ok(value), _) => to_target(value), + (_, Ok(value)) => to_target(value), _ => HashSet::with_capacity(1), }; @@ -193,8 +206,14 @@ fn read_log_targets_from_env_var() -> HashSet { } pub fn read_log_level_from_env_var(default: LevelFilter) -> LevelFilter { - match std::env::var(ELASTIC_APM_PROFILER_LOG_ENV_VAR) { - Ok(value) => LevelFilter::from_str(value.as_str()).unwrap_or(default), + match ( + std::env::var(ELASTIC_OTEL_FILE_LOG_LEVEL_ENV_VAR), + std::env::var(ELASTIC_APM_PROFILER_LOG_ENV_VAR), + std::env::var(ELASTIC_APM_LOG_DIRECTORY_ENV_VAR) + ) { + (Ok(value), _, _) => LevelFilter::from_str(value.as_str()).unwrap_or(default), + (None, Ok(value), _) => LevelFilter::from_str(value.as_str()).unwrap_or(default), + (None, None, Ok(value)) => LevelFilter::from_str(value.as_str()).unwrap_or(default), _ => default, } } @@ -291,9 +310,15 @@ fn get_home_log_dir() -> PathBuf { } fn get_log_dir() -> PathBuf { - match std::env::var(ELASTIC_APM_PROFILER_LOG_DIR_ENV_VAR) { - Ok(path) => PathBuf::from(path), - Err(_) => get_default_log_dir(), + match ( + std::env::var(ELASTIC_OTEL_FILE_LOG_DIRECTORY_ENV_VAR), + std::env::var(ELASTIC_APM_PROFILER_LOG_DIR_ENV_VAR), + std::env::var(ELASTIC_APM_LOG_DIRECTORY_ENV_VAR), + ) { + (Ok(path), _, _) => PathBuf::from(path), + (_, Ok(path), _) => PathBuf::from(path), + (_, _, Ok(path)) => PathBuf::from(path), + _ => get_default_log_dir(), } } From c6d1c7cbbd2a7cbf77deec0331098554a1701860 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Mon, 3 Jun 2024 16:28:42 +0200 Subject: [PATCH 09/21] move profiler tests over to otel log config and update docs --- docs/setup-auto-instrumentation.asciidoc | 15 ++++++++++++--- .../BasicTests.cs | 3 ++- .../ExcludeTests.cs | 7 ++++--- .../ProfiledApplication.cs | 7 ++++--- .../SatelliteAssemblyTests.cs | 3 ++- 5 files changed, 24 insertions(+), 11 deletions(-) diff --git a/docs/setup-auto-instrumentation.asciidoc b/docs/setup-auto-instrumentation.asciidoc index ca5e49b77..0c36d0907 100644 --- a/docs/setup-auto-instrumentation.asciidoc +++ b/docs/setup-auto-instrumentation.asciidoc @@ -394,7 +394,7 @@ A semi-colon separated list of APM service names to exclude from auto-instrument Values defined are checked against the value of <> environment variable. -`ELASTIC_APM_PROFILER_LOG` _(optional)_:: +`ELASTIC_OTEL_FILE_LOG_LEVEL` _(optional)_:: The log level at which the profiler should log. Valid values are @@ -405,11 +405,15 @@ The log level at which the profiler should log. Valid values are * error * none + The default value is `warn`. More verbose log levels like `trace` and `debug` can affect the runtime performance of profiler auto instrumentation, so are recommended _only_ for diagnostics purposes. -`ELASTIC_APM_PROFILER_LOG_DIR` _(optional)_:: +This takes precedence over the now deprecated `ELASTIC_APM_PROFILER_LOG` + + +`ELASTIC_OTEL_FILE_LOG_DIRECTORY` _(optional)_:: The directory in which to write profiler log files. If unset, defaults to @@ -420,6 +424,8 @@ If the default directory cannot be written to for some reason, the profiler will try to write log files to a `logs` directory in the home directory specified by `ELASTIC_APM_PROFILER_HOME` environment variable. +This takes precedence over the now deprecated `ELASTIC_APM_PROFILER_LOG_DIR` + [IMPORTANT] -- The user account under which the profiler process runs must have permission to @@ -428,7 +434,8 @@ on IIS, the https://learn.microsoft.com/en-us/iis/manage/configuring-security/ap has write permissions in the target directory. -- -`ELASTIC_APM_PROFILER_LOG_TARGETS` _(optional)_:: + +`ELASTIC_OTEL_LOG_TARGETS` _(optional)_:: A semi-colon separated list of targets for profiler logs. Valid values are @@ -437,3 +444,5 @@ A semi-colon separated list of targets for profiler logs. Valid values are The default value is `file`, which logs to the directory specified by `ELASTIC_APM_PROFILER_LOG_DIR` environment variable. + +This takes precedence over the now deprecated `ELASTIC_APM_PROFILER_LOG_TARGETS` \ No newline at end of file diff --git a/test/profiler/Elastic.Apm.Profiler.Managed.Tests/BasicTests.cs b/test/profiler/Elastic.Apm.Profiler.Managed.Tests/BasicTests.cs index 1a956ecb9..e60c24d16 100644 --- a/test/profiler/Elastic.Apm.Profiler.Managed.Tests/BasicTests.cs +++ b/test/profiler/Elastic.Apm.Profiler.Managed.Tests/BasicTests.cs @@ -12,6 +12,7 @@ using Elastic.Apm.Tests.Utilities.XUnit; using FluentAssertions; using Xunit.Abstractions; +using static Elastic.Apm.Logging.LogEnvironmentVariables; namespace Elastic.Apm.Profiler.Managed.Tests; @@ -38,7 +39,7 @@ public async Task AgentVersionTest() { ["ELASTIC_APM_SERVER_URL"] = $"http://localhost:{port}", ["ELASTIC_APM_DISABLE_METRICS"] = "*", - ["ELASTIC_APM_PROFILER_LOG_TARGETS"] = "file;stdout", + [ELASTIC_OTEL_LOG_TARGETS] = "file;stdout", }; profiledApplication.Start( diff --git a/test/profiler/Elastic.Apm.Profiler.Managed.Tests/ExcludeTests.cs b/test/profiler/Elastic.Apm.Profiler.Managed.Tests/ExcludeTests.cs index f58479399..0564df106 100644 --- a/test/profiler/Elastic.Apm.Profiler.Managed.Tests/ExcludeTests.cs +++ b/test/profiler/Elastic.Apm.Profiler.Managed.Tests/ExcludeTests.cs @@ -13,6 +13,7 @@ using FluentAssertions; using Xunit; using Xunit.Abstractions; +using static Elastic.Apm.Logging.LogEnvironmentVariables; namespace Elastic.Apm.Profiler.Managed.Tests; @@ -39,7 +40,7 @@ public async Task ShouldNotInstrumentExcludedIntegrations() ["ELASTIC_APM_SERVER_URL"] = $"http://localhost:{port}", ["ELASTIC_APM_PROFILER_EXCLUDE_INTEGRATIONS"] = "SqliteCommand;AdoNet", ["ELASTIC_APM_DISABLE_METRICS"] = "*", - ["ELASTIC_APM_PROFILER_LOG_TARGETS"] = "file;stdout" + [ELASTIC_OTEL_LOG_TARGETS] = "file;stdout" }; profiledApplication.Start( @@ -95,7 +96,7 @@ public async Task ShouldNotInstrumentExcludedProcess(string targetFramework, str { ["ELASTIC_APM_SERVER_URL"] = $"http://localhost:{port}", ["ELASTIC_APM_DISABLE_METRICS"] = "*", - ["ELASTIC_APM_PROFILER_LOG_TARGETS"] = "file;stdout", + [ELASTIC_OTEL_LOG_TARGETS] = "file;stdout", ["ELASTIC_APM_PROFILER_EXCLUDE_PROCESSES"] = excludeProcess }; @@ -142,7 +143,7 @@ public async Task ShouldNotInstrumentExcludedServiceName() { ["ELASTIC_APM_SERVER_URL"] = $"http://localhost:{port}", ["ELASTIC_APM_DISABLE_METRICS"] = "*", - ["ELASTIC_APM_PROFILER_LOG_TARGETS"] = "file;stdout", + [ELASTIC_OTEL_LOG_TARGETS] = "file;stdout", ["ELASTIC_APM_SERVICE_NAME"] = serviceName, ["ELASTIC_APM_PROFILER_EXCLUDE_SERVICE_NAMES"] = serviceName }; diff --git a/test/profiler/Elastic.Apm.Profiler.Managed.Tests/ProfiledApplication.cs b/test/profiler/Elastic.Apm.Profiler.Managed.Tests/ProfiledApplication.cs index 274ed3858..b52da32ef 100644 --- a/test/profiler/Elastic.Apm.Profiler.Managed.Tests/ProfiledApplication.cs +++ b/test/profiler/Elastic.Apm.Profiler.Managed.Tests/ProfiledApplication.cs @@ -13,6 +13,7 @@ using Elastic.Apm.Tests.Utilities; using ProcNet; using ProcNet.Std; +using static Elastic.Apm.Logging.LogEnvironmentVariables; namespace Elastic.Apm.Profiler.Managed.Tests { @@ -143,11 +144,11 @@ public void Start( environmentVariables["ELASTIC_APM_PROFILER_INTEGRATIONS"] = Path.Combine(SolutionPaths.Root, "src", "profiler", "Elastic.Apm.Profiler.Managed", "integrations.yml"); - environmentVariables["ELASTIC_APM_PROFILER_LOG"] = "trace"; + environmentVariables[ELASTIC_OTEL_FILE_LOG_LEVEL] = "trace"; // log to relative logs directory for managed loader - environmentVariables["ELASTIC_APM_PROFILER_LOG_DIR"] = Path.Combine(SolutionPaths.Root, "logs"); + environmentVariables[ELASTIC_OTEL_FILE_LOG_DIRECTORY] = Path.Combine(SolutionPaths.Root, "logs"); - environmentVariables["ELASTIC_APM_PROFILER_LOG_TARGETS"] = "file;stdout"; + environmentVariables[ELASTIC_OTEL_LOG_TARGETS] = "file;stdout"; //environmentVariables["ELASTIC_APM_PROFILER_LOG_IL"] = "true"; // use the .exe for net462 diff --git a/test/profiler/Elastic.Apm.Profiler.Managed.Tests/SatelliteAssemblyTests.cs b/test/profiler/Elastic.Apm.Profiler.Managed.Tests/SatelliteAssemblyTests.cs index ca3ba9cc9..ac9a5c01a 100644 --- a/test/profiler/Elastic.Apm.Profiler.Managed.Tests/SatelliteAssemblyTests.cs +++ b/test/profiler/Elastic.Apm.Profiler.Managed.Tests/SatelliteAssemblyTests.cs @@ -12,6 +12,7 @@ using FluentAssertions; using Xunit; using Xunit.Abstractions; +using static Elastic.Apm.Logging.LogEnvironmentVariables; namespace Elastic.Apm.Profiler.Managed.Tests; @@ -36,7 +37,7 @@ public async Task CorrectlyReadSatelliteAssemblyMetadata() { ["ELASTIC_APM_SERVER_URL"] = $"http://localhost:{port}", ["ELASTIC_APM_DISABLE_METRICS"] = "*", - ["ELASTIC_APM_PROFILER_LOG_TARGETS"] = "file;stdout", + [ELASTIC_OTEL_LOG_TARGETS] = "file;stdout", ["ELASTIC_APM_PROFILER_LOG"] = "debug", }; From 70edc92e0b71feebadabe43e766cac39a0af28e2 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Mon, 3 Jun 2024 16:42:00 +0200 Subject: [PATCH 10/21] update troubleshotting.asciidoc --- docs/troubleshooting.asciidoc | 108 +++++++++++++++++----------------- 1 file changed, 55 insertions(+), 53 deletions(-) diff --git a/docs/troubleshooting.asciidoc b/docs/troubleshooting.asciidoc index ba23c0943..61aae1b21 100644 --- a/docs/troubleshooting.asciidoc +++ b/docs/troubleshooting.asciidoc @@ -32,7 +32,59 @@ If you don't see anything suspicious in the agent logs (no warning or error), it [[collect-agent-logs]] === Collecting agent logs -The way to collect logs depends on the setup of your application. +[float] +[[collect-logs-globally]] +==== Enable global file logging. + +To easiest way to get debug information from the Agent, regardless of the way its run, is to enable global file logging. + +Specifying atleast one of the following environment variables will ensure the agent logs to a file + +`ELASTIC_OTEL_FILE_LOG_LEVEL` _(optional)_:: + +The log level at which the profiler should log. Valid values are + +* trace +* debug +* info +* warn +* error +* none + + +The default value is `warn`. More verbose log levels like `trace` and `debug` can +affect the runtime performance of profiler auto instrumentation, so are recommended +_only_ for diagnostics purposes. + +`ELASTIC_OTEL_FILE_LOG_DIRECTORY` _(optional)_:: + +The directory in which to write profiler log files. If unset, defaults to + +* `%PROGRAMDATA%\elastic\apm-agent-dotnet\logs` on Windows +* `/var/log/elastic/apm-agent-dotnet` on Linux + +If the default directory cannot be written to for some reason, the profiler +will try to write log files to a `logs` directory in the home directory specified +by `ELASTIC_APM_PROFILER_HOME` environment variable. + +[IMPORTANT] +-- +The user account under which the profiler process runs must have permission to +write to the destination log directory. Specifically, ensure that when running +on IIS, the https://learn.microsoft.com/en-us/iis/manage/configuring-security/application-pool-identities[AppPool identity] +has write permissions in the target directory. +-- + +`ELASTIC_OTEL_LOG_TARGETS` _(optional)_:: + +A semi-colon separated list of targets for profiler logs. Valid values are + +* file +* stdout + +The default value is `file`, which logs to the directory specified by +`ELASTIC_APM_PROFILER_LOG_DIR` environment variable. + [float] [[collect-logs-core]] @@ -62,49 +114,9 @@ For example, the following configuration in `appsettings.json` limits APM agents ---- <1> Control the verbosity of the agent logs by setting the log level for the `Elastic.Apm` category -[float] -[[collect-logs-classic]] -==== ASP.NET Classic - -ASP.NET (classic) does not have a predefined logging system. By default, the agent is configured to -emit log messages to a -https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.tracesource[`System.Diagnostics.TraceSource`] -with the source name `"Elastic.Apm"`. The TraceSource adheres to the log levels defined in the -APM agent configuration. Typically, you will configure a preferred log level using an application setting in `web.config`. - -[IMPORTANT] --- -System.Diagnostics.TraceSource requires the https://docs.microsoft.com/en-us/dotnet/framework/debug-trace-profile/how-to-compile-conditionally-with-trace-and-debug[`TRACE` compiler directive to be specified], which is specified -by default for both Debug and Release build configurations. --- - -https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.tracelistener[TraceListeners] -can be configured to monitor log messages for the trace source, using the https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/trace-debug/system-diagnostics-element[``] section of -web.config. For example, the following web.config section writes Elastic.Apm log messages to a file -named my_log_file.log: - -[source,xml] ----- - - - - - <1> - - - - - - - ----- -<1> Define listeners under a source with name `"Elastic.Apm"` to capture agent logs - [float] [[collect-logs-class-other-logging-systems]] -===== Other logging systems +==== Other logging systems If you have a logging system in place such as https://nlog-project.org/[NLog], https://serilog.net/[Serilog], or similar, you can direct the agent logs into your logging system by creating an adapter between @@ -242,17 +254,7 @@ The agent is only supported on IIS7 and higher where the `Integrated Pipeline Mo [[startup-hook-failure]] === Startup hooks failure -If the <> integration throws an exception, additional detail can be obtained by -setting the `ELASTIC_APM_STARTUP_HOOKS_LOGGING` environment variable before starting the application - -[source,sh] ----- -set ELASTIC_APM_STARTUP_HOOKS_LOGGING=1 ----- - -and then running the application in a context where the environment variable will be visible. In setting this value, -an `ElasticApmAgentStartupHook.log` file is written to the directory containing the startup hook assembly, in addition to -writing to standard output. +If the <> integration throws an exception, additional detail can be obtained through <>. [float] [[agent-overhead]] From 734172a4214b08afc7421d02f42d9bc88fba1536 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Mon, 3 Jun 2024 17:16:30 +0200 Subject: [PATCH 11/21] Ensure we also always globally log when using .NET core IServiceCollection --- .../ApplicationBuilderExtensions.cs | 4 +-- ...etCoreLogger.cs => ApmExtensionsLogger.cs} | 6 ++-- .../CompositeLogger.cs | 35 +++++++++++++++++++ .../Config/ApmConfiguration.cs | 2 +- .../HostBuilderExtensions.cs | 9 ++++- .../ServiceCollectionExtensions.cs | 31 +++++++--------- .../Elastic.Apm.StartupHook.Loader/Loader.cs | 1 + .../ApplicationBuilderExtensionLoggingTest.cs | 6 ++-- .../AspNetCoreBasicTests.cs | 1 + .../AspNetCoreLoggerTests.cs | 6 ++-- .../TransactionIgnoreUrlsTest.cs | 1 + 11 files changed, 71 insertions(+), 31 deletions(-) rename src/integrations/Elastic.Apm.Extensions.Hosting/{NetCoreLogger.cs => ApmExtensionsLogger.cs} (84%) create mode 100644 src/integrations/Elastic.Apm.Extensions.Hosting/CompositeLogger.cs diff --git a/src/integrations/Elastic.Apm.AspNetCore/ApplicationBuilderExtensions.cs b/src/integrations/Elastic.Apm.AspNetCore/ApplicationBuilderExtensions.cs index 0a260f0d8..1efbfe841 100644 --- a/src/integrations/Elastic.Apm.AspNetCore/ApplicationBuilderExtensions.cs +++ b/src/integrations/Elastic.Apm.AspNetCore/ApplicationBuilderExtensions.cs @@ -52,7 +52,7 @@ public static IApplicationBuilder UseElasticApm( params IDiagnosticsSubscriber[] subscribers ) { - var logger = NetCoreLogger.GetApmLogger(builder.ApplicationServices); + var logger = ApmExtensionsLogger.GetApmLogger(builder.ApplicationServices); var configReader = configuration == null ? new EnvironmentConfiguration(logger) @@ -70,7 +70,7 @@ params IDiagnosticsSubscriber[] subscribers internal static IApplicationBuilder UseElasticApm( this IApplicationBuilder builder, ApmAgent agent, - IApmLogger logger, + Logging.IApmLogger logger, params IDiagnosticsSubscriber[] subscribers ) { diff --git a/src/integrations/Elastic.Apm.Extensions.Hosting/NetCoreLogger.cs b/src/integrations/Elastic.Apm.Extensions.Hosting/ApmExtensionsLogger.cs similarity index 84% rename from src/integrations/Elastic.Apm.Extensions.Hosting/NetCoreLogger.cs rename to src/integrations/Elastic.Apm.Extensions.Hosting/ApmExtensionsLogger.cs index f806f3499..48d6dd422 100644 --- a/src/integrations/Elastic.Apm.Extensions.Hosting/NetCoreLogger.cs +++ b/src/integrations/Elastic.Apm.Extensions.Hosting/ApmExtensionsLogger.cs @@ -9,11 +9,11 @@ namespace Elastic.Apm.Extensions.Hosting; -internal sealed class NetCoreLogger : IApmLogger +internal sealed class ApmExtensionsLogger : IApmLogger { private readonly ILogger _logger; - public NetCoreLogger(ILoggerFactory loggerFactory) => _logger = loggerFactory?.CreateLogger("Elastic.Apm") ?? throw new ArgumentNullException(nameof(loggerFactory)); + public ApmExtensionsLogger(ILoggerFactory loggerFactory) => _logger = loggerFactory?.CreateLogger("Elastic.Apm") ?? throw new ArgumentNullException(nameof(loggerFactory)); public bool IsEnabled(LogLevel level) => _logger.IsEnabled(Convert(level)); @@ -34,6 +34,6 @@ private static Microsoft.Extensions.Logging.LogLevel Convert(LogLevel logLevel) internal static IApmLogger GetApmLogger(IServiceProvider serviceProvider) => serviceProvider.GetService(typeof(ILoggerFactory)) is ILoggerFactory loggerFactory - ? new NetCoreLogger(loggerFactory) + ? new ApmExtensionsLogger(loggerFactory) : ConsoleLogger.Instance; } diff --git a/src/integrations/Elastic.Apm.Extensions.Hosting/CompositeLogger.cs b/src/integrations/Elastic.Apm.Extensions.Hosting/CompositeLogger.cs new file mode 100644 index 000000000..b9e52c53e --- /dev/null +++ b/src/integrations/Elastic.Apm.Extensions.Hosting/CompositeLogger.cs @@ -0,0 +1,35 @@ +// 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 System; +using Elastic.Apm.Logging; +using LogLevel = Elastic.Apm.Logging.LogLevel; + +namespace Elastic.Apm.Extensions.Hosting; + +internal sealed class CompositeLogger(TraceLogger traceLogger, IApmLogger logger) : IDisposable , IApmLogger +{ + public TraceLogger TraceLogger { get; } = traceLogger; + public IApmLogger ApmLogger { get; } = logger; + + private bool _isDisposed; + + public void Dispose() => _isDisposed = true; + + public void Log(LogLevel level, TState state, Exception e, Func formatter) + { + if (_isDisposed) + return; + + if (TraceLogger.IsEnabled(level)) + TraceLogger.Log(level, state, e, formatter); + + if (ApmLogger.IsEnabled(level)) + ApmLogger.Log(level, state, e, formatter); + } + + public bool IsEnabled(LogLevel logLevel) => ApmLogger.IsEnabled(logLevel) || TraceLogger.IsEnabled(logLevel); + + +} diff --git a/src/integrations/Elastic.Apm.Extensions.Hosting/Config/ApmConfiguration.cs b/src/integrations/Elastic.Apm.Extensions.Hosting/Config/ApmConfiguration.cs index b07095571..4105fa587 100644 --- a/src/integrations/Elastic.Apm.Extensions.Hosting/Config/ApmConfiguration.cs +++ b/src/integrations/Elastic.Apm.Extensions.Hosting/Config/ApmConfiguration.cs @@ -33,7 +33,7 @@ internal class ApmConfiguration : FallbackToEnvironmentConfigurationBase { private const string ThisClassName = nameof(ApmConfiguration); - public ApmConfiguration(IConfiguration configuration, IApmLogger logger, string defaultEnvironmentName) + public ApmConfiguration(IConfiguration configuration, Apm.Logging.IApmLogger logger, string defaultEnvironmentName) : base(logger, new ConfigurationDefaults { EnvironmentName = defaultEnvironmentName, DebugName = ThisClassName }, new ConfigurationKeyValueProvider(configuration)) => diff --git a/src/integrations/Elastic.Apm.Extensions.Hosting/HostBuilderExtensions.cs b/src/integrations/Elastic.Apm.Extensions.Hosting/HostBuilderExtensions.cs index d33a62d73..91f97818b 100644 --- a/src/integrations/Elastic.Apm.Extensions.Hosting/HostBuilderExtensions.cs +++ b/src/integrations/Elastic.Apm.Extensions.Hosting/HostBuilderExtensions.cs @@ -14,6 +14,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using LogLevel = Elastic.Apm.Logging.LogLevel; namespace Elastic.Apm.Extensions.Hosting { @@ -54,7 +55,13 @@ public static IHostBuilder UseElasticApm(this IHostBuilder builder, params IDiag //If the static agent doesn't exist, we create one here. If there is already 1 agent created, we reuse it. if (!Agent.IsConfigured) { - services.AddSingleton(); + services.AddSingleton(sp => + { + var netCoreLogger = ApmExtensionsLogger.GetApmLogger(sp); + var globalLogger = AgentComponents.GetGlobalLogger(netCoreLogger, LogLevel.Error); + var logger = globalLogger is TraceLogger g ? new CompositeLogger(g, netCoreLogger) : netCoreLogger; + return logger; + }); services.AddSingleton(sp => new ApmConfiguration(ctx.Configuration, sp.GetService(), GetHostingEnvironmentName(ctx, sp.GetService()))); } diff --git a/src/integrations/Elastic.Apm.Extensions.Hosting/ServiceCollectionExtensions.cs b/src/integrations/Elastic.Apm.Extensions.Hosting/ServiceCollectionExtensions.cs index 1923e42e2..2931fe1a3 100644 --- a/src/integrations/Elastic.Apm.Extensions.Hosting/ServiceCollectionExtensions.cs +++ b/src/integrations/Elastic.Apm.Extensions.Hosting/ServiceCollectionExtensions.cs @@ -12,10 +12,8 @@ using Elastic.Apm.Logging; using Elastic.Apm.NetCoreAll; using Elastic.Apm.Report; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using static Microsoft.Extensions.DependencyInjection.ServiceDescriptor; namespace Microsoft.Extensions.DependencyInjection; @@ -49,28 +47,25 @@ public static IServiceCollection AddElasticApm(this IServiceCollection services, if (agentConfigured) return Agent.Instance; - var logger = NetCoreLogger.GetApmLogger(sp); - var environmentName = GetEnvironmentName(sp); - - if (environmentName is null) - { - logger?.Warning()?.Log("Failed to retrieve hosting environment name"); - environmentName = "Undetermined"; - } - + var netCoreLogger = ApmExtensionsLogger.GetApmLogger(sp); var configuration = sp.GetService(); + var environmentName = GetDefaultEnvironmentName(sp); IConfigurationReader configurationReader = configuration is null - ? new EnvironmentConfiguration(logger) - : new ApmConfiguration(configuration, logger, environmentName); + ? new EnvironmentConfiguration(netCoreLogger) + : new ApmConfiguration(configuration, netCoreLogger, environmentName ?? "Undetermined"); + + var globalLogger = AgentComponents.GetGlobalLogger(netCoreLogger, configurationReader.LogLevel); + + var logger = globalLogger is TraceLogger g ? new CompositeLogger(g, netCoreLogger) : netCoreLogger; + + if (environmentName is null) + logger?.Warning()?.Log("Failed to retrieve default hosting environment name"); // This may be null, which is fine var payloadSender = sp.GetService(); - var components = agentConfigured - ? Agent.Components - : new AgentComponents(logger, configurationReader, payloadSender); - + var components = new AgentComponents(logger, configurationReader, payloadSender); HostBuilderExtensions.UpdateServiceInformation(components.Service); Agent.Setup(components); @@ -110,7 +105,7 @@ public static IServiceCollection AddElasticApm(this IServiceCollection services, return services; } - private static string GetEnvironmentName(IServiceProvider serviceProvider) => + private static string GetDefaultEnvironmentName(IServiceProvider serviceProvider) => #if NET6_0_OR_GREATER (serviceProvider.GetService(typeof(IHostEnvironment)) as IHostEnvironment)?.EnvironmentName; // This is preferred since 3.0 #else diff --git a/src/startuphook/Elastic.Apm.StartupHook.Loader/Loader.cs b/src/startuphook/Elastic.Apm.StartupHook.Loader/Loader.cs index fc87c735f..0e3d5677d 100644 --- a/src/startuphook/Elastic.Apm.StartupHook.Loader/Loader.cs +++ b/src/startuphook/Elastic.Apm.StartupHook.Loader/Loader.cs @@ -14,6 +14,7 @@ using Elastic.Apm.GrpcClient; using Elastic.Apm.Instrumentations.SqlClient; using Elastic.Apm.Logging; +using IApmLogger = Elastic.Apm.Logging.IApmLogger; namespace Elastic.Apm.StartupHook.Loader { diff --git a/test/integrations/Elastic.Apm.AspNetCore.Tests/ApplicationBuilderExtensionLoggingTest.cs b/test/integrations/Elastic.Apm.AspNetCore.Tests/ApplicationBuilderExtensionLoggingTest.cs index 8c0480aea..1a23b5c01 100644 --- a/test/integrations/Elastic.Apm.AspNetCore.Tests/ApplicationBuilderExtensionLoggingTest.cs +++ b/test/integrations/Elastic.Apm.AspNetCore.Tests/ApplicationBuilderExtensionLoggingTest.cs @@ -20,9 +20,9 @@ public void UseElasticApmShouldUseAspNetLoggerWhenLoggingIsConfigured() var services = new ServiceCollection() .AddLogging(); - var logger = NetCoreLogger.GetApmLogger(services.BuildServiceProvider()); + var logger = ApmExtensionsLogger.GetApmLogger(services.BuildServiceProvider()); - Assert.IsType(logger); + Assert.IsType(logger); } [Fact] @@ -30,7 +30,7 @@ public void UseElasticApmShouldUseConsoleLoggerInstanceWhenLoggingIsNotConfigure { var services = new ServiceCollection(); - var logger = NetCoreLogger.GetApmLogger(services.BuildServiceProvider()); + var logger = ApmExtensionsLogger.GetApmLogger(services.BuildServiceProvider()); Assert.IsType(logger); Assert.Same(ConsoleLogger.Instance, logger); diff --git a/test/integrations/Elastic.Apm.AspNetCore.Tests/AspNetCoreBasicTests.cs b/test/integrations/Elastic.Apm.AspNetCore.Tests/AspNetCoreBasicTests.cs index 23edf20e0..10f38113d 100644 --- a/test/integrations/Elastic.Apm.AspNetCore.Tests/AspNetCoreBasicTests.cs +++ b/test/integrations/Elastic.Apm.AspNetCore.Tests/AspNetCoreBasicTests.cs @@ -24,6 +24,7 @@ using SampleAspNetCoreApp; using Xunit; using Xunit.Abstractions; +using IApmLogger = Elastic.Apm.Logging.IApmLogger; namespace Elastic.Apm.AspNetCore.Tests { diff --git a/test/integrations/Elastic.Apm.AspNetCore.Tests/AspNetCoreLoggerTests.cs b/test/integrations/Elastic.Apm.AspNetCore.Tests/AspNetCoreLoggerTests.cs index a6e4476b1..98e1844ba 100644 --- a/test/integrations/Elastic.Apm.AspNetCore.Tests/AspNetCoreLoggerTests.cs +++ b/test/integrations/Elastic.Apm.AspNetCore.Tests/AspNetCoreLoggerTests.cs @@ -11,13 +11,13 @@ namespace Elastic.Apm.AspNetCore.Tests { /// - /// Tests the type. + /// Tests the type. /// public class AspNetCoreLoggerTests { [Fact] public void AspNetCoreLoggerShouldThrowExceptionWhenLoggerFactoryIsNull() - => Assert.Throws(() => new NetCoreLogger(null)); + => Assert.Throws(() => new ApmExtensionsLogger(null)); [Fact] public void AspNetCoreLoggerShouldGetLoggerFromFactoryWithProperCategoryName() @@ -27,7 +27,7 @@ public void AspNetCoreLoggerShouldGetLoggerFromFactoryWithProperCategoryName() .Returns(() => Mock.Of()); // ReSharper disable UnusedVariable - var logger = new NetCoreLogger(loggerFactoryMock.Object); + var logger = new ApmExtensionsLogger(loggerFactoryMock.Object); // ReSharper restore UnusedVariable loggerFactoryMock.Verify(x => x.CreateLogger(It.Is(s => s.Equals("Elastic.Apm"))), Times.Once); diff --git a/test/integrations/Elastic.Apm.AspNetCore.Tests/TransactionIgnoreUrlsTest.cs b/test/integrations/Elastic.Apm.AspNetCore.Tests/TransactionIgnoreUrlsTest.cs index 369380440..7cd8c4aab 100644 --- a/test/integrations/Elastic.Apm.AspNetCore.Tests/TransactionIgnoreUrlsTest.cs +++ b/test/integrations/Elastic.Apm.AspNetCore.Tests/TransactionIgnoreUrlsTest.cs @@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Mvc.Testing; using SampleAspNetCoreApp; using Xunit; +using IApmLogger = Elastic.Apm.Logging.IApmLogger; namespace Elastic.Apm.AspNetCore.Tests { From 65ee18a4079a26977fbd1712fc155056ec5bf014 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Mon, 3 Jun 2024 17:29:28 +0200 Subject: [PATCH 12/21] run dotnet-format --- .../Logging/GlobalLogConfiguration.cs | 127 +++++++++--------- .../CompositeLogger.cs | 26 ++-- .../Logger.cs | 3 +- .../GlobalLogConfigurationPrecedenceTests.cs | 3 +- .../Config/GlobalLogConfigurationTests.cs | 4 +- 5 files changed, 83 insertions(+), 80 deletions(-) diff --git a/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs index b1b84996d..56a6372da 100644 --- a/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs +++ b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs @@ -26,79 +26,80 @@ internal class EnvironmentLoggingConfiguration(IDictionary environmentVariables { public IDictionary EnvironmentVariables { get; } = environmentVariables ?? Environment.GetEnvironmentVariables(); - public string GetSafeEnvironmentVariable(string key) - { - var value = EnvironmentVariables.Contains(key) ? EnvironmentVariables[key]?.ToString() : null; - return value ?? string.Empty; - } +public string GetSafeEnvironmentVariable(string key) +{ + var value = EnvironmentVariables.Contains(key) ? EnvironmentVariables[key]?.ToString() : null; + return value ?? string.Empty; +} - public LogLevel GetLogLevel(params string[] keys) - { - var level = keys - .Select(k => GetSafeEnvironmentVariable(k)) - .Select(v => v.ToLowerInvariant() switch - { - "trace" => LogLevel.Trace, - "debug" => LogLevel.Debug, - "info" => LogLevel.Information, - "warn" => LogLevel.Warning, - "error" => LogLevel.Error, - "none" => LogLevel.None, - _ => null - }) - .FirstOrDefault(l => l != null); - return level ?? LogLevel.Warning; - } +public LogLevel GetLogLevel(params string[] keys) +{ + var level = keys + .Select(k => GetSafeEnvironmentVariable(k)) + .Select(v => v.ToLowerInvariant() switch + { + "trace" => LogLevel.Trace, + "debug" => LogLevel.Debug, + "info" => LogLevel.Information, + "warn" => LogLevel.Warning, + "error" => LogLevel.Error, + "none" => LogLevel.None, + _ => null + }) + .FirstOrDefault(l => l != null); + return level ?? LogLevel.Warning; +} - public string GetLogFilePath(params string[] keys) - { - var path = keys - .Select(k => GetSafeEnvironmentVariable(k)) - .FirstOrDefault(p => !string.IsNullOrEmpty(p)); +public string GetLogFilePath(params string[] keys) +{ + var path = keys + .Select(k => GetSafeEnvironmentVariable(k)) + .FirstOrDefault(p => !string.IsNullOrEmpty(p)); - return path ?? GetDefaultLogDirectory(); - } + return path ?? GetDefaultLogDirectory(); +} - public bool AnyConfigured(params string[] keys) => - keys - .Select(k => GetSafeEnvironmentVariable(k)) - .Any(p => !string.IsNullOrEmpty(p)); +public bool AnyConfigured(params string[] keys) => + keys + .Select(k => GetSafeEnvironmentVariable(k)) + .Any(p => !string.IsNullOrEmpty(p)); - public GlobalLogTarget ParseLogTargets(params string[] keys) - { +public GlobalLogTarget ParseLogTargets(params string[] keys) +{ - var targets = keys - .Select(k => GetSafeEnvironmentVariable(k)) - .FirstOrDefault(p => !string.IsNullOrEmpty(p)); - if (string.IsNullOrWhiteSpace(targets)) - return GlobalLogTarget.File; + var targets = keys + .Select(k => GetSafeEnvironmentVariable(k)) + .FirstOrDefault(p => !string.IsNullOrEmpty(p)); + if (string.IsNullOrWhiteSpace(targets)) + return GlobalLogTarget.File; - var logTargets = GlobalLogTarget.None; - var found = false; + var logTargets = GlobalLogTarget.None; + var found = false; - foreach (var target in targets.Split(new [] {';'}, StringSplitOptions.RemoveEmptyEntries)) - { - if (IsSet(target, "stdout")) - logTargets |= GlobalLogTarget.StdOut; - else if (IsSet(target, "file")) - logTargets |= GlobalLogTarget.File; - else if (IsSet(target, "none")) - logTargets |= GlobalLogTarget.None; - } - return !found ? GlobalLogTarget.File : logTargets; - - bool IsSet(string k, string v) - { - var b = k.Trim().Equals(v, StringComparison.InvariantCultureIgnoreCase); - if (b) found = true; - return b; - } + foreach (var target in targets.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) + { + if (IsSet(target, "stdout")) + logTargets |= GlobalLogTarget.StdOut; + else if (IsSet(target, "file")) + logTargets |= GlobalLogTarget.File; + else if (IsSet(target, "none")) + logTargets |= GlobalLogTarget.None; } + return !found ? GlobalLogTarget.File : logTargets; + + bool IsSet(string k, string v) + { + var b = k.Trim().Equals(v, StringComparison.InvariantCultureIgnoreCase); + if (b) + found = true; + return b; + } +} - internal static string GetDefaultLogDirectory() => - Environment.OSVersion.Platform == PlatformID.Win32NT - ? Path.Combine(Environment.GetEnvironmentVariable("PROGRAMDATA")!, "elastic", "apm-agent-dotnet", "logs") - : "/var/log/elastic/apm-agent-dotnet"; +internal static string GetDefaultLogDirectory() => + Environment.OSVersion.Platform == PlatformID.Win32NT + ? Path.Combine(Environment.GetEnvironmentVariable("PROGRAMDATA")!, "elastic", "apm-agent-dotnet", "logs") + : "/var/log/elastic/apm-agent-dotnet"; } diff --git a/src/integrations/Elastic.Apm.Extensions.Hosting/CompositeLogger.cs b/src/integrations/Elastic.Apm.Extensions.Hosting/CompositeLogger.cs index b9e52c53e..e7498dc98 100644 --- a/src/integrations/Elastic.Apm.Extensions.Hosting/CompositeLogger.cs +++ b/src/integrations/Elastic.Apm.Extensions.Hosting/CompositeLogger.cs @@ -11,25 +11,25 @@ namespace Elastic.Apm.Extensions.Hosting; internal sealed class CompositeLogger(TraceLogger traceLogger, IApmLogger logger) : IDisposable , IApmLogger { public TraceLogger TraceLogger { get; } = traceLogger; - public IApmLogger ApmLogger { get; } = logger; +public IApmLogger ApmLogger { get; } = logger; - private bool _isDisposed; +private bool _isDisposed; - public void Dispose() => _isDisposed = true; +public void Dispose() => _isDisposed = true; - public void Log(LogLevel level, TState state, Exception e, Func formatter) - { - if (_isDisposed) - return; +public void Log(LogLevel level, TState state, Exception e, Func formatter) +{ + if (_isDisposed) + return; - if (TraceLogger.IsEnabled(level)) - TraceLogger.Log(level, state, e, formatter); + if (TraceLogger.IsEnabled(level)) + TraceLogger.Log(level, state, e, formatter); - if (ApmLogger.IsEnabled(level)) - ApmLogger.Log(level, state, e, formatter); - } + if (ApmLogger.IsEnabled(level)) + ApmLogger.Log(level, state, e, formatter); +} - public bool IsEnabled(LogLevel logLevel) => ApmLogger.IsEnabled(logLevel) || TraceLogger.IsEnabled(logLevel); +public bool IsEnabled(LogLevel logLevel) => ApmLogger.IsEnabled(logLevel) || TraceLogger.IsEnabled(logLevel); } diff --git a/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Logger.cs b/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Logger.cs index c0dd622de..b498caca6 100644 --- a/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Logger.cs +++ b/src/profiler/Elastic.Apm.Profiler.Managed.Loader/Logger.cs @@ -65,7 +65,8 @@ public static void Log(LogLevel level, string message, params object[] args) if (!IsActive || Level > level) return; - if (string.IsNullOrWhiteSpace(LogFile)) return; + if (string.IsNullOrWhiteSpace(LogFile)) + return; try { diff --git a/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationPrecedenceTests.cs b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationPrecedenceTests.cs index e63d04a41..3699bf597 100644 --- a/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationPrecedenceTests.cs +++ b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationPrecedenceTests.cs @@ -74,7 +74,8 @@ public void CheckLogTargetsPrecedence() private static GlobalLogConfiguration CreateConfig(params (string key, string v)[] values) { var environment = new Hashtable(); - foreach (var kv in values) environment.Add(kv.key, kv.v); + foreach (var kv in values) + environment.Add(kv.key, kv.v); var config = GlobalLogConfiguration.FromEnvironment(environment); return config; } diff --git a/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs index aae2f8cba..25f45144e 100644 --- a/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs +++ b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs @@ -42,7 +42,7 @@ public void Check_Defaults() [InlineData(ELASTIC_APM_PROFILER_LOG_TARGETS, "BadEnumValue")] public void CheckActivation(string environmentVariable, string value) { - var config = GlobalLogConfiguration.FromEnvironment(new Hashtable { { environmentVariable, value}}); + var config = GlobalLogConfiguration.FromEnvironment(new Hashtable { { environmentVariable, value } }); config.IsActive.Should().BeTrue(); } @@ -58,7 +58,7 @@ public void CheckActivation(string environmentVariable, string value) [InlineData(ELASTIC_APM_PROFILER_LOG, "None")] public void CheckNonActivation(string environmentVariable, string value) { - var config = GlobalLogConfiguration.FromEnvironment(new Hashtable { { environmentVariable , value}}); + var config = GlobalLogConfiguration.FromEnvironment(new Hashtable { { environmentVariable, value } }); config.IsActive.Should().BeFalse(); } From 71afd16586c96cd069624b94ac4a873348a04cf4 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Mon, 3 Jun 2024 19:18:39 +0200 Subject: [PATCH 13/21] Err != None --- src/profiler/elastic_apm_profiler/src/profiler/env.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/profiler/elastic_apm_profiler/src/profiler/env.rs b/src/profiler/elastic_apm_profiler/src/profiler/env.rs index 6d630765f..602618521 100644 --- a/src/profiler/elastic_apm_profiler/src/profiler/env.rs +++ b/src/profiler/elastic_apm_profiler/src/profiler/env.rs @@ -212,8 +212,8 @@ pub fn read_log_level_from_env_var(default: LevelFilter) -> LevelFilter { std::env::var(ELASTIC_APM_LOG_DIRECTORY_ENV_VAR) ) { (Ok(value), _, _) => LevelFilter::from_str(value.as_str()).unwrap_or(default), - (None, Ok(value), _) => LevelFilter::from_str(value.as_str()).unwrap_or(default), - (None, None, Ok(value)) => LevelFilter::from_str(value.as_str()).unwrap_or(default), + (_, Ok(value), _) => LevelFilter::from_str(value.as_str()).unwrap_or(default), + (_, _, Ok(value)) => LevelFilter::from_str(value.as_str()).unwrap_or(default), _ => default, } } From fc6373096ee85d489952c9b4ff60efbe25fdaf25 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Tue, 4 Jun 2024 10:12:33 +0200 Subject: [PATCH 14/21] Apply suggestions from code review Co-authored-by: Steve Gordon --- docs/troubleshooting.asciidoc | 4 ++-- src/Elastic.Apm/AgentComponents.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/troubleshooting.asciidoc b/docs/troubleshooting.asciidoc index 61aae1b21..f071af755 100644 --- a/docs/troubleshooting.asciidoc +++ b/docs/troubleshooting.asciidoc @@ -36,9 +36,9 @@ If you don't see anything suspicious in the agent logs (no warning or error), it [[collect-logs-globally]] ==== Enable global file logging. -To easiest way to get debug information from the Agent, regardless of the way its run, is to enable global file logging. +The easiest way to get debug information from the Agent, regardless of the way it's run, is to enable global file logging. -Specifying atleast one of the following environment variables will ensure the agent logs to a file +Specifying at least one of the following environment variables will ensure the agent logs to a file `ELASTIC_OTEL_FILE_LOG_LEVEL` _(optional)_:: diff --git a/src/Elastic.Apm/AgentComponents.cs b/src/Elastic.Apm/AgentComponents.cs index 34a53bdfd..5e63467f9 100644 --- a/src/Elastic.Apm/AgentComponents.cs +++ b/src/Elastic.Apm/AgentComponents.cs @@ -198,7 +198,7 @@ private static IConfigurationReader CreateConfiguration(IApmLogger logger, IConf } /// - /// This ensures agents will log respect externally provided loggers. + /// This ensures agents will respect externally provided loggers. /// If the agent is started as part of profiling it should adhere to profiling configuration /// If file logging environment variables are set we should always log to that location /// From 617ccc5257c954a75fa1d77ee5cbd9bb825db6ff Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Tue, 4 Jun 2024 11:10:30 +0200 Subject: [PATCH 15/21] remove _FILE_ from environment variables --- docs/setup-auto-instrumentation.asciidoc | 4 ++-- docs/troubleshooting.asciidoc | 4 ++-- src/Elastic.Apm/Logging/GlobalLogConfiguration.cs | 14 +++++++------- .../elastic_apm_profiler/src/profiler/env.rs | 8 ++++---- .../GlobalLogConfigurationPrecedenceTests.cs | 6 +++--- .../Config/GlobalLogConfigurationTests.cs | 14 +++++++------- .../ProfiledApplication.cs | 4 ++-- 7 files changed, 27 insertions(+), 27 deletions(-) diff --git a/docs/setup-auto-instrumentation.asciidoc b/docs/setup-auto-instrumentation.asciidoc index 0c36d0907..413acfbbb 100644 --- a/docs/setup-auto-instrumentation.asciidoc +++ b/docs/setup-auto-instrumentation.asciidoc @@ -394,7 +394,7 @@ A semi-colon separated list of APM service names to exclude from auto-instrument Values defined are checked against the value of <> environment variable. -`ELASTIC_OTEL_FILE_LOG_LEVEL` _(optional)_:: +`ELASTIC_OTEL_LOG_LEVEL` _(optional)_:: The log level at which the profiler should log. Valid values are @@ -413,7 +413,7 @@ _only_ for diagnostics purposes. This takes precedence over the now deprecated `ELASTIC_APM_PROFILER_LOG` -`ELASTIC_OTEL_FILE_LOG_DIRECTORY` _(optional)_:: +`ELASTIC_OTEL_LOG_DIRECTORY` _(optional)_:: The directory in which to write profiler log files. If unset, defaults to diff --git a/docs/troubleshooting.asciidoc b/docs/troubleshooting.asciidoc index f071af755..5c21c3f43 100644 --- a/docs/troubleshooting.asciidoc +++ b/docs/troubleshooting.asciidoc @@ -40,7 +40,7 @@ The easiest way to get debug information from the Agent, regardless of the way i Specifying at least one of the following environment variables will ensure the agent logs to a file -`ELASTIC_OTEL_FILE_LOG_LEVEL` _(optional)_:: +`ELASTIC_OTEL_LOG_LEVEL` _(optional)_:: The log level at which the profiler should log. Valid values are @@ -56,7 +56,7 @@ The default value is `warn`. More verbose log levels like `trace` and `debug` ca affect the runtime performance of profiler auto instrumentation, so are recommended _only_ for diagnostics purposes. -`ELASTIC_OTEL_FILE_LOG_DIRECTORY` _(optional)_:: +`ELASTIC_OTEL_LOG_DIRECTORY` _(optional)_:: The directory in which to write profiler log files. If unset, defaults to diff --git a/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs index 56a6372da..fb1cc84c1 100644 --- a/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs +++ b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs @@ -114,15 +114,15 @@ internal enum GlobalLogTarget public static class LogEnvironmentVariables { // ReSharper disable once InconsistentNaming - public const string ELASTIC_OTEL_FILE_LOG_LEVEL = nameof(ELASTIC_OTEL_FILE_LOG_LEVEL); - public const string ELASTIC_OTEL_FILE_LOG_DIRECTORY = nameof(ELASTIC_OTEL_FILE_LOG_DIRECTORY); + public const string ELASTIC_OTEL_LOG_LEVEL = nameof(ELASTIC_OTEL_LOG_LEVEL); + public const string ELASTIC_OTEL_LOG_DIRECTORY = nameof(ELASTIC_OTEL_LOG_DIRECTORY); public const string ELASTIC_OTEL_LOG_TARGETS = nameof(ELASTIC_OTEL_LOG_TARGETS); public const string ELASTIC_APM_LOG_LEVEL = nameof(ELASTIC_APM_LOG_LEVEL); public const string ELASTIC_APM_LOG_DIRECTORY = nameof(ELASTIC_APM_LOG_DIRECTORY); - // profiler logs are deprecated in favor of ELASTIC_OTEL_FILE_* + // profiler logs are deprecated in favor of ELASTIC_OTEL_* public const string ELASTIC_APM_PROFILER_LOG = nameof(ELASTIC_APM_PROFILER_LOG); public const string ELASTIC_APM_PROFILER_LOG_DIR = nameof(ELASTIC_APM_PROFILER_LOG_DIR); public const string ELASTIC_APM_PROFILER_LOG_TARGETS = nameof(ELASTIC_APM_PROFILER_LOG_TARGETS); @@ -159,14 +159,14 @@ private GlobalLogConfiguration(bool isActive, LogLevel logLevel, GlobalLogTarget internal static GlobalLogConfiguration FromEnvironment(IDictionary environmentVariables = null) { var config = new EnvironmentLoggingConfiguration(environmentVariables); - var logLevel = config.GetLogLevel(ELASTIC_OTEL_FILE_LOG_LEVEL, ELASTIC_APM_PROFILER_LOG, ELASTIC_APM_LOG_LEVEL); - var logFileDirectory = config.GetLogFilePath(ELASTIC_OTEL_FILE_LOG_DIRECTORY, ELASTIC_APM_PROFILER_LOG_DIR, ELASTIC_APM_LOG_DIRECTORY); + var logLevel = config.GetLogLevel(ELASTIC_OTEL_LOG_LEVEL, ELASTIC_APM_PROFILER_LOG, ELASTIC_APM_LOG_LEVEL); + var logFileDirectory = config.GetLogFilePath(ELASTIC_OTEL_LOG_DIRECTORY, ELASTIC_APM_PROFILER_LOG_DIR, ELASTIC_APM_LOG_DIRECTORY); var logFilePrefix = GetLogFilePrefix(); var logTarget = config.ParseLogTargets(ELASTIC_OTEL_LOG_TARGETS, ELASTIC_APM_PROFILER_LOG_TARGETS); var isActive = config.AnyConfigured( - ELASTIC_OTEL_FILE_LOG_LEVEL, - ELASTIC_OTEL_FILE_LOG_DIRECTORY, + ELASTIC_OTEL_LOG_LEVEL, + ELASTIC_OTEL_LOG_DIRECTORY, ELASTIC_OTEL_LOG_TARGETS, ELASTIC_APM_LOG_DIRECTORY, ELASTIC_APM_PROFILER_LOG, diff --git a/src/profiler/elastic_apm_profiler/src/profiler/env.rs b/src/profiler/elastic_apm_profiler/src/profiler/env.rs index 602618521..ba0284236 100644 --- a/src/profiler/elastic_apm_profiler/src/profiler/env.rs +++ b/src/profiler/elastic_apm_profiler/src/profiler/env.rs @@ -45,8 +45,8 @@ const ELASTIC_OTEL_LOG_TARGETS_ENV_VAR: &str = "ELASTIC_OTEL_LOG_TARGETS"; const ELASTIC_APM_PROFILER_LOG_DIR_ENV_VAR: &str = "ELASTIC_APM_PROFILER_LOG_DIR"; const ELASTIC_APM_PROFILER_LOG_ENV_VAR: &str = "ELASTIC_APM_PROFILER_LOG"; -const ELASTIC_OTEL_FILE_LOG_LEVEL_ENV_VAR: &str = "ELASTIC_OTEL_FILE_LOG_LEVEL"; -const ELASTIC_OTEL_FILE_LOG_DIRECTORY_ENV_VAR: &str = "ELASTIC_OTEL_FILE_LOG_DIRECTORY"; +const ELASTIC_OTEL_LOG_LEVEL_ENV_VAR: &str = "ELASTIC_OTEL_LOG_LEVEL"; +const ELASTIC_OTEL_LOG_DIRECTORY_ENV_VAR: &str = "ELASTIC_OTEL_LOG_DIRECTORY"; const ELASTIC_APM_LOG_LEVEL_ENV_VAR: &str = "ELASTIC_APM_LOG_LEVEL"; const ELASTIC_APM_LOG_DIRECTORY_ENV_VAR: &str = "ELASTIC_APM_LOG_DIRECTORY"; @@ -207,7 +207,7 @@ fn read_log_targets_from_env_var() -> HashSet { pub fn read_log_level_from_env_var(default: LevelFilter) -> LevelFilter { match ( - std::env::var(ELASTIC_OTEL_FILE_LOG_LEVEL_ENV_VAR), + std::env::var(ELASTIC_OTEL_LOG_LEVEL_ENV_VAR), std::env::var(ELASTIC_APM_PROFILER_LOG_ENV_VAR), std::env::var(ELASTIC_APM_LOG_DIRECTORY_ENV_VAR) ) { @@ -311,7 +311,7 @@ fn get_home_log_dir() -> PathBuf { fn get_log_dir() -> PathBuf { match ( - std::env::var(ELASTIC_OTEL_FILE_LOG_DIRECTORY_ENV_VAR), + std::env::var(ELASTIC_OTEL_LOG_DIRECTORY_ENV_VAR), std::env::var(ELASTIC_APM_PROFILER_LOG_DIR_ENV_VAR), std::env::var(ELASTIC_APM_LOG_DIRECTORY_ENV_VAR), ) { diff --git a/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationPrecedenceTests.cs b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationPrecedenceTests.cs index 3699bf597..96121d8da 100644 --- a/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationPrecedenceTests.cs +++ b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationPrecedenceTests.cs @@ -16,7 +16,7 @@ public class GlobalLogConfigurationPrecedenceTests public void CheckLogLevelPrecedence() { var config = CreateConfig([ - (ELASTIC_OTEL_FILE_LOG_LEVEL, "trace"), + (ELASTIC_OTEL_LOG_LEVEL, "trace"), (ELASTIC_APM_PROFILER_LOG, "info"), (ELASTIC_APM_LOG_LEVEL, "error"), ]); @@ -38,11 +38,11 @@ public void CheckLogLevelPrecedence() public void CheckLogDirPrecedence() { var config = CreateConfig([ - (ELASTIC_OTEL_FILE_LOG_DIRECTORY, nameof(ELASTIC_OTEL_FILE_LOG_DIRECTORY)), + (ELASTIC_OTEL_LOG_DIRECTORY, nameof(ELASTIC_OTEL_LOG_DIRECTORY)), (ELASTIC_APM_PROFILER_LOG_DIR, nameof(ELASTIC_APM_PROFILER_LOG_DIR)), (ELASTIC_APM_LOG_DIRECTORY, nameof(ELASTIC_APM_LOG_DIRECTORY)), ]); - config.LogFileDirectory.Should().Be(nameof(ELASTIC_OTEL_FILE_LOG_DIRECTORY)); + config.LogFileDirectory.Should().Be(nameof(ELASTIC_OTEL_LOG_DIRECTORY)); config = CreateConfig([ (ELASTIC_APM_PROFILER_LOG_DIR, nameof(ELASTIC_APM_PROFILER_LOG_DIR)), diff --git a/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs index 25f45144e..f91a52916 100644 --- a/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs +++ b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs @@ -25,13 +25,13 @@ public void Check_Defaults() } // [Theory] - [InlineData(ELASTIC_OTEL_FILE_LOG_LEVEL, "Info")] + [InlineData(ELASTIC_OTEL_LOG_LEVEL, "Info")] [InlineData(ELASTIC_APM_PROFILER_LOG, "Info")] //only if explicitly specified to 'none' should we not default to file logging. - [InlineData(ELASTIC_OTEL_FILE_LOG_LEVEL, "BadEnumValue")] + [InlineData(ELASTIC_OTEL_LOG_LEVEL, "BadEnumValue")] [InlineData(ELASTIC_APM_PROFILER_LOG, "BadEnumValue")] - [InlineData(ELASTIC_OTEL_FILE_LOG_DIRECTORY, "1")] + [InlineData(ELASTIC_OTEL_LOG_DIRECTORY, "1")] [InlineData(ELASTIC_APM_LOG_DIRECTORY, "1")] [InlineData(ELASTIC_APM_PROFILER_LOG_DIR, "1")] [InlineData(ELASTIC_APM_STARTUP_HOOKS_LOGGING, "1")] @@ -53,7 +53,7 @@ public void CheckActivation(string environmentVariable, string value) [InlineData(ELASTIC_OTEL_LOG_TARGETS, "None")] [InlineData(ELASTIC_APM_PROFILER_LOG_TARGETS, "None")] //setting file log level to none will result in no global trace logging - [InlineData(ELASTIC_OTEL_FILE_LOG_LEVEL, "None")] + [InlineData(ELASTIC_OTEL_LOG_LEVEL, "None")] //setting profiler log level to none will result in no global trace logging [InlineData(ELASTIC_APM_PROFILER_LOG, "None")] public void CheckNonActivation(string environmentVariable, string value) @@ -75,7 +75,7 @@ public void Check_LogLevelValues_AreMappedCorrectly(string envVarValue, LogLevel { Check(ELASTIC_APM_PROFILER_LOG, envVarValue, logLevel); Check(ELASTIC_APM_LOG_LEVEL, envVarValue, logLevel); - Check(ELASTIC_OTEL_FILE_LOG_LEVEL, envVarValue, logLevel); + Check(ELASTIC_OTEL_LOG_LEVEL, envVarValue, logLevel); return; static void Check(string key, string envVarValue, LogLevel level) @@ -94,7 +94,7 @@ public void Check_InvalidLogLevelValues_AreMappedToDefaultWarn(string envVarValu { Check(ELASTIC_APM_PROFILER_LOG, envVarValue); Check(ELASTIC_APM_LOG_LEVEL, envVarValue); - Check(ELASTIC_OTEL_FILE_LOG_LEVEL, envVarValue); + Check(ELASTIC_OTEL_LOG_LEVEL, envVarValue); return; static void Check(string key, string envVarValue) @@ -109,7 +109,7 @@ public void Check_LogDir_IsEvaluatedCorrectly() { Check(ELASTIC_APM_PROFILER_LOG_DIR, "/foo/bar"); Check(ELASTIC_APM_LOG_DIRECTORY, "/foo/bar"); - Check(ELASTIC_OTEL_FILE_LOG_DIRECTORY, "/foo/bar"); + Check(ELASTIC_OTEL_LOG_DIRECTORY, "/foo/bar"); return; static void Check(string key, string envVarValue) diff --git a/test/profiler/Elastic.Apm.Profiler.Managed.Tests/ProfiledApplication.cs b/test/profiler/Elastic.Apm.Profiler.Managed.Tests/ProfiledApplication.cs index b52da32ef..aa1194c8a 100644 --- a/test/profiler/Elastic.Apm.Profiler.Managed.Tests/ProfiledApplication.cs +++ b/test/profiler/Elastic.Apm.Profiler.Managed.Tests/ProfiledApplication.cs @@ -144,9 +144,9 @@ public void Start( environmentVariables["ELASTIC_APM_PROFILER_INTEGRATIONS"] = Path.Combine(SolutionPaths.Root, "src", "profiler", "Elastic.Apm.Profiler.Managed", "integrations.yml"); - environmentVariables[ELASTIC_OTEL_FILE_LOG_LEVEL] = "trace"; + environmentVariables[ELASTIC_OTEL_LOG_LEVEL] = "trace"; // log to relative logs directory for managed loader - environmentVariables[ELASTIC_OTEL_FILE_LOG_DIRECTORY] = Path.Combine(SolutionPaths.Root, "logs"); + environmentVariables[ELASTIC_OTEL_LOG_DIRECTORY] = Path.Combine(SolutionPaths.Root, "logs"); environmentVariables[ELASTIC_OTEL_LOG_TARGETS] = "file;stdout"; //environmentVariables["ELASTIC_APM_PROFILER_LOG_IL"] = "true"; From 2cc27faba39bad9755a9088f76c3b0cc132a00f3 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Tue, 4 Jun 2024 14:55:55 +0200 Subject: [PATCH 16/21] Make activation of global logging more explicit --- src/Elastic.Apm/AgentComponents.cs | 1 - .../Logging/GlobalLogConfiguration.cs | 165 +++++++++--------- .../Config/GlobalLogConfigurationTests.cs | 43 +++-- 3 files changed, 114 insertions(+), 95 deletions(-) diff --git a/src/Elastic.Apm/AgentComponents.cs b/src/Elastic.Apm/AgentComponents.cs index 5e63467f9..0f4af390e 100644 --- a/src/Elastic.Apm/AgentComponents.cs +++ b/src/Elastic.Apm/AgentComponents.cs @@ -218,7 +218,6 @@ internal static IApmLogger GetGlobalLogger(IApmLogger fallbackLogger, LogLevel a } var effectiveLogLevel = LogLevelUtils.GetFinest(agentLogLevel, fileLogConfig.LogLevel); - if ((fileLogConfig.LogTargets & GlobalLogTarget.File) == GlobalLogTarget.File) TraceLogger.TraceSource.Listeners.Add(new TextWriterTraceListener(fileLogConfig.AgentLogFilePath)); if ((fileLogConfig.LogTargets & GlobalLogTarget.StdOut) == GlobalLogTarget.StdOut) diff --git a/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs index fb1cc84c1..4ffcda2fb 100644 --- a/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs +++ b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs @@ -19,6 +19,7 @@ namespace Elastic.Apm.Profiler.Managed; namespace ElasticApmStartupHook; #else using static Elastic.Apm.Logging.LogEnvironmentVariables; + namespace Elastic.Apm.Logging; #endif @@ -26,81 +27,79 @@ internal class EnvironmentLoggingConfiguration(IDictionary environmentVariables { public IDictionary EnvironmentVariables { get; } = environmentVariables ?? Environment.GetEnvironmentVariables(); -public string GetSafeEnvironmentVariable(string key) -{ - var value = EnvironmentVariables.Contains(key) ? EnvironmentVariables[key]?.ToString() : null; - return value ?? string.Empty; -} - -public LogLevel GetLogLevel(params string[] keys) -{ - var level = keys - .Select(k => GetSafeEnvironmentVariable(k)) - .Select(v => v.ToLowerInvariant() switch - { - "trace" => LogLevel.Trace, - "debug" => LogLevel.Debug, - "info" => LogLevel.Information, - "warn" => LogLevel.Warning, - "error" => LogLevel.Error, - "none" => LogLevel.None, - _ => null - }) - .FirstOrDefault(l => l != null); - return level ?? LogLevel.Warning; -} - -public string GetLogFilePath(params string[] keys) -{ - var path = keys - .Select(k => GetSafeEnvironmentVariable(k)) - .FirstOrDefault(p => !string.IsNullOrEmpty(p)); - - return path ?? GetDefaultLogDirectory(); -} + public string GetSafeEnvironmentVariable(string key) + { + var value = EnvironmentVariables.Contains(key) ? EnvironmentVariables[key]?.ToString() : null; + return value ?? string.Empty; + } -public bool AnyConfigured(params string[] keys) => - keys - .Select(k => GetSafeEnvironmentVariable(k)) - .Any(p => !string.IsNullOrEmpty(p)); + public LogLevel? GetLogLevel(params string[] keys) + { + var level = keys + .Select(k => GetSafeEnvironmentVariable(k)) + .Select(v => v.ToLowerInvariant() switch + { + "trace" => LogLevel.Trace, + "debug" => LogLevel.Debug, + "info" => LogLevel.Information, + "warn" => LogLevel.Warning, + "error" => LogLevel.Error, + "none" => LogLevel.None, + _ => null + }) + .FirstOrDefault(l => l != null); + return level; + } -public GlobalLogTarget ParseLogTargets(params string[] keys) -{ + public string GetLogDirectory(params string[] keys) + { + var path = keys + .Select(k => GetSafeEnvironmentVariable(k)) + .FirstOrDefault(p => !string.IsNullOrEmpty(p)); - var targets = keys - .Select(k => GetSafeEnvironmentVariable(k)) - .FirstOrDefault(p => !string.IsNullOrEmpty(p)); - if (string.IsNullOrWhiteSpace(targets)) - return GlobalLogTarget.File; + return path; + } - var logTargets = GlobalLogTarget.None; - var found = false; + public bool AnyConfigured(params string[] keys) => + keys + .Select(k => GetSafeEnvironmentVariable(k)) + .Any(p => !string.IsNullOrEmpty(p)); - foreach (var target in targets.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) + public GlobalLogTarget? ParseLogTargets(params string[] keys) { - if (IsSet(target, "stdout")) - logTargets |= GlobalLogTarget.StdOut; - else if (IsSet(target, "file")) - logTargets |= GlobalLogTarget.File; - else if (IsSet(target, "none")) - logTargets |= GlobalLogTarget.None; - } - return !found ? GlobalLogTarget.File : logTargets; + var targets = keys + .Select(k => GetSafeEnvironmentVariable(k)) + .FirstOrDefault(p => !string.IsNullOrEmpty(p)); + if (string.IsNullOrWhiteSpace(targets)) + return null; - bool IsSet(string k, string v) - { - var b = k.Trim().Equals(v, StringComparison.InvariantCultureIgnoreCase); - if (b) - found = true; - return b; - } -} + var logTargets = GlobalLogTarget.None; + var found = false; -internal static string GetDefaultLogDirectory() => - Environment.OSVersion.Platform == PlatformID.Win32NT - ? Path.Combine(Environment.GetEnvironmentVariable("PROGRAMDATA")!, "elastic", "apm-agent-dotnet", "logs") - : "/var/log/elastic/apm-agent-dotnet"; + foreach (var target in targets.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) + { + if (IsSet(target, "stdout")) + logTargets |= GlobalLogTarget.StdOut; + else if (IsSet(target, "file")) + logTargets |= GlobalLogTarget.File; + else if (IsSet(target, "none")) + logTargets |= GlobalLogTarget.None; + } + return !found ? null : logTargets; + + bool IsSet(string k, string v) + { + var b = k.Trim().Equals(v, StringComparison.InvariantCultureIgnoreCase); + if (b) + found = true; + return b; + } + } + internal static string GetDefaultLogDirectory() => + Environment.OSVersion.Platform == PlatformID.Win32NT + ? Path.Combine(Environment.GetEnvironmentVariable("PROGRAMDATA")!, "elastic", "apm-agent-dotnet", "logs") + : "/var/log/elastic/apm-agent-dotnet"; } [Flags] @@ -135,7 +134,8 @@ public static class LogEnvironmentVariables internal readonly struct GlobalLogConfiguration { - private GlobalLogConfiguration(bool isActive, LogLevel logLevel, GlobalLogTarget logTarget, string logFileDirectory, string logFilePrefix) : this() + private GlobalLogConfiguration(bool isActive, LogLevel logLevel, GlobalLogTarget logTarget, string logFileDirectory, string logFilePrefix) : + this() { IsActive = isActive; LogLevel = logLevel; @@ -160,23 +160,28 @@ internal static GlobalLogConfiguration FromEnvironment(IDictionary environmentVa { var config = new EnvironmentLoggingConfiguration(environmentVariables); var logLevel = config.GetLogLevel(ELASTIC_OTEL_LOG_LEVEL, ELASTIC_APM_PROFILER_LOG, ELASTIC_APM_LOG_LEVEL); - var logFileDirectory = config.GetLogFilePath(ELASTIC_OTEL_LOG_DIRECTORY, ELASTIC_APM_PROFILER_LOG_DIR, ELASTIC_APM_LOG_DIRECTORY); + var logFileDirectory = config.GetLogDirectory(ELASTIC_OTEL_LOG_DIRECTORY, ELASTIC_APM_PROFILER_LOG_DIR, ELASTIC_APM_LOG_DIRECTORY); var logFilePrefix = GetLogFilePrefix(); var logTarget = config.ParseLogTargets(ELASTIC_OTEL_LOG_TARGETS, ELASTIC_APM_PROFILER_LOG_TARGETS); - var isActive = config.AnyConfigured( - ELASTIC_OTEL_LOG_LEVEL, - ELASTIC_OTEL_LOG_DIRECTORY, - ELASTIC_OTEL_LOG_TARGETS, - ELASTIC_APM_LOG_DIRECTORY, - ELASTIC_APM_PROFILER_LOG, - ELASTIC_APM_PROFILER_LOG_DIR, - ELASTIC_APM_PROFILER_LOG_TARGETS, - ELASTIC_APM_STARTUP_HOOKS_LOGGING + //The presence of some variables enable file logging for historical purposes + var isActive = config.AnyConfigured(ELASTIC_APM_STARTUP_HOOKS_LOGGING); + if (logLevel.HasValue || !string.IsNullOrWhiteSpace(logFileDirectory) || logTarget.HasValue) + { + isActive = true; + if (logLevel is LogLevel.None) + isActive = false; + else if (logTarget is GlobalLogTarget.None) + isActive = false; + } - ) && logTarget != GlobalLogTarget.None && logLevel != LogLevel.None; + // now that we know what's actively configured, assign defaults + logFileDirectory ??= EnvironmentLoggingConfiguration.GetDefaultLogDirectory(); + var level = logLevel ?? LogLevel.Warning; - return new(isActive, logLevel, logTarget, logFileDirectory, logFilePrefix); + var target = logTarget ?? (isActive ? GlobalLogTarget.File : GlobalLogTarget.None); + + return new(isActive, level, target, logFileDirectory, logFilePrefix); } private static string GetLogFilePrefix() @@ -190,6 +195,4 @@ public string CreateLogFileName(string applicationName = "agent") var logFileName = Path.Combine(LogFileDirectory, $"{LogFilePrefix}.{applicationName}.log"); return logFileName; } - } - diff --git a/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs index f91a52916..2c6495a1c 100644 --- a/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs +++ b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs @@ -21,15 +21,15 @@ public void Check_Defaults() config.LogLevel.Should().Be(LogLevel.Warning); config.AgentLogFilePath.Should().StartWith(EnvironmentLoggingConfiguration.GetDefaultLogDirectory()); config.AgentLogFilePath.Should().EndWith(".agent.log"); - config.LogTargets.Should().Be(GlobalLogTarget.File); + //because is active is false log targets defaults to none; + config.LogTargets.Should().Be(GlobalLogTarget.None); } + + // [Theory] [InlineData(ELASTIC_OTEL_LOG_LEVEL, "Info")] [InlineData(ELASTIC_APM_PROFILER_LOG, "Info")] - //only if explicitly specified to 'none' should we not default to file logging. - [InlineData(ELASTIC_OTEL_LOG_LEVEL, "BadEnumValue")] - [InlineData(ELASTIC_APM_PROFILER_LOG, "BadEnumValue")] [InlineData(ELASTIC_OTEL_LOG_DIRECTORY, "1")] [InlineData(ELASTIC_APM_LOG_DIRECTORY, "1")] @@ -37,13 +37,30 @@ public void Check_Defaults() [InlineData(ELASTIC_APM_STARTUP_HOOKS_LOGGING, "1")] //only if explicitly specified to 'none' should we not default to file logging. [InlineData(ELASTIC_OTEL_LOG_TARGETS, "file")] - [InlineData(ELASTIC_OTEL_LOG_TARGETS, "BadEnumValue")] [InlineData(ELASTIC_APM_PROFILER_LOG_TARGETS, "file")] - [InlineData(ELASTIC_APM_PROFILER_LOG_TARGETS, "BadEnumValue")] public void CheckActivation(string environmentVariable, string value) { var config = GlobalLogConfiguration.FromEnvironment(new Hashtable { { environmentVariable, value } }); config.IsActive.Should().BeTrue(); + config.LogTargets.Should().Be(GlobalLogTarget.File); + } + + // + [Theory] + [InlineData(ELASTIC_OTEL_LOG_LEVEL, "none")] + [InlineData(ELASTIC_APM_PROFILER_LOG, "None")] + //only if explicitly specified to 'none' should we not default to file logging. + [InlineData(ELASTIC_OTEL_LOG_TARGETS, "none")] + [InlineData(ELASTIC_APM_PROFILER_LOG_TARGETS, "none")] + public void CheckDeactivation(string environmentVariable, string value) + { + var config = GlobalLogConfiguration.FromEnvironment(new Hashtable + { + { ELASTIC_OTEL_LOG_DIRECTORY, "" }, + { environmentVariable, value } + }); + config.IsActive.Should().BeFalse(); + config.LogTargets.Should().Be(GlobalLogTarget.None); } [Theory] @@ -121,11 +138,11 @@ static void Check(string key, string envVarValue) } [Theory] - [InlineData(null, GlobalLogTarget.File)] - [InlineData("", GlobalLogTarget.File)] - [InlineData("foo", GlobalLogTarget.File)] - [InlineData("foo,bar", GlobalLogTarget.File)] - [InlineData("foo;bar", GlobalLogTarget.File)] + [InlineData(null, GlobalLogTarget.None)] + [InlineData("", GlobalLogTarget.None)] + [InlineData("foo", GlobalLogTarget.None)] + [InlineData("foo,bar", GlobalLogTarget.None)] + [InlineData("foo;bar", GlobalLogTarget.None)] [InlineData("file;foo;bar", GlobalLogTarget.File)] [InlineData("file", GlobalLogTarget.File)] [InlineData("stdout", GlobalLogTarget.StdOut)] @@ -134,13 +151,13 @@ static void Check(string key, string envVarValue) [InlineData("FILE;StdOut", GlobalLogTarget.File | GlobalLogTarget.StdOut)] [InlineData("file;stdout;file", GlobalLogTarget.File | GlobalLogTarget.StdOut)] [InlineData("FILE;StdOut;stdout", GlobalLogTarget.File | GlobalLogTarget.StdOut)] - internal void Check_LogTargets_AreEvaluatedCorrectly(string envVarValue, GlobalLogTarget targets) + internal void Check_LogTargets_AreEvaluatedCorrectly(string envVarValue, GlobalLogTarget? targets) { Check(ELASTIC_APM_PROFILER_LOG_TARGETS, envVarValue, targets); Check(ELASTIC_OTEL_LOG_TARGETS, envVarValue, targets); return; - static void Check(string key, string envVarValue, GlobalLogTarget targets) + static void Check(string key, string envVarValue, GlobalLogTarget? targets) { var config = CreateConfig(key, envVarValue); config.LogTargets.Should().Be(targets, "{0}", key); From 05bd10760006ac19b679c9ffb7067e0d71adfe93 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Tue, 16 Jul 2024 11:22:59 +0200 Subject: [PATCH 17/21] start rename to OTEL_* variables --- .../Logging/GlobalLogConfiguration.cs | 8 ++++---- .../GlobalLogConfigurationPrecedenceTests.cs | 6 +++--- .../Config/GlobalLogConfigurationTests.cs | 16 ++++++++-------- .../ProfiledApplication.cs | 4 ++-- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs index 4ffcda2fb..d5f81f135 100644 --- a/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs +++ b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs @@ -113,8 +113,8 @@ internal enum GlobalLogTarget public static class LogEnvironmentVariables { // ReSharper disable once InconsistentNaming - public const string ELASTIC_OTEL_LOG_LEVEL = nameof(ELASTIC_OTEL_LOG_LEVEL); - public const string ELASTIC_OTEL_LOG_DIRECTORY = nameof(ELASTIC_OTEL_LOG_DIRECTORY); + public const string OTEL_LOG_LEVEL = nameof(OTEL_LOG_LEVEL); + public const string OTEL_DOTNET_AUTO_LOG_DIRECTORY = nameof(OTEL_DOTNET_AUTO_LOG_DIRECTORY); public const string ELASTIC_OTEL_LOG_TARGETS = nameof(ELASTIC_OTEL_LOG_TARGETS); public const string ELASTIC_APM_LOG_LEVEL = nameof(ELASTIC_APM_LOG_LEVEL); @@ -159,8 +159,8 @@ private GlobalLogConfiguration(bool isActive, LogLevel logLevel, GlobalLogTarget internal static GlobalLogConfiguration FromEnvironment(IDictionary environmentVariables = null) { var config = new EnvironmentLoggingConfiguration(environmentVariables); - var logLevel = config.GetLogLevel(ELASTIC_OTEL_LOG_LEVEL, ELASTIC_APM_PROFILER_LOG, ELASTIC_APM_LOG_LEVEL); - var logFileDirectory = config.GetLogDirectory(ELASTIC_OTEL_LOG_DIRECTORY, ELASTIC_APM_PROFILER_LOG_DIR, ELASTIC_APM_LOG_DIRECTORY); + var logLevel = config.GetLogLevel(OTEL_LOG_LEVEL, ELASTIC_APM_PROFILER_LOG, ELASTIC_APM_LOG_LEVEL); + var logFileDirectory = config.GetLogDirectory(OTEL_DOTNET_AUTO_LOG_DIRECTORY, ELASTIC_APM_PROFILER_LOG_DIR, ELASTIC_APM_LOG_DIRECTORY); var logFilePrefix = GetLogFilePrefix(); var logTarget = config.ParseLogTargets(ELASTIC_OTEL_LOG_TARGETS, ELASTIC_APM_PROFILER_LOG_TARGETS); diff --git a/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationPrecedenceTests.cs b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationPrecedenceTests.cs index 96121d8da..75c0db47d 100644 --- a/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationPrecedenceTests.cs +++ b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationPrecedenceTests.cs @@ -16,7 +16,7 @@ public class GlobalLogConfigurationPrecedenceTests public void CheckLogLevelPrecedence() { var config = CreateConfig([ - (ELASTIC_OTEL_LOG_LEVEL, "trace"), + (OTEL_LOG_LEVEL, "trace"), (ELASTIC_APM_PROFILER_LOG, "info"), (ELASTIC_APM_LOG_LEVEL, "error"), ]); @@ -38,11 +38,11 @@ public void CheckLogLevelPrecedence() public void CheckLogDirPrecedence() { var config = CreateConfig([ - (ELASTIC_OTEL_LOG_DIRECTORY, nameof(ELASTIC_OTEL_LOG_DIRECTORY)), + (OTEL_DOTNET_AUTO_LOG_DIRECTORY, nameof(OTEL_DOTNET_AUTO_LOG_DIRECTORY)), (ELASTIC_APM_PROFILER_LOG_DIR, nameof(ELASTIC_APM_PROFILER_LOG_DIR)), (ELASTIC_APM_LOG_DIRECTORY, nameof(ELASTIC_APM_LOG_DIRECTORY)), ]); - config.LogFileDirectory.Should().Be(nameof(ELASTIC_OTEL_LOG_DIRECTORY)); + config.LogFileDirectory.Should().Be(nameof(OTEL_DOTNET_AUTO_LOG_DIRECTORY)); config = CreateConfig([ (ELASTIC_APM_PROFILER_LOG_DIR, nameof(ELASTIC_APM_PROFILER_LOG_DIR)), diff --git a/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs index 2c6495a1c..7b9b34222 100644 --- a/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs +++ b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs @@ -28,10 +28,10 @@ public void Check_Defaults() // [Theory] - [InlineData(ELASTIC_OTEL_LOG_LEVEL, "Info")] + [InlineData(OTEL_LOG_LEVEL, "Info")] [InlineData(ELASTIC_APM_PROFILER_LOG, "Info")] - [InlineData(ELASTIC_OTEL_LOG_DIRECTORY, "1")] + [InlineData(OTEL_DOTNET_AUTO_LOG_DIRECTORY, "1")] [InlineData(ELASTIC_APM_LOG_DIRECTORY, "1")] [InlineData(ELASTIC_APM_PROFILER_LOG_DIR, "1")] [InlineData(ELASTIC_APM_STARTUP_HOOKS_LOGGING, "1")] @@ -47,7 +47,7 @@ public void CheckActivation(string environmentVariable, string value) // [Theory] - [InlineData(ELASTIC_OTEL_LOG_LEVEL, "none")] + [InlineData(OTEL_LOG_LEVEL, "none")] [InlineData(ELASTIC_APM_PROFILER_LOG, "None")] //only if explicitly specified to 'none' should we not default to file logging. [InlineData(ELASTIC_OTEL_LOG_TARGETS, "none")] @@ -56,7 +56,7 @@ public void CheckDeactivation(string environmentVariable, string value) { var config = GlobalLogConfiguration.FromEnvironment(new Hashtable { - { ELASTIC_OTEL_LOG_DIRECTORY, "" }, + { OTEL_DOTNET_AUTO_LOG_DIRECTORY, "" }, { environmentVariable, value } }); config.IsActive.Should().BeFalse(); @@ -70,7 +70,7 @@ public void CheckDeactivation(string environmentVariable, string value) [InlineData(ELASTIC_OTEL_LOG_TARGETS, "None")] [InlineData(ELASTIC_APM_PROFILER_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")] //setting profiler log level to none will result in no global trace logging [InlineData(ELASTIC_APM_PROFILER_LOG, "None")] public void CheckNonActivation(string environmentVariable, string value) @@ -92,7 +92,7 @@ public void Check_LogLevelValues_AreMappedCorrectly(string envVarValue, LogLevel { Check(ELASTIC_APM_PROFILER_LOG, envVarValue, logLevel); Check(ELASTIC_APM_LOG_LEVEL, envVarValue, logLevel); - Check(ELASTIC_OTEL_LOG_LEVEL, envVarValue, logLevel); + Check(OTEL_LOG_LEVEL, envVarValue, logLevel); return; static void Check(string key, string envVarValue, LogLevel level) @@ -111,7 +111,7 @@ public void Check_InvalidLogLevelValues_AreMappedToDefaultWarn(string envVarValu { Check(ELASTIC_APM_PROFILER_LOG, envVarValue); Check(ELASTIC_APM_LOG_LEVEL, envVarValue); - Check(ELASTIC_OTEL_LOG_LEVEL, envVarValue); + Check(OTEL_LOG_LEVEL, envVarValue); return; static void Check(string key, string envVarValue) @@ -126,7 +126,7 @@ public void Check_LogDir_IsEvaluatedCorrectly() { Check(ELASTIC_APM_PROFILER_LOG_DIR, "/foo/bar"); Check(ELASTIC_APM_LOG_DIRECTORY, "/foo/bar"); - Check(ELASTIC_OTEL_LOG_DIRECTORY, "/foo/bar"); + Check(OTEL_DOTNET_AUTO_LOG_DIRECTORY, "/foo/bar"); return; static void Check(string key, string envVarValue) diff --git a/test/profiler/Elastic.Apm.Profiler.Managed.Tests/ProfiledApplication.cs b/test/profiler/Elastic.Apm.Profiler.Managed.Tests/ProfiledApplication.cs index aa1194c8a..d4b98a126 100644 --- a/test/profiler/Elastic.Apm.Profiler.Managed.Tests/ProfiledApplication.cs +++ b/test/profiler/Elastic.Apm.Profiler.Managed.Tests/ProfiledApplication.cs @@ -144,9 +144,9 @@ public void Start( environmentVariables["ELASTIC_APM_PROFILER_INTEGRATIONS"] = Path.Combine(SolutionPaths.Root, "src", "profiler", "Elastic.Apm.Profiler.Managed", "integrations.yml"); - environmentVariables[ELASTIC_OTEL_LOG_LEVEL] = "trace"; + environmentVariables[OTEL_LOG_LEVEL] = "trace"; // log to relative logs directory for managed loader - environmentVariables[ELASTIC_OTEL_LOG_DIRECTORY] = Path.Combine(SolutionPaths.Root, "logs"); + environmentVariables[OTEL_DOTNET_AUTO_LOG_DIRECTORY] = Path.Combine(SolutionPaths.Root, "logs"); environmentVariables[ELASTIC_OTEL_LOG_TARGETS] = "file;stdout"; //environmentVariables["ELASTIC_APM_PROFILER_LOG_IL"] = "true"; From cb7b9a1519adc1eae1f0bc5613740c25a3e2337e Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Mon, 22 Jul 2024 11:51:42 +0200 Subject: [PATCH 18/21] update profile environment variables --- src/profiler/elastic_apm_profiler/src/profiler/env.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/profiler/elastic_apm_profiler/src/profiler/env.rs b/src/profiler/elastic_apm_profiler/src/profiler/env.rs index ba0284236..5538a4db8 100644 --- a/src/profiler/elastic_apm_profiler/src/profiler/env.rs +++ b/src/profiler/elastic_apm_profiler/src/profiler/env.rs @@ -45,8 +45,8 @@ const ELASTIC_OTEL_LOG_TARGETS_ENV_VAR: &str = "ELASTIC_OTEL_LOG_TARGETS"; const ELASTIC_APM_PROFILER_LOG_DIR_ENV_VAR: &str = "ELASTIC_APM_PROFILER_LOG_DIR"; const ELASTIC_APM_PROFILER_LOG_ENV_VAR: &str = "ELASTIC_APM_PROFILER_LOG"; -const ELASTIC_OTEL_LOG_LEVEL_ENV_VAR: &str = "ELASTIC_OTEL_LOG_LEVEL"; -const ELASTIC_OTEL_LOG_DIRECTORY_ENV_VAR: &str = "ELASTIC_OTEL_LOG_DIRECTORY"; +const OTEL_LOG_LEVEL_ENV_VAR: &str = "OTEL_LOG_LEVEL"; +const OTEL_DOTNET_AUTO_LOG_DIRECTORY_ENV_VAR: &str = "OTEL_DOTNET_AUTO_LOG_DIRECTORY"; const ELASTIC_APM_LOG_LEVEL_ENV_VAR: &str = "ELASTIC_APM_LOG_LEVEL"; const ELASTIC_APM_LOG_DIRECTORY_ENV_VAR: &str = "ELASTIC_APM_LOG_DIRECTORY"; @@ -106,6 +106,7 @@ pub fn get_env_vars() -> String { let key = k.to_uppercase(); if key.starts_with("ELASTIC_") || key.starts_with("CORECLR_") + || key.starts_with("OTEL_") || key.starts_with("COR_") || key == APP_POOL_ID_ENV_VAR || key == DOTNET_CLI_TELEMETRY_PROFILE_ENV_VAR @@ -207,7 +208,7 @@ fn read_log_targets_from_env_var() -> HashSet { pub fn read_log_level_from_env_var(default: LevelFilter) -> LevelFilter { match ( - std::env::var(ELASTIC_OTEL_LOG_LEVEL_ENV_VAR), + std::env::var(OTEL_LOG_LEVEL_ENV_VAR), std::env::var(ELASTIC_APM_PROFILER_LOG_ENV_VAR), std::env::var(ELASTIC_APM_LOG_DIRECTORY_ENV_VAR) ) { @@ -311,7 +312,7 @@ fn get_home_log_dir() -> PathBuf { fn get_log_dir() -> PathBuf { match ( - std::env::var(ELASTIC_OTEL_LOG_DIRECTORY_ENV_VAR), + std::env::var(OTEL_DOTNET_AUTO_LOG_DIRECTORY_ENV_VAR), std::env::var(ELASTIC_APM_PROFILER_LOG_DIR_ENV_VAR), std::env::var(ELASTIC_APM_LOG_DIRECTORY_ENV_VAR), ) { From d587b0e02b27f441e02ba632b7a77da3669c9bc0 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Mon, 22 Jul 2024 11:52:52 +0200 Subject: [PATCH 19/21] dotnet format --- .../Logging/GlobalLogConfiguration.cs | 126 +++++++++--------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs index d5f81f135..4fd944cb1 100644 --- a/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs +++ b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs @@ -27,79 +27,79 @@ internal class EnvironmentLoggingConfiguration(IDictionary environmentVariables { public IDictionary EnvironmentVariables { get; } = environmentVariables ?? Environment.GetEnvironmentVariables(); - public string GetSafeEnvironmentVariable(string key) - { - var value = EnvironmentVariables.Contains(key) ? EnvironmentVariables[key]?.ToString() : null; - return value ?? string.Empty; - } +public string GetSafeEnvironmentVariable(string key) +{ + var value = EnvironmentVariables.Contains(key) ? EnvironmentVariables[key]?.ToString() : null; + return value ?? string.Empty; +} - public LogLevel? GetLogLevel(params string[] keys) - { - var level = keys - .Select(k => GetSafeEnvironmentVariable(k)) - .Select(v => v.ToLowerInvariant() switch - { - "trace" => LogLevel.Trace, - "debug" => LogLevel.Debug, - "info" => LogLevel.Information, - "warn" => LogLevel.Warning, - "error" => LogLevel.Error, - "none" => LogLevel.None, - _ => null - }) - .FirstOrDefault(l => l != null); - return level; - } +public LogLevel? GetLogLevel(params string[] keys) +{ + var level = keys + .Select(k => GetSafeEnvironmentVariable(k)) + .Select(v => v.ToLowerInvariant() switch + { + "trace" => LogLevel.Trace, + "debug" => LogLevel.Debug, + "info" => LogLevel.Information, + "warn" => LogLevel.Warning, + "error" => LogLevel.Error, + "none" => LogLevel.None, + _ => null + }) + .FirstOrDefault(l => l != null); + return level; +} - public string GetLogDirectory(params string[] keys) - { - var path = keys - .Select(k => GetSafeEnvironmentVariable(k)) - .FirstOrDefault(p => !string.IsNullOrEmpty(p)); +public string GetLogDirectory(params string[] keys) +{ + var path = keys + .Select(k => GetSafeEnvironmentVariable(k)) + .FirstOrDefault(p => !string.IsNullOrEmpty(p)); - return path; - } + return path; +} - public bool AnyConfigured(params string[] keys) => - keys - .Select(k => GetSafeEnvironmentVariable(k)) - .Any(p => !string.IsNullOrEmpty(p)); +public bool AnyConfigured(params string[] keys) => + keys + .Select(k => GetSafeEnvironmentVariable(k)) + .Any(p => !string.IsNullOrEmpty(p)); - public GlobalLogTarget? ParseLogTargets(params string[] keys) - { - var targets = keys - .Select(k => GetSafeEnvironmentVariable(k)) - .FirstOrDefault(p => !string.IsNullOrEmpty(p)); - if (string.IsNullOrWhiteSpace(targets)) - return null; +public GlobalLogTarget? ParseLogTargets(params string[] keys) +{ + var targets = keys + .Select(k => GetSafeEnvironmentVariable(k)) + .FirstOrDefault(p => !string.IsNullOrEmpty(p)); + if (string.IsNullOrWhiteSpace(targets)) + return null; - var logTargets = GlobalLogTarget.None; - var found = false; + var logTargets = GlobalLogTarget.None; + var found = false; - foreach (var target in targets.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) - { - if (IsSet(target, "stdout")) - logTargets |= GlobalLogTarget.StdOut; - else if (IsSet(target, "file")) - logTargets |= GlobalLogTarget.File; - else if (IsSet(target, "none")) - logTargets |= GlobalLogTarget.None; - } - return !found ? null : logTargets; + foreach (var target in targets.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) + { + if (IsSet(target, "stdout")) + logTargets |= GlobalLogTarget.StdOut; + else if (IsSet(target, "file")) + logTargets |= GlobalLogTarget.File; + else if (IsSet(target, "none")) + logTargets |= GlobalLogTarget.None; + } + return !found ? null : logTargets; - bool IsSet(string k, string v) - { - var b = k.Trim().Equals(v, StringComparison.InvariantCultureIgnoreCase); - if (b) - found = true; - return b; - } + bool IsSet(string k, string v) + { + var b = k.Trim().Equals(v, StringComparison.InvariantCultureIgnoreCase); + if (b) + found = true; + return b; } +} - internal static string GetDefaultLogDirectory() => - Environment.OSVersion.Platform == PlatformID.Win32NT - ? Path.Combine(Environment.GetEnvironmentVariable("PROGRAMDATA")!, "elastic", "apm-agent-dotnet", "logs") - : "/var/log/elastic/apm-agent-dotnet"; +internal static string GetDefaultLogDirectory() => + Environment.OSVersion.Platform == PlatformID.Win32NT + ? Path.Combine(Environment.GetEnvironmentVariable("PROGRAMDATA")!, "elastic", "apm-agent-dotnet", "logs") + : "/var/log/elastic/apm-agent-dotnet"; } [Flags] From e9087dabcc97e31fbc45baba8da987f18729f1d0 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Mon, 22 Jul 2024 12:17:37 +0200 Subject: [PATCH 20/21] Only enable file logging if OTEL or APM log level >= debug --- src/Elastic.Apm/Logging/GlobalLogConfiguration.cs | 14 +++++++++++--- .../Config/GlobalLogConfigurationTests.cs | 6 ++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs index 4fd944cb1..dfd1ac6b3 100644 --- a/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs +++ b/src/Elastic.Apm/Logging/GlobalLogConfiguration.cs @@ -159,14 +159,23 @@ private GlobalLogConfiguration(bool isActive, LogLevel logLevel, GlobalLogTarget internal static GlobalLogConfiguration FromEnvironment(IDictionary environmentVariables = null) { var config = new EnvironmentLoggingConfiguration(environmentVariables); - var logLevel = config.GetLogLevel(OTEL_LOG_LEVEL, ELASTIC_APM_PROFILER_LOG, ELASTIC_APM_LOG_LEVEL); + var otelLogLevel = config.GetLogLevel(OTEL_LOG_LEVEL); + var profilerLogLevel = config.GetLogLevel(ELASTIC_APM_PROFILER_LOG); + var apmLogLevel = config.GetLogLevel(ELASTIC_APM_LOG_LEVEL); + + var logLevel = otelLogLevel ?? profilerLogLevel ?? apmLogLevel; + var logFileDirectory = config.GetLogDirectory(OTEL_DOTNET_AUTO_LOG_DIRECTORY, ELASTIC_APM_PROFILER_LOG_DIR, ELASTIC_APM_LOG_DIRECTORY); var logFilePrefix = GetLogFilePrefix(); var logTarget = config.ParseLogTargets(ELASTIC_OTEL_LOG_TARGETS, ELASTIC_APM_PROFILER_LOG_TARGETS); //The presence of some variables enable file logging for historical purposes var isActive = config.AnyConfigured(ELASTIC_APM_STARTUP_HOOKS_LOGGING); - if (logLevel.HasValue || !string.IsNullOrWhiteSpace(logFileDirectory) || logTarget.HasValue) + var activeFromLogging = + otelLogLevel is <= LogLevel.Debug + || apmLogLevel is <= LogLevel.Debug + || profilerLogLevel.HasValue; + if (activeFromLogging || !string.IsNullOrWhiteSpace(logFileDirectory) || logTarget.HasValue) { isActive = true; if (logLevel is LogLevel.None) @@ -180,7 +189,6 @@ internal static GlobalLogConfiguration FromEnvironment(IDictionary environmentVa var level = logLevel ?? LogLevel.Warning; var target = logTarget ?? (isActive ? GlobalLogTarget.File : GlobalLogTarget.None); - return new(isActive, level, target, logFileDirectory, logFilePrefix); } diff --git a/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs index 7b9b34222..63cf310f9 100644 --- a/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs +++ b/test/Elastic.Apm.Tests/Config/GlobalLogConfigurationTests.cs @@ -28,9 +28,9 @@ public void Check_Defaults() // [Theory] - [InlineData(OTEL_LOG_LEVEL, "Info")] + [InlineData(OTEL_LOG_LEVEL, "Debug")] + [InlineData(ELASTIC_APM_LOG_LEVEL, "Debug")] [InlineData(ELASTIC_APM_PROFILER_LOG, "Info")] - [InlineData(OTEL_DOTNET_AUTO_LOG_DIRECTORY, "1")] [InlineData(ELASTIC_APM_LOG_DIRECTORY, "1")] [InlineData(ELASTIC_APM_PROFILER_LOG_DIR, "1")] @@ -48,6 +48,8 @@ public void CheckActivation(string environmentVariable, string value) // [Theory] [InlineData(OTEL_LOG_LEVEL, "none")] + [InlineData(OTEL_LOG_LEVEL, "Info")] + [InlineData(ELASTIC_APM_LOG_LEVEL, "Info")] [InlineData(ELASTIC_APM_PROFILER_LOG, "None")] //only if explicitly specified to 'none' should we not default to file logging. [InlineData(ELASTIC_OTEL_LOG_TARGETS, "none")] From ab64bdeb1590a9e4dce11163e81f3f4b52be1ff2 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Mon, 22 Jul 2024 12:29:48 +0200 Subject: [PATCH 21/21] update documentation --- docs/troubleshooting.asciidoc | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/troubleshooting.asciidoc b/docs/troubleshooting.asciidoc index 5c21c3f43..2e9d962f0 100644 --- a/docs/troubleshooting.asciidoc +++ b/docs/troubleshooting.asciidoc @@ -40,7 +40,7 @@ The easiest way to get debug information from the Agent, regardless of the way i Specifying at least one of the following environment variables will ensure the agent logs to a file -`ELASTIC_OTEL_LOG_LEVEL` _(optional)_:: +`OTEL_LOG_LEVEL` _(optional)_:: The log level at which the profiler should log. Valid values are @@ -56,16 +56,16 @@ The default value is `warn`. More verbose log levels like `trace` and `debug` ca affect the runtime performance of profiler auto instrumentation, so are recommended _only_ for diagnostics purposes. -`ELASTIC_OTEL_LOG_DIRECTORY` _(optional)_:: +NOTE: if `ELASTIC_OTEL_LOG_TARGETS` is not explicitly set to include `file` global file logging will only +be enabled when configured with `trace` or `debug`. -The directory in which to write profiler log files. If unset, defaults to +`OTEL_DOTNET_AUTO_LOG_DIRECTORY` _(optional)_:: + +The directory in which to write log files. If unset, defaults to * `%PROGRAMDATA%\elastic\apm-agent-dotnet\logs` on Windows * `/var/log/elastic/apm-agent-dotnet` on Linux -If the default directory cannot be written to for some reason, the profiler -will try to write log files to a `logs` directory in the home directory specified -by `ELASTIC_APM_PROFILER_HOME` environment variable. [IMPORTANT] -- @@ -82,8 +82,7 @@ A semi-colon separated list of targets for profiler logs. Valid values are * file * stdout -The default value is `file`, which logs to the directory specified by -`ELASTIC_APM_PROFILER_LOG_DIR` environment variable. +The default value is `file` if `OTEL_DOTNET_AUTO_LOG_DIRECTORY` is set or `OTEL_LOG_LEVEL` is set to `trace` or `debug`. [float]