Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable global file logging #2371

Merged
merged 22 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9d167ca
Ensure a globally enabled file logger can be specified
Mpdreamz Jun 3, 2024
404c747
Share globalconfiguration logic with managed loader without taking a …
Mpdreamz Jun 3, 2024
42cd909
Startup hooks now listen to global log configuration
Mpdreamz Jun 3, 2024
6e205e8
Add more tests
Mpdreamz Jun 3, 2024
ec2cd25
Share logger between loader and managed profiler
Mpdreamz Jun 3, 2024
cd548ea
remove namespace
Mpdreamz Jun 3, 2024
33e57fa
clearer compile constants
Mpdreamz Jun 3, 2024
b9fccc9
Introduce new global environment configs to profiler
Mpdreamz Jun 3, 2024
c6d1c7c
move profiler tests over to otel log config and update docs
Mpdreamz Jun 3, 2024
70edc92
update troubleshotting.asciidoc
Mpdreamz Jun 3, 2024
734172a
Ensure we also always globally log when using .NET core IServiceColle…
Mpdreamz Jun 3, 2024
65ee18a
run dotnet-format
Mpdreamz Jun 3, 2024
71afd16
Err != None
Mpdreamz Jun 3, 2024
fc63730
Apply suggestions from code review
Mpdreamz Jun 4, 2024
617ccc5
remove _FILE_ from environment variables
Mpdreamz Jun 4, 2024
2cc27fa
Make activation of global logging more explicit
Mpdreamz Jun 4, 2024
05bd107
start rename to OTEL_* variables
Mpdreamz Jul 16, 2024
596a183
Merge remote-tracking branch 'origin/main' into feature/external-logg…
Mpdreamz Jul 22, 2024
cb7b9a1
update profile environment variables
Mpdreamz Jul 22, 2024
d587b0e
dotnet format
Mpdreamz Jul 22, 2024
e9087da
Only enable file logging if OTEL or APM log level >= debug
Mpdreamz Jul 22, 2024
ab64bde
update documentation
Mpdreamz Jul 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions docs/setup-auto-instrumentation.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -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 <<config-service-name,`ELASTIC_APM_SERVICE_NAME`>>
environment variable.

`ELASTIC_APM_PROFILER_LOG` _(optional)_::
`ELASTIC_OTEL_LOG_LEVEL` _(optional)_::

The log level at which the profiler should log. Valid values are

Expand All @@ -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_LOG_DIRECTORY` _(optional)_::

The directory in which to write profiler log files. If unset, defaults to

Expand All @@ -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
Expand All @@ -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

Expand All @@ -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`
108 changes: 55 additions & 53 deletions docs/troubleshooting.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.

The easiest way to get debug information from the Agent, regardless of the way it's run, is to enable global file logging.

Specifying at least one of the following environment variables will ensure the agent logs to a file

`ELASTIC_OTEL_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_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]]
Expand Down Expand Up @@ -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[`<system.diagnostics>`] 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]
----
<configuration>
<!-- other sections .... -->
<system.diagnostics>
<sources>
<source name="Elastic.Apm"> <1>
<listeners>
<add name="file"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="my_log_file.log" />
</listeners>
</source>
</sources>
</system.diagnostics>
</configuration>
----
<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
Expand Down Expand Up @@ -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 <<zero-code-change-setup, startup hook>> 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 <<zero-code-change-setup, startup hook>> integration throws an exception, additional detail can be obtained through <<collect-logs-globally, enabling the global log collection>>.

[float]
[[agent-overhead]]
Expand Down
52 changes: 27 additions & 25 deletions src/Elastic.Apm/AgentComponents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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)
/// <summary>
/// This ensures agents will respect externally provided loggers.
/// <para>If the agent is started as part of profiling it should adhere to profiling configuration</para>
/// <para>If file logging environment variables are set we should always log to that location</para>
/// </summary>
/// <param name="fallbackLogger"></param>
/// <param name="agentLogLevel"></param>
/// <param name="environmentVariables"></param>
/// <returns></returns>
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.AgentLogFilePath));
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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/// <summary>
Expand Down
90 changes: 0 additions & 90 deletions src/Elastic.Apm/Config/ProfilerLogConfig.cs

This file was deleted.

Loading
Loading