Skip to content

Commit 2e88dba

Browse files
committed
Implement IEquatable<MutableSettings>
1 parent 34cd0c5 commit 2e88dba

File tree

4 files changed

+187
-4
lines changed

4 files changed

+187
-4
lines changed

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

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#nullable enable
77

8+
using System;
89
using System.Collections.Generic;
910
using Datadog.Trace.Configuration.Telemetry;
1011

@@ -13,7 +14,7 @@ namespace Datadog.Trace.Configuration
1314
/// <summary>
1415
/// Contains integration-specific settings.
1516
/// </summary>
16-
public class IntegrationSettings
17+
public class IntegrationSettings : IEquatable<IntegrationSettings>
1718
{
1819
/// <summary>
1920
/// Initializes a new instance of the <see cref="IntegrationSettings"/> class.
@@ -82,5 +83,51 @@ internal IntegrationSettings(string integrationName, IConfigurationSource? sourc
8283
/// that determines the sampling rate for this integration.
8384
/// </summary>
8485
public double AnalyticsSampleRate { get; }
86+
87+
/// <inheritdoc/>
88+
public bool Equals(IntegrationSettings? other)
89+
{
90+
if (other is null)
91+
{
92+
return false;
93+
}
94+
95+
if (ReferenceEquals(this, other))
96+
{
97+
return true;
98+
}
99+
100+
return IntegrationName == other.IntegrationName &&
101+
Enabled == other.Enabled &&
102+
AnalyticsEnabled == other.AnalyticsEnabled &&
103+
AnalyticsSampleRate.Equals(other.AnalyticsSampleRate);
104+
}
105+
106+
/// <inheritdoc/>
107+
public override bool Equals(object? obj)
108+
{
109+
if (obj is null)
110+
{
111+
return false;
112+
}
113+
114+
if (ReferenceEquals(this, obj))
115+
{
116+
return true;
117+
}
118+
119+
if (obj.GetType() != GetType())
120+
{
121+
return false;
122+
}
123+
124+
return Equals((IntegrationSettings)obj);
125+
}
126+
127+
/// <inheritdoc/>
128+
public override int GetHashCode()
129+
{
130+
return HashCode.Combine(IntegrationName, Enabled, AnalyticsEnabled, AnalyticsSampleRate);
131+
}
85132
}
86133
}

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

Lines changed: 132 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ namespace Datadog.Trace.Configuration;
2727
/// code or via remote configuration. Note that the specific instance is immutable, but there may be a
2828
/// new version in the lifetime of the application
2929
/// </summary>
30-
public class MutableSettings
30+
internal sealed class MutableSettings : IEquatable<MutableSettings>
3131
{
3232
// we cached the static instance here, because is being used in the hotpath
3333
// by IsIntegrationEnabled method (called from all integrations)
@@ -368,6 +368,137 @@ void TrySetValue(int index)
368368
return httpErrorCodesArray;
369369
}
370370

371+
public bool Equals(MutableSettings? other)
372+
{
373+
if (other is null)
374+
{
375+
return false;
376+
}
377+
378+
if (ReferenceEquals(this, other))
379+
{
380+
return true;
381+
}
382+
383+
return TraceEnabled == other.TraceEnabled &&
384+
CustomSamplingRules == other.CustomSamplingRules &&
385+
CustomSamplingRulesIsRemote == other.CustomSamplingRulesIsRemote &&
386+
Nullable.Equals(GlobalSamplingRate, other.GlobalSamplingRate) &&
387+
LogsInjectionEnabled == other.LogsInjectionEnabled &&
388+
StartupDiagnosticLogEnabled == other.StartupDiagnosticLogEnabled &&
389+
Environment == other.Environment &&
390+
ServiceName == other.ServiceName &&
391+
ServiceVersion == other.ServiceVersion &&
392+
TracerMetricsEnabled == other.TracerMetricsEnabled &&
393+
#pragma warning disable 618 // App analytics is deprecated, but still used
394+
AnalyticsEnabled == other.AnalyticsEnabled &&
395+
#pragma warning restore 618
396+
MaxTracesSubmittedPerSecond == other.MaxTracesSubmittedPerSecond &&
397+
KafkaCreateConsumerScopeEnabled == other.KafkaCreateConsumerScopeEnabled &&
398+
GitRepositoryUrl == other.GitRepositoryUrl &&
399+
GitCommitSha == other.GitCommitSha &&
400+
// Do collection comparisons at the end, as generally more expensive
401+
AreEqual(GlobalTags, other.GlobalTags) &&
402+
AreEqual(HeaderTags, other.HeaderTags) &&
403+
AreEqual(GrpcTags, other.GrpcTags) &&
404+
AreEqual(ServiceNameMappings, other.ServiceNameMappings) &&
405+
DisabledIntegrationNames.SetEquals(other.DisabledIntegrationNames) &&
406+
// Could unroll the Linq, but prob not worth the hassle
407+
HttpServerErrorStatusCodes.SequenceEqual(other.HttpServerErrorStatusCodes) &&
408+
HttpClientErrorStatusCodes.SequenceEqual(other.HttpClientErrorStatusCodes) &&
409+
// Most expensive one
410+
AreEqualIntegrations(Integrations, other.Integrations);
411+
412+
static bool AreEqual(ReadOnlyDictionary<string, string>? dictionary1, ReadOnlyDictionary<string, string>? dictionary2)
413+
{
414+
if (dictionary1 == null || dictionary2 == null)
415+
{
416+
return ReferenceEquals(dictionary1, dictionary2);
417+
}
418+
419+
if (dictionary1.Count != dictionary2.Count)
420+
{
421+
return false;
422+
}
423+
424+
foreach (var pair in dictionary1)
425+
{
426+
if (dictionary2.TryGetValue(pair.Key, out var value))
427+
{
428+
if (!string.Equals(value, pair.Value))
429+
{
430+
return false;
431+
}
432+
}
433+
else
434+
{
435+
return false;
436+
}
437+
}
438+
439+
return true;
440+
}
441+
442+
static bool AreEqualIntegrations(IntegrationSettingsCollection integrations1, IntegrationSettingsCollection integrations2)
443+
{
444+
if (integrations1.Settings.Length != integrations2.Settings.Length)
445+
{
446+
return false;
447+
}
448+
449+
// They should be the exact same settings in both cases
450+
for (var i = 0; i < integrations1.Settings.Length; i++)
451+
{
452+
var integration1 = integrations1.Settings[i];
453+
var integration2 = integrations2.Settings[i];
454+
455+
if (!integration1.Equals(integration2))
456+
{
457+
return false;
458+
}
459+
}
460+
461+
return true;
462+
}
463+
}
464+
465+
public override bool Equals(object? obj)
466+
{
467+
return ReferenceEquals(this, obj) || (obj is MutableSettings other && Equals(other));
468+
}
469+
470+
public override int GetHashCode()
471+
{
472+
// we can't easily include the collections in the hash code
473+
var hashCode = new HashCode();
474+
hashCode.Add(TraceEnabled);
475+
hashCode.Add(CustomSamplingRules);
476+
hashCode.Add(CustomSamplingRulesIsRemote);
477+
hashCode.Add(GlobalSamplingRate);
478+
hashCode.Add(LogsInjectionEnabled);
479+
// hashCode.Add(GlobalTags);
480+
// hashCode.Add(HeaderTags);
481+
hashCode.Add(StartupDiagnosticLogEnabled);
482+
hashCode.Add(Environment);
483+
hashCode.Add(ServiceName);
484+
hashCode.Add(ServiceVersion);
485+
// hashCode.Add(DisabledIntegrationNames);
486+
// hashCode.Add(GrpcTags);
487+
hashCode.Add(TracerMetricsEnabled);
488+
// hashCode.Add(Integrations);
489+
#pragma warning disable 618 // App analytics is deprecated, but still used
490+
hashCode.Add(AnalyticsEnabled);
491+
#pragma warning restore 618
492+
hashCode.Add(MaxTracesSubmittedPerSecond);
493+
hashCode.Add(KafkaCreateConsumerScopeEnabled);
494+
// hashCode.Add(HttpServerErrorStatusCodes);
495+
// hashCode.Add(HttpClientErrorStatusCodes);
496+
// hashCode.Add(ServiceNameMappings);
497+
hashCode.Add(GitRepositoryUrl);
498+
hashCode.Add(GitCommitSha);
499+
return hashCode.ToHashCode();
500+
}
501+
371502
internal bool IsErrorStatusCode(int statusCode, bool serverStatusCode)
372503
{
373504
var source = serverStatusCode ? HttpServerErrorStatusCodes : HttpClientErrorStatusCodes;

tracer/test/Datadog.Trace.Tests/Configuration/IntegrationSettingsCollectionTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ public void ReturnsDefaultSettingsForUnknownIntegration()
3636
instance1.Enabled.Should().BeNull();
3737

3838
var instance2 = settings[integrationName];
39-
instance2.Should().NotBe(instance1);
39+
instance2.Should().NotBeSameAs(instance1);
40+
instance2.Should().Be(instance1); // works because of value-comparison IEquatable implementation
4041
instance2.IntegrationName.Should().Be(integrationName);
4142
instance2.Enabled.Should().BeNull();
4243
}

tracer/test/Datadog.Trace.Tests/ManualInstrumentation/SettingsInstrumentationTests.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,11 @@ public void AutomaticToManual_ImmutableSettingsAreTransferredCorrectly()
236236
manual.GlobalSamplingRate.Should().Be(automatic.GlobalSamplingRate);
237237
manual.GlobalTags.Should().BeEquivalentTo(automatic.GlobalTags);
238238
manual.HeaderTags.Should().BeEquivalentTo(automatic.HeaderTags);
239-
manual.Integrations.Settings.Should().BeEquivalentTo(automatic.Integrations.Settings.ToDictionary(x => x.IntegrationName, x => x));
239+
// force fluent assertions to just compare the properties, not use the `Equals` implementation
240+
manual.Integrations.Settings.Should()
241+
.BeEquivalentTo(
242+
automatic.Integrations.Settings.ToDictionary(x => x.IntegrationName, x => x),
243+
options => options.ComparingByMembers(typeof(IntegrationSettings)));
240244
manual.KafkaCreateConsumerScopeEnabled.Should().Be(automatic.KafkaCreateConsumerScopeEnabled);
241245
manual.LogsInjectionEnabled.Should().Be(automatic.LogsInjectionEnabled);
242246
manual.MaxTracesSubmittedPerSecond.Should().Be(automatic.MaxTracesSubmittedPerSecond);

0 commit comments

Comments
 (0)