< Summary

Information
Class: Chronicis.Api.Infrastructure.DatadogDiagnostics
Assembly: Chronicis.Api
File(s): /home/runner/work/chronicis/chronicis/src/Chronicis.Api/Infrastructure/DatadogDiagnostics.cs
Line coverage
100%
Covered lines: 31
Uncovered lines: 0
Coverable lines: 31
Total lines: 204
Line coverage: 100%
Branch coverage
100%
Covered branches: 2
Total branches: 2
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
LogTracerState(...)100%11100%
LogTracerState(...)100%11100%
ReadEnvironmentVariables()100%11100%
ReadTracerState()100%11100%
UriToStringOrNull(...)100%22100%

File(s)

/home/runner/work/chronicis/chronicis/src/Chronicis.Api/Infrastructure/DatadogDiagnostics.cs

#LineLine coverage
 1using Datadog.Trace;
 2
 3namespace Chronicis.Api.Infrastructure;
 4
 5/// <summary>
 6/// Read-only diagnostic utilities for Datadog APM tracing.
 7///
 8/// IMPORTANT: This class does NOT configure the tracer. All Datadog configuration
 9/// is driven exclusively by DD_* environment variables, which are read by the
 10/// Datadog.Trace automatic instrumentation before application code runs.
 11///
 12/// This class provides:
 13/// - Read-only access to environment variables
 14/// - Read-only access to Tracer.Instance.Settings
 15/// - Agent connectivity testing
 16///
 17/// It does NOT:
 18/// - Call Tracer.Configure()
 19/// - Mutate TracerSettings
 20/// - Apply fallback values or defaults
 21/// - Make assumptions about the runtime environment
 22/// </summary>
 23public static class DatadogDiagnostics
 24{
 25    /// <summary>
 26    /// Logs the current Datadog tracer state for verification.
 27    /// Read-only - does not configure anything.
 28    /// </summary>
 29    public static void LogTracerState(Serilog.ILogger logger)
 130        => LogTracerState(logger, ReadEnvironmentVariables, ReadTracerState);
 31
 32    /// <summary>
 33    /// Internal overload to support deterministic testing of success and failure paths.
 34    /// </summary>
 35    internal static void LogTracerState(
 36        Serilog.ILogger logger,
 37        Func<DatadogEnvVars> envVarsReader,
 38        Func<DatadogTracerState> tracerStateReader)
 39    {
 40        try
 41        {
 342            _ = envVarsReader();
 243            _ = tracerStateReader();
 244        }
 145        catch (Exception ex)
 46        {
 147            logger.Warning(ex, "Failed to read Datadog tracer state");
 148        }
 349    }
 50
 51    /// <summary>
 52    /// Reads current environment variables (DD_*) without interpretation or fallbacks.
 53    /// </summary>
 54    public static DatadogEnvVars ReadEnvironmentVariables()
 55    {
 256        return new DatadogEnvVars
 257        {
 258            DD_SERVICE = Environment.GetEnvironmentVariable("DD_SERVICE"),
 259            DD_ENV = Environment.GetEnvironmentVariable("DD_ENV"),
 260            DD_VERSION = Environment.GetEnvironmentVariable("DD_VERSION"),
 261            DD_AGENT_HOST = Environment.GetEnvironmentVariable("DD_AGENT_HOST"),
 262            DD_TRACE_AGENT_PORT = Environment.GetEnvironmentVariable("DD_TRACE_AGENT_PORT"),
 263            DD_LOGS_INJECTION = Environment.GetEnvironmentVariable("DD_LOGS_INJECTION"),
 264            DD_TRACE_ENABLED = Environment.GetEnvironmentVariable("DD_TRACE_ENABLED"),
 265            DD_TRACE_AGENT_URL = Environment.GetEnvironmentVariable("DD_TRACE_AGENT_URL"),
 266            ASPNETCORE_ENVIRONMENT = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")
 267        };
 68    }
 69
 70    /// <summary>
 71    /// Reads current tracer settings. Read-only snapshot of Tracer.Instance.Settings.
 72    /// </summary>
 73    public static DatadogTracerState ReadTracerState()
 74    {
 275        var settings = Tracer.Instance.Settings;
 76
 277        return new DatadogTracerState
 278        {
 279            ServiceName = settings.ServiceName,
 280            Environment = settings.Environment,
 281            ServiceVersion = settings.ServiceVersion,
 282            AgentUri = UriToStringOrNull(settings.AgentUri),
 283            LogsInjectionEnabled = settings.LogsInjectionEnabled,
 284            TraceEnabled = settings.TraceEnabled
 285        };
 86    }
 87
 88    internal static string? UriToStringOrNull(Uri? uri)
 489        => uri is null ? null : uri.ToString();
 90
 91    /// <summary>
 92    /// Attempts to reach a Datadog agent endpoint and returns connectivity status.
 93    /// </summary>
 94    public static async Task<AgentConnectivityResult> CheckAgentConnectivityAsync(
 95        HttpClient httpClient,
 96        string agentBaseUrl)
 97    {
 98        if (string.IsNullOrEmpty(agentBaseUrl))
 99        {
 100            return new AgentConnectivityResult
 101            {
 102                Url = "(not provided)",
 103                Status = "skipped",
 104                Message = "No URL provided"
 105            };
 106        }
 107
 108        var infoUrl = $"{agentBaseUrl.TrimEnd('/')}/info";
 109
 110        try
 111        {
 112            using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
 113            var response = await httpClient.GetAsync(infoUrl, cts.Token);
 114
 115            if (response.IsSuccessStatusCode)
 116            {
 117                var content = await response.Content.ReadAsStringAsync(cts.Token);
 118                return new AgentConnectivityResult
 119                {
 120                    Url = infoUrl,
 121                    Status = "reachable",
 122                    StatusCode = (int)response.StatusCode,
 123                    AgentInfo = content
 124                };
 125            }
 126
 127            return new AgentConnectivityResult
 128            {
 129                Url = infoUrl,
 130                Status = "http_error",
 131                StatusCode = (int)response.StatusCode,
 132                Message = $"HTTP {(int)response.StatusCode} {response.ReasonPhrase}"
 133            };
 134        }
 135        catch (HttpRequestException ex)
 136        {
 137            return new AgentConnectivityResult
 138            {
 139                Url = infoUrl,
 140                Status = "unreachable",
 141                Message = ex.Message
 142            };
 143        }
 144        catch (TaskCanceledException)
 145        {
 146            return new AgentConnectivityResult
 147            {
 148                Url = infoUrl,
 149                Status = "timeout",
 150                Message = "Connection timed out after 5 seconds"
 151            };
 152        }
 153        catch (Exception ex)
 154        {
 155            return new AgentConnectivityResult
 156            {
 157                Url = infoUrl,
 158                Status = "error",
 159                Message = ex.Message
 160            };
 161        }
 162    }
 163}
 164
 165/// <summary>
 166/// Raw environment variable values. No interpretation, no fallbacks.
 167/// </summary>
 168public record DatadogEnvVars
 169{
 170    public string? DD_SERVICE { get; init; }
 171    public string? DD_ENV { get; init; }
 172    public string? DD_VERSION { get; init; }
 173    public string? DD_AGENT_HOST { get; init; }
 174    public string? DD_TRACE_AGENT_PORT { get; init; }
 175    public string? DD_TRACE_AGENT_URL { get; init; }
 176    public string? DD_LOGS_INJECTION { get; init; }
 177    public string? DD_TRACE_ENABLED { get; init; }
 178    public string? ASPNETCORE_ENVIRONMENT { get; init; }
 179}
 180
 181/// <summary>
 182/// Read-only snapshot of Tracer.Instance.Settings.
 183/// </summary>
 184public record DatadogTracerState
 185{
 186    public string? ServiceName { get; init; }
 187    public string? Environment { get; init; }
 188    public string? ServiceVersion { get; init; }
 189    public string? AgentUri { get; init; }
 190    public bool LogsInjectionEnabled { get; init; }
 191    public bool TraceEnabled { get; init; }
 192}
 193
 194/// <summary>
 195/// Result of an agent connectivity check.
 196/// </summary>
 197public record AgentConnectivityResult
 198{
 199    public required string Url { get; init; }
 200    public required string Status { get; init; }
 201    public int? StatusCode { get; init; }
 202    public string? Message { get; init; }
 203    public string? AgentInfo { get; init; }
 204}