Skip to content

Commit 303c4f9

Browse files
authored
[HandsOff Config] Read hands-off configuration files with libdatadog (#7282)
## Summary of changes - Introducing stable config managed part with libdatadog: reading 2 yml files through libdatadog: one local file, one fleet file. - Added some unit testing for GlobalConfigurationSource ## Reason for change Stable configuration managed implementation ## Implementation details - Added DD_APPLICATION_MONITORING_CONFIG_FILE_ENABLED to deactivate hands off config for support cases. Env var is added here as well: DataDog/dd-go#193905 - Prevent logging when initializing stable config from libdatadog as the Lazy IsLibDatadogAvailable ends up calling itself again even if it uses a class with a static logger field - Using results instead of logging to log later on - transforming logger to lazy<logger> in some class - ## Test coverage - Added some unit testing for GlobalConfigurationSource ## Other details <!-- Fixes #{issue} --> <!-- ⚠️ Note: where possible, please obtain 2 approvals prior to merging. Unless CODEOWNERS specifies otherwise, for external teams it is typically best to have one review from a team member, and one review from apm-dotnet. Trivial changes do not require 2 reviews. -->
1 parent 1006e19 commit 303c4f9

File tree

45 files changed

+862
-86
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+862
-86
lines changed

tracer/src/Datadog.Trace/Ci/Configuration/TestOptimizationSettings.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using Datadog.Trace.Configuration;
1313
using Datadog.Trace.Configuration.ConfigurationSources.Telemetry;
1414
using Datadog.Trace.Configuration.Telemetry;
15+
using Datadog.Trace.LibDatadog;
1516
using Datadog.Trace.Telemetry;
1617
using Datadog.Trace.Util;
1718

@@ -345,10 +346,10 @@ internal void SetDefaultManualInstrumentationSettings()
345346
}
346347

347348
private TracerSettings InitializeTracerSettings()
348-
=> InitializeTracerSettings(GlobalConfigurationSource.CreateDefaultConfigurationSource());
349+
=> InitializeTracerSettings(GlobalConfigurationSource.Instance);
349350

350351
// Internal for testing
351-
internal TracerSettings InitializeTracerSettings(CompositeConfigurationSource source)
352+
internal TracerSettings InitializeTracerSettings(IConfigurationSource source)
352353
{
353354
// This is a somewhat hacky way to "tell" TracerSettings that we're running in CI Visibility
354355
// There's no doubt various other ways we could flag it based on values we're _already_ extracting,
@@ -368,7 +369,7 @@ internal TracerSettings InitializeTracerSettings(CompositeConfigurationSource so
368369
}
369370

370371
var newSource = new CompositeConfigurationSource([new DictionaryObjectConfigurationSource(additionalSource), source]);
371-
return new TracerSettings(newSource, telemetry, new OverrideErrorLog());
372+
return new TracerSettings(newSource, telemetry, new OverrideErrorLog(), new LibDatadogAvailableResult(false));
372373
}
373374
}
374375
}

tracer/src/Datadog.Trace/ClrProfiler/Instrumentation.cs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -50,27 +50,6 @@ public static class Instrumentation
5050

5151
private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(Instrumentation));
5252

53-
/// <summary>
54-
/// Gets a value indicating whether Datadog's profiler is attached to the current process.
55-
/// </summary>
56-
/// <value>
57-
/// <c>true</c> if the profiler is currently attached; <c>false</c> otherwise.
58-
/// </value>
59-
public static bool ProfilerAttached
60-
{
61-
get
62-
{
63-
try
64-
{
65-
return NativeMethods.IsProfilerAttached();
66-
}
67-
catch (DllNotFoundException)
68-
{
69-
return false;
70-
}
71-
}
72-
}
73-
7453
/// <summary>
7554
/// Gets a value indicating the version of the native Datadog profiler. This method
7655
/// is rewritten by the profiler.

tracer/src/Datadog.Trace/ClrProfiler/NativeMethods.cs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,35 @@
33
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
44
// </copyright>
55

6-
using System;
76
using System.Runtime.InteropServices;
8-
using Datadog.Trace.Debugger.PInvoke;
97
using Datadog.Trace.Iast.Analyzers;
108

119
// ReSharper disable MemberHidesStaticFromOuterClass
1210
namespace Datadog.Trace.ClrProfiler
1311
{
12+
/// <remarks>
13+
/// This class should not log or contain a logger field or call a type containing a logger. Logging here could be an issue as this is accessed before Configuration objects are built. Logging here could create a loop where Configuration building tests if profiler is attached to access libdatadog, the test wants to log, the Logger being created for the first time tried to access the Configuration object.
14+
/// </remarks>
1415
internal static class NativeMethods
1516
{
16-
private static readonly bool IsWindows = FrameworkDescription.Instance.IsWindows();
17+
public static bool IsWindows
18+
{
19+
get
20+
{
21+
#if NETFRAMEWORK
22+
return true;
23+
#else
24+
return System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
25+
#endif
26+
}
27+
}
1728

29+
/// <summary>
30+
/// Gets a value indicating whether Datadog's instrumentation library (aka CLR profiler) is attached to the current process.
31+
/// </summary>
32+
/// <value>
33+
/// <c>true</c> if the profiler is currently attached; <c>false</c> otherwise.
34+
/// </value>
1835
public static bool IsProfilerAttached()
1936
{
2037
if (IsWindows)

tracer/src/Datadog.Trace/Configuration/ConfigurationKeys.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,12 @@ internal static partial class ConfigurationKeys
571571
/// <seealso cref="TracerSettings.GraphQLErrorExtensions"/>
572572
public const string GraphQLErrorExtensions = "DD_TRACE_GRAPHQL_ERROR_EXTENSIONS";
573573

574+
/// <summary>
575+
/// Configuration key for deactivating reading the application monitoring config file through libdatadog (hands off config).
576+
/// True by default
577+
/// </summary>
578+
public const string ApplicationMonitoringConfigFileEnabled = "DD_APPLICATION_MONITORING_CONFIG_FILE_ENABLED";
579+
574580
/// <summary>
575581
/// String constants for CI Visibility configuration keys.
576582
/// </summary>

tracer/src/Datadog.Trace/Configuration/ConfigurationSources/GlobalConfigurationSource.cs

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
using System;
99
using System.Diagnostics.CodeAnalysis;
1010
using System.IO;
11+
using Datadog.Trace.Configuration.ConfigurationSources;
1112
using Datadog.Trace.Configuration.Telemetry;
13+
using Datadog.Trace.LibDatadog.HandsOffConfiguration;
1214
using Datadog.Trace.Telemetry;
1315

1416
namespace Datadog.Trace.Configuration;
@@ -21,30 +23,71 @@ internal class GlobalConfigurationSource
2123
/// <summary>
2224
/// Gets the configuration source instance.
2325
/// </summary>
24-
internal static IConfigurationSource Instance { get; private set; } = CreateDefaultConfigurationSource();
26+
internal static IConfigurationSource Instance => CreationResult.ConfigurationSource;
27+
28+
internal static GlobalConfigurationSourceResult CreationResult { get; private set; } = CreateDefaultConfigurationSource();
2529

2630
/// <summary>
2731
/// Creates a <see cref="IConfigurationSource"/> by combining environment variables,
28-
/// AppSettings where available, and a local datadog.json file, if present.
32+
/// Precedence is as follows:
33+
/// - fleet hands-off config, if enabled through DD_APPLICATION_MONITORING_CONFIG_FILE_ENABLED
34+
/// - environment variables
35+
/// - local hands-off config, if enabled through DD_APPLICATION_MONITORING_CONFIG_FILE_ENABLED
36+
/// - AppSettings, app/web.config, if .NET Framework and if file exists
37+
/// - local datadog.json file, if file exists
2938
/// </summary>
3039
/// <returns>A new <see cref="IConfigurationSource"/> instance.</returns>
31-
internal static CompositeConfigurationSource CreateDefaultConfigurationSource()
40+
internal static GlobalConfigurationSourceResult CreateDefaultConfigurationSource(string? handsOffLocalConfigPath = null, string? handsOffFleetConfigPath = null, bool? isLibdatadogAvailable = null)
3241
{
33-
// env > AppSettings > datadog.json
42+
string? message = null;
43+
Exception? exception = null;
44+
var resultType = Result.Success;
3445
var configurationSource = new CompositeConfigurationSource();
35-
configurationSource.Add(new EnvironmentConfigurationSource());
46+
var environmentSource = new EnvironmentConfigurationSource();
47+
var configBuilder = new ConfigurationBuilder(environmentSource, TelemetryFactory.Config);
48+
var applicationMonitoringConfigFileEnabled = configBuilder.WithKeys(ConfigurationKeys.ApplicationMonitoringConfigFileEnabled).AsBool(true);
49+
if (applicationMonitoringConfigFileEnabled)
50+
{
51+
var debugEnabled = configBuilder.WithKeys(ConfigurationKeys.DebugEnabled).AsBool(false);
52+
var configsResult = LibDatadog.HandsOffConfiguration.ConfiguratorHelper.GetConfiguration(debugEnabled, handsOffLocalConfigPath, handsOffFleetConfigPath, isLibdatadogAvailable);
53+
if (configsResult is { ConfigurationSuccessResult: { } configsValue })
54+
{
55+
// fleet managed hands-off config
56+
configurationSource.Add(new HandsOffConfigurationSource(configsValue.ConfigEntriesFleet, false));
57+
// env vars
58+
configurationSource.Add(environmentSource);
59+
// local managed hands-off config
60+
configurationSource.Add(new HandsOffConfigurationSource(configsValue.ConfigEntriesLocal, true));
61+
}
62+
else
63+
{
64+
message = configsResult.ErrorMessage;
65+
exception = configsResult.Exception;
66+
resultType = configsResult.Result;
67+
// env vars only
68+
configurationSource.Add(environmentSource);
69+
}
70+
}
71+
else
72+
{
73+
resultType = Result.ApplicationMonitoringConfigFileDisabled;
74+
message = $"{nameof(ConfigurationKeys.ApplicationMonitoringConfigFileEnabled)} is disabled, not using hands-off configuration";
75+
// env vars only
76+
configurationSource.Add(environmentSource);
77+
}
3678

3779
#if NETFRAMEWORK
3880
// on .NET Framework only, also read from app.config/web.config
3981
configurationSource.Add(new NameValueConfigurationSource(System.Configuration.ConfigurationManager.AppSettings, ConfigurationOrigins.AppConfig));
4082
#endif
4183

84+
// datadog.json
4285
if (TryLoadJsonConfigurationFile(configurationSource, null, out var jsonConfigurationSource))
4386
{
4487
configurationSource.Add(jsonConfigurationSource);
4588
}
4689

47-
return configurationSource;
90+
return new(configurationSource, resultType, message, exception);
4891
}
4992

5093
internal static bool TryLoadJsonConfigurationFile(IConfigurationSource configurationSource, string? baseDirectory, [NotNullWhen(true)] out IConfigurationSource? jsonConfigurationSource)
@@ -81,9 +124,10 @@ internal static bool TryLoadJsonConfigurationFile(IConfigurationSource configura
81124
/// <summary>
82125
/// Used to refresh configuration source.
83126
/// </summary>
84-
internal static void Reload()
127+
/// <param name="isLibdatadogAvailable">whether libdatadog is available</param>
128+
internal static void Reload(bool isLibdatadogAvailable)
85129
{
86-
Instance = CreateDefaultConfigurationSource();
130+
CreationResult = CreateDefaultConfigurationSource(isLibdatadogAvailable: isLibdatadogAvailable);
87131
}
88132

89133
private static string GetCurrentDirectory()
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// <copyright file="GlobalConfigurationSourceResult.cs" company="Datadog">
2+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
4+
// </copyright>
5+
6+
#nullable enable
7+
8+
using System;
9+
using Datadog.Trace.LibDatadog.HandsOffConfiguration;
10+
11+
namespace Datadog.Trace.Configuration.ConfigurationSources;
12+
13+
internal readonly struct GlobalConfigurationSourceResult(CompositeConfigurationSource configurationSource, Result result, string? errorMessage = null, Exception? exception = null)
14+
{
15+
internal string? ErrorMessage { get; } = errorMessage;
16+
17+
public Exception? Exception { get; } = exception;
18+
19+
internal CompositeConfigurationSource ConfigurationSource { get; } = configurationSource;
20+
21+
internal Result Result { get; } = result;
22+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// <copyright file="HandsOffConfigurationSource.cs" company="Datadog">
2+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
4+
// </copyright>
5+
6+
#nullable enable
7+
using System;
8+
using System.Collections.Generic;
9+
using Datadog.Trace.Configuration.Telemetry;
10+
using Datadog.Trace.LibDatadog.HandsOffConfiguration;
11+
12+
namespace Datadog.Trace.Configuration.ConfigurationSources;
13+
14+
internal sealed class HandsOffConfigurationSource(Dictionary<string, string> configurations, bool localFile)
15+
: StringConfigurationSource
16+
{
17+
private readonly Dictionary<string, string> _configurations = configurations;
18+
private readonly bool _localFile = localFile;
19+
20+
internal override ConfigurationOrigins Origin => _localFile ? ConfigurationOrigins.LocalStableConfig : ConfigurationOrigins.FleetStableConfig;
21+
22+
protected override string? GetString(string key)
23+
{
24+
_configurations.TryGetValue(key, out var value);
25+
return value;
26+
}
27+
}

tracer/src/Datadog.Trace/Configuration/ConfigurationSources/Telemetry/ConfigurationOrigins.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,16 @@ public enum ConfigurationOrigins
6464
/// </summary>
6565
[Description("unknown")]
6666
Unknown,
67+
68+
/// <summary>
69+
/// Set when it comes from configurations defined in a user-managed file
70+
/// </summary>
71+
[Description("local_stable_config")]
72+
LocalStableConfig,
73+
74+
/// <summary>
75+
/// Set when it comes from configurations defined in a fleet-managed file
76+
/// </summary>
77+
[Description("fleet_stable_config")]
78+
FleetStableConfig
6779
}

tracer/src/Datadog.Trace/Configuration/GlobalSettings.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@
88
using System;
99
using Datadog.Trace.Configuration.ConfigurationSources.Telemetry;
1010
using Datadog.Trace.Configuration.Telemetry;
11+
using Datadog.Trace.LibDatadog;
1112
using Datadog.Trace.Logging;
1213
using Datadog.Trace.SourceGenerators;
1314
using Datadog.Trace.Telemetry;
1415
using Datadog.Trace.Telemetry.Metrics;
15-
using Datadog.Trace.Vendors.Serilog.Events;
16+
using LogEventLevel = Datadog.Trace.Vendors.Serilog.Events.LogEventLevel;
1617

1718
namespace Datadog.Trace.Configuration
1819
{
@@ -121,8 +122,13 @@ public static void Reload()
121122
{
122123
TelemetryFactory.Metrics.Record(PublicApiUsage.GlobalSettings_Reload);
123124
DatadogLogging.Reset();
124-
LibDatadog.Logger.Instance.SetLogLevel(debugEnabled: false);
125-
GlobalConfigurationSource.Reload();
125+
var isLibdatadogAvailable = LibDatadogAvailaibilityHelper.IsLibDatadogAvailable;
126+
if (isLibdatadogAvailable.IsAvailable)
127+
{
128+
LibDatadog.Logger.Instance.SetLogLevel(debugEnabled: false);
129+
}
130+
131+
GlobalConfigurationSource.Reload(isLibdatadogAvailable.IsAvailable);
126132
Instance = CreateFromDefaultSources();
127133
}
128134

tracer/src/Datadog.Trace/Configuration/TracerSettings.cs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using Datadog.Trace.Configuration.ConfigurationSources.Telemetry;
2222
using Datadog.Trace.Configuration.Telemetry;
2323
using Datadog.Trace.ContinuousProfiler;
24+
using Datadog.Trace.LibDatadog;
2425
using Datadog.Trace.Logging;
2526
using Datadog.Trace.Logging.DirectSubmission;
2627
using Datadog.Trace.Processors;
@@ -97,7 +98,7 @@ public TracerSettings(IConfigurationSource? source)
9798
/// <param name="telemetry">The telemetry collection instance. Typically you should create a new <see cref="ConfigurationTelemetry"/> </param>
9899
/// <param name="errorLog">Used to record cases where telemetry is overridden </param>
99100
internal TracerSettings(IConfigurationSource? source, IConfigurationTelemetry telemetry, OverrideErrorLog errorLog)
100-
: this(source, telemetry, errorLog, LibDatadog.NativeInterop.IsLibDatadogAvailable)
101+
: this(source, telemetry, errorLog, LibDatadogAvailaibilityHelper.IsLibDatadogAvailable)
101102
{
102103
}
103104

@@ -109,7 +110,7 @@ internal TracerSettings(IConfigurationSource? source, IConfigurationTelemetry te
109110
/// <param name="telemetry">The telemetry collection instance. Typically you should create a new <see cref="ConfigurationTelemetry"/> </param>
110111
/// <param name="errorLog">Used to record cases where telemetry is overridden </param>
111112
/// <param name="isLibDatadogAvailable">Used to check whether the libdatadog library is available. Useful for integration tests</param>
112-
internal TracerSettings(IConfigurationSource? source, IConfigurationTelemetry telemetry, OverrideErrorLog errorLog, bool isLibDatadogAvailable)
113+
internal TracerSettings(IConfigurationSource? source, IConfigurationTelemetry telemetry, OverrideErrorLog errorLog, LibDatadogAvailableResult isLibDatadogAvailable)
113114
{
114115
var commaSeparator = new[] { ',' };
115116
source ??= NullConfigurationSource.Instance;
@@ -435,11 +436,21 @@ internal TracerSettings(IConfigurationSource? source, IConfigurationTelemetry te
435436
_telemetry.Record(ConfigurationKeys.TraceDataPipelineEnabled, false, ConfigurationOrigins.Calculated);
436437
}
437438

438-
if (!isLibDatadogAvailable)
439+
if (!isLibDatadogAvailable.IsAvailable)
439440
{
440441
DataPipelineEnabled = false;
441-
Log.Warning(
442-
$"{ConfigurationKeys.TraceDataPipelineEnabled} is enabled, but libdatadog is not available. Disabling data pipeline.");
442+
if (isLibDatadogAvailable.Exception is not null)
443+
{
444+
Log.Warning(
445+
isLibDatadogAvailable.Exception,
446+
$"{ConfigurationKeys.TraceDataPipelineEnabled} is enabled, but libdatadog is not available. Disabling data pipeline.");
447+
}
448+
else
449+
{
450+
Log.Warning(
451+
$"{ConfigurationKeys.TraceDataPipelineEnabled} is enabled, but libdatadog is not available. Disabling data pipeline.");
452+
}
453+
443454
_telemetry.Record(ConfigurationKeys.TraceDataPipelineEnabled, false, ConfigurationOrigins.Calculated);
444455
}
445456
}
@@ -1607,9 +1618,9 @@ internal string GetDefaultHttpClientExclusions()
16071618
}
16081619

16091620
internal static TracerSettings Create(Dictionary<string, object?> settings)
1610-
=> Create(settings, LibDatadog.NativeInterop.IsLibDatadogAvailable);
1621+
=> Create(settings, LibDatadogAvailaibilityHelper.IsLibDatadogAvailable);
16111622

1612-
internal static TracerSettings Create(Dictionary<string, object?> settings, bool isLibDatadogAvailable)
1623+
internal static TracerSettings Create(Dictionary<string, object?> settings, LibDatadogAvailableResult isLibDatadogAvailable)
16131624
=> new(new DictionaryConfigurationSource(settings.ToDictionary(x => x.Key, x => x.Value?.ToString()!)), new ConfigurationTelemetry(), new(), isLibDatadogAvailable);
16141625

16151626
internal void CollectTelemetry(IConfigurationTelemetry destination)

0 commit comments

Comments
 (0)