From ea4d7104ee3ee41c0a6b7ba2820e327e0f211d22 Mon Sep 17 00:00:00 2001 From: "Calvin A. Allen" Date: Mon, 29 Dec 2025 20:59:47 -0500 Subject: [PATCH] feature: add fluentbuilder for configuration and custom debug exporters --- .../CodingWithCalvin.Otel4Vsix.csproj | 4 +- .../Exporters/DebugActivityExporter.cs | 53 ++++ .../Exporters/DebugLoggerProvider.cs | 75 +++++ .../Exporters/DebugMetricExporter.cs | 77 +++++ .../TelemetryBuilder.cs | 289 ++++++++++++++++++ .../TelemetryConfiguration.cs | 18 ++ .../TelemetryMode.cs | 30 ++ .../VsixTelemetry.cs | 90 ++++-- 8 files changed, 617 insertions(+), 19 deletions(-) create mode 100644 src/CodingWithCalvin.Otel4Vsix/Exporters/DebugActivityExporter.cs create mode 100644 src/CodingWithCalvin.Otel4Vsix/Exporters/DebugLoggerProvider.cs create mode 100644 src/CodingWithCalvin.Otel4Vsix/Exporters/DebugMetricExporter.cs create mode 100644 src/CodingWithCalvin.Otel4Vsix/TelemetryBuilder.cs create mode 100644 src/CodingWithCalvin.Otel4Vsix/TelemetryMode.cs diff --git a/src/CodingWithCalvin.Otel4Vsix/CodingWithCalvin.Otel4Vsix.csproj b/src/CodingWithCalvin.Otel4Vsix/CodingWithCalvin.Otel4Vsix.csproj index ccdf83b..dc31886 100644 --- a/src/CodingWithCalvin.Otel4Vsix/CodingWithCalvin.Otel4Vsix.csproj +++ b/src/CodingWithCalvin.Otel4Vsix/CodingWithCalvin.Otel4Vsix.csproj @@ -9,7 +9,7 @@ false CodingWithCalvin.Otel4Vsix - 0.0.15-local + 0.0.1 Calvin A. Allen Coding With Calvin CodingWithCalvin.Otel4Vsix @@ -40,10 +40,10 @@ - + diff --git a/src/CodingWithCalvin.Otel4Vsix/Exporters/DebugActivityExporter.cs b/src/CodingWithCalvin.Otel4Vsix/Exporters/DebugActivityExporter.cs new file mode 100644 index 0000000..9b81d79 --- /dev/null +++ b/src/CodingWithCalvin.Otel4Vsix/Exporters/DebugActivityExporter.cs @@ -0,0 +1,53 @@ +using System.Diagnostics; +using System.Text; +using OpenTelemetry; + +namespace CodingWithCalvin.Otel4Vsix.Exporters; + +/// +/// An activity exporter that writes trace data to . +/// Output appears in the Visual Studio Output window when debugging. +/// +internal sealed class DebugActivityExporter : BaseExporter +{ + private readonly string _prefix; + + public DebugActivityExporter(string serviceName, string serviceVersion) + { + _prefix = $"[{serviceName} v{serviceVersion}]"; + } + + /// + public override ExportResult Export(in Batch batch) + { + foreach (var activity in batch) + { + var sb = new StringBuilder(); + sb.AppendLine($"{_prefix} [TRACE] {activity.DisplayName}"); + sb.AppendLine($" TraceId: {activity.TraceId}"); + sb.AppendLine($" SpanId: {activity.SpanId}"); + sb.AppendLine($" Duration: {activity.Duration.TotalMilliseconds:F2}ms"); + sb.AppendLine($" Status: {activity.Status}"); + + if (activity.Tags != null) + { + foreach (var tag in activity.Tags) + { + sb.AppendLine($" {tag.Key}: {tag.Value}"); + } + } + + if (activity.Events != null) + { + foreach (var evt in activity.Events) + { + sb.AppendLine($" Event: {evt.Name} at {evt.Timestamp}"); + } + } + + System.Diagnostics.Trace.WriteLine(sb.ToString()); + } + + return ExportResult.Success; + } +} diff --git a/src/CodingWithCalvin.Otel4Vsix/Exporters/DebugLoggerProvider.cs b/src/CodingWithCalvin.Otel4Vsix/Exporters/DebugLoggerProvider.cs new file mode 100644 index 0000000..aeac2b3 --- /dev/null +++ b/src/CodingWithCalvin.Otel4Vsix/Exporters/DebugLoggerProvider.cs @@ -0,0 +1,75 @@ +using System; +using Microsoft.Extensions.Logging; + +namespace CodingWithCalvin.Otel4Vsix.Exporters; + +/// +/// A logger provider that writes to . +/// +internal sealed class DebugLoggerProvider : ILoggerProvider +{ + private readonly string _serviceName; + private readonly string _serviceVersion; + + public DebugLoggerProvider(string serviceName, string serviceVersion) + { + _serviceName = serviceName; + _serviceVersion = serviceVersion; + } + + public ILogger CreateLogger(string categoryName) => new DebugLogger(_serviceName, _serviceVersion, categoryName); + + public void Dispose() { } +} + +/// +/// A logger that writes to . +/// +internal sealed class DebugLogger : ILogger +{ + private readonly string _prefix; + private readonly string _categoryName; + + public DebugLogger(string serviceName, string serviceVersion, string categoryName) + { + _prefix = $"[{serviceName} v{serviceVersion}]"; + _categoryName = categoryName; + } + + public IDisposable BeginScope(TState state) => NullScope.Instance; + + public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None; + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + if (!IsEnabled(logLevel)) + { + return; + } + + var message = formatter(state, exception); + var levelStr = logLevel switch + { + LogLevel.Trace => "TRACE", + LogLevel.Debug => "DEBUG", + LogLevel.Information => "INFO", + LogLevel.Warning => "WARN", + LogLevel.Error => "ERROR", + LogLevel.Critical => "CRIT", + _ => "NONE" + }; + + System.Diagnostics.Trace.WriteLine($"{_prefix} [{levelStr}] {_categoryName}: {message}"); + + if (exception != null) + { + System.Diagnostics.Trace.WriteLine($" Exception: {exception}"); + } + } + + private sealed class NullScope : IDisposable + { + public static readonly NullScope Instance = new NullScope(); + public void Dispose() { } + } +} diff --git a/src/CodingWithCalvin.Otel4Vsix/Exporters/DebugMetricExporter.cs b/src/CodingWithCalvin.Otel4Vsix/Exporters/DebugMetricExporter.cs new file mode 100644 index 0000000..ce7aa72 --- /dev/null +++ b/src/CodingWithCalvin.Otel4Vsix/Exporters/DebugMetricExporter.cs @@ -0,0 +1,77 @@ +using System.Text; +using OpenTelemetry; +using OpenTelemetry.Metrics; + +namespace CodingWithCalvin.Otel4Vsix.Exporters; + +/// +/// A metric exporter that writes metric data to . +/// Output appears in the Visual Studio Output window when debugging. +/// +internal sealed class DebugMetricExporter : BaseExporter +{ + private readonly string _prefix; + + public DebugMetricExporter(string serviceName, string serviceVersion) + { + _prefix = $"[{serviceName} v{serviceVersion}]"; + } + + /// + public override ExportResult Export(in Batch batch) + { + foreach (var metric in batch) + { + var sb = new StringBuilder(); + sb.AppendLine($"{_prefix} [METRIC] {metric.Name}"); + + if (!string.IsNullOrEmpty(metric.Unit)) + { + sb.AppendLine($" Unit: {metric.Unit}"); + } + + if (!string.IsNullOrEmpty(metric.Description)) + { + sb.AppendLine($" Description: {metric.Description}"); + } + + foreach (var metricPoint in metric.GetMetricPoints()) + { + sb.Append(" "); + + switch (metric.MetricType) + { + case MetricType.LongSum: + case MetricType.LongSumNonMonotonic: + sb.AppendLine($"Value: {metricPoint.GetSumLong()}"); + break; + case MetricType.DoubleSum: + case MetricType.DoubleSumNonMonotonic: + sb.AppendLine($"Value: {metricPoint.GetSumDouble()}"); + break; + case MetricType.LongGauge: + sb.AppendLine($"Value: {metricPoint.GetGaugeLastValueLong()}"); + break; + case MetricType.DoubleGauge: + sb.AppendLine($"Value: {metricPoint.GetGaugeLastValueDouble()}"); + break; + case MetricType.Histogram: + sb.AppendLine($"Count: {metricPoint.GetHistogramCount()}, Sum: {metricPoint.GetHistogramSum()}"); + break; + default: + sb.AppendLine($"Type: {metric.MetricType}"); + break; + } + + foreach (var tag in metricPoint.Tags) + { + sb.AppendLine($" {tag.Key}: {tag.Value}"); + } + } + + System.Diagnostics.Trace.WriteLine(sb.ToString()); + } + + return ExportResult.Success; + } +} diff --git a/src/CodingWithCalvin.Otel4Vsix/TelemetryBuilder.cs b/src/CodingWithCalvin.Otel4Vsix/TelemetryBuilder.cs new file mode 100644 index 0000000..e343127 --- /dev/null +++ b/src/CodingWithCalvin.Otel4Vsix/TelemetryBuilder.cs @@ -0,0 +1,289 @@ +using System; +using System.Runtime.InteropServices; +using EnvDTE; +using EnvDTE80; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace CodingWithCalvin.Otel4Vsix; + +/// +/// Fluent builder for configuring and initializing OpenTelemetry in Visual Studio extensions. +/// +public sealed class TelemetryBuilder +{ + private readonly TelemetryConfiguration _configuration = new TelemetryConfiguration(); + + /// + /// Sets the service name used to identify this extension in telemetry. + /// + /// The service name. + /// The builder for chaining. + public TelemetryBuilder WithServiceName(string serviceName) + { + _configuration.ServiceName = serviceName; + return this; + } + + /// + /// Sets the service version. + /// + /// The service version. + /// The builder for chaining. + public TelemetryBuilder WithServiceVersion(string serviceVersion) + { + _configuration.ServiceVersion = serviceVersion; + return this; + } + + /// + /// Configures OTLP export using HTTP/protobuf protocol. + /// + /// The OTLP HTTP endpoint (e.g., "https://api.honeycomb.io"). + /// The builder for chaining. + public TelemetryBuilder WithOtlpHttp(string endpoint) + { + _configuration.OtlpEndpoint = endpoint; + _configuration.UseOtlpHttp = true; + return this; + } + + /// + /// Configures OTLP export using gRPC protocol. + /// + /// The OTLP gRPC endpoint (e.g., "http://localhost:4317"). + /// The builder for chaining. + public TelemetryBuilder WithOtlpGrpc(string endpoint) + { + _configuration.OtlpEndpoint = endpoint; + _configuration.UseOtlpHttp = false; + return this; + } + + /// + /// Adds a header to OTLP export requests. + /// + /// The header name. + /// The header value. + /// The builder for chaining. + public TelemetryBuilder WithHeader(string key, string value) + { + _configuration.OtlpHeaders[key] = value; + return this; + } + + /// + /// Adds a resource attribute to all telemetry. + /// + /// The attribute name. + /// The attribute value. + /// The builder for chaining. + public TelemetryBuilder WithResourceAttribute(string key, object value) + { + _configuration.ResourceAttributes[key] = value; + return this; + } + + /// + /// Adds Visual Studio version and edition as resource attributes by querying VS services. + /// + /// The async service provider (typically the AsyncPackage). + /// The builder for chaining. + /// + /// Adds the following resource attributes: + /// + /// vs.version - The full Visual Studio version (e.g., "17.12.35521.163") + /// vs.edition - The Visual Studio edition (e.g., "Community", "Professional", "Enterprise") + /// + /// This method queries DTE for the edition and IVsShell for the full release version. + /// + public TelemetryBuilder WithVisualStudioAttributes(IAsyncServiceProvider serviceProvider) + { + ThreadHelper.JoinableTaskFactory.Run(async () => + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + + var vsEdition = "unknown"; + var vsVersion = "unknown"; + + var dte = await serviceProvider.GetServiceAsync(typeof(DTE)) as DTE2; + if (dte != null) + { + vsEdition = dte.Edition ?? "unknown"; + } + + var shell = await serviceProvider.GetServiceAsync(typeof(SVsShell)) as IVsShell; + if (shell != null && shell.GetProperty((int)__VSSPROPID5.VSSPROPID_ReleaseVersion, out var versionObj) == 0) + { + vsVersion = versionObj as string ?? "unknown"; + } + + _configuration.ResourceAttributes["vs.version"] = vsVersion; + _configuration.ResourceAttributes["vs.edition"] = vsEdition; + }); + + return this; + } + + /// + /// Adds Visual Studio version and edition as resource attributes. + /// + /// The Visual Studio version (e.g., "17.12"). + /// The Visual Studio edition (e.g., "Community", "Professional", "Enterprise"). + /// The builder for chaining. + /// + /// Adds the following resource attributes: + /// + /// vs.version - The Visual Studio version + /// vs.edition - The Visual Studio edition + /// + /// + public TelemetryBuilder WithVisualStudioAttributes(string version, string edition) + { + _configuration.ResourceAttributes["vs.version"] = version ?? "unknown"; + _configuration.ResourceAttributes["vs.edition"] = edition ?? "unknown"; + return this; + } + + /// + /// Adds environment attributes for OS and architecture. + /// + /// The builder for chaining. + /// + /// Adds the following resource attributes: + /// + /// os.version - The operating system version + /// host.arch - The processor architecture (e.g., "X64", "Arm64") + /// + /// + public TelemetryBuilder WithEnvironmentAttributes() + { + _configuration.ResourceAttributes["os.version"] = Environment.OSVersion.Version.ToString(); + _configuration.ResourceAttributes["host.arch"] = RuntimeInformation.ProcessArchitecture.ToString(); + return this; + } + + /// + /// Sets the telemetry mode. + /// + /// The telemetry mode. Defaults to . + /// The builder for chaining. + public TelemetryBuilder WithMode(TelemetryMode mode) + { + _configuration.Mode = mode; + return this; + } + + /// + /// Enables or disables tracing. + /// + /// True to enable tracing, false to disable. + /// The builder for chaining. + public TelemetryBuilder WithTracing(bool enabled = true) + { + _configuration.EnableTracing = enabled; + return this; + } + + /// + /// Enables or disables metrics collection. + /// + /// True to enable metrics, false to disable. + /// The builder for chaining. + public TelemetryBuilder WithMetrics(bool enabled = true) + { + _configuration.EnableMetrics = enabled; + return this; + } + + /// + /// Enables or disables logging. + /// + /// True to enable logging, false to disable. + /// The builder for chaining. + public TelemetryBuilder WithLogging(bool enabled = true) + { + _configuration.EnableLogging = enabled; + return this; + } + + /// + /// Sets the trace sampling ratio. + /// + /// The sampling ratio (0.0 to 1.0). 1.0 samples all traces. + /// The builder for chaining. + public TelemetryBuilder WithTraceSamplingRatio(double ratio) + { + _configuration.TraceSamplingRatio = ratio; + return this; + } + + /// + /// Enables or disables the global exception handler. + /// + /// True to enable, false to disable. + /// The builder for chaining. + public TelemetryBuilder WithGlobalExceptionHandler(bool enabled = true) + { + _configuration.EnableGlobalExceptionHandler = enabled; + return this; + } + + /// + /// Sets an exception filter function. + /// + /// Function that returns true to track the exception, false to ignore. + /// The builder for chaining. + public TelemetryBuilder WithExceptionFilter(Func filter) + { + _configuration.ExceptionFilter = filter; + return this; + } + + /// + /// Enables or disables Visual Studio context in telemetry. + /// + /// True to include VS context, false to exclude. + /// The builder for chaining. + public TelemetryBuilder WithVisualStudioContext(bool enabled = true) + { + _configuration.IncludeVisualStudioContext = enabled; + return this; + } + + /// + /// Sets the export timeout. + /// + /// The timeout in milliseconds. + /// The builder for chaining. + public TelemetryBuilder WithExportTimeout(int timeoutMilliseconds) + { + _configuration.ExportTimeoutMilliseconds = timeoutMilliseconds; + return this; + } + + /// + /// Builds the configuration and initializes telemetry. + /// + /// + /// When is set (the default), the effective mode is + /// determined by inspecting the configuration: + /// + /// If an OTLP endpoint is configured, telemetry is exported via OTLP. + /// Otherwise, telemetry is written to the debug output window. + /// + /// + public void Initialize() + { + VsixTelemetry.Initialize(_configuration); + } + + /// + /// Builds and returns the configuration without initializing. + /// + /// The built configuration. + public TelemetryConfiguration Build() + { + return _configuration; + } +} diff --git a/src/CodingWithCalvin.Otel4Vsix/TelemetryConfiguration.cs b/src/CodingWithCalvin.Otel4Vsix/TelemetryConfiguration.cs index 55cd19c..f15339a 100644 --- a/src/CodingWithCalvin.Otel4Vsix/TelemetryConfiguration.cs +++ b/src/CodingWithCalvin.Otel4Vsix/TelemetryConfiguration.cs @@ -8,6 +8,24 @@ namespace CodingWithCalvin.Otel4Vsix; /// public sealed class TelemetryConfiguration { + /// + /// Gets or sets the telemetry mode. + /// + /// + /// + /// When set to (the default), the library automatically + /// determines the mode based on configuration state: + /// + /// + /// If an OTLP endpoint is configured, uses . + /// Otherwise, uses . + /// + /// + /// This allows consumers to conditionally set the endpoint for different build configurations. + /// + /// + public TelemetryMode Mode { get; set; } = TelemetryMode.Auto; + /// /// Gets or sets the service name used to identify this extension in telemetry. /// diff --git a/src/CodingWithCalvin.Otel4Vsix/TelemetryMode.cs b/src/CodingWithCalvin.Otel4Vsix/TelemetryMode.cs new file mode 100644 index 0000000..c527277 --- /dev/null +++ b/src/CodingWithCalvin.Otel4Vsix/TelemetryMode.cs @@ -0,0 +1,30 @@ +namespace CodingWithCalvin.Otel4Vsix; + +/// +/// Specifies the telemetry export mode. +/// +public enum TelemetryMode +{ + /// + /// Automatically determines the mode based on configuration state. + /// If an OTLP endpoint is configured, uses . + /// Otherwise, uses . + /// + Auto, + + /// + /// Telemetry is completely disabled. No data is collected or exported. + /// + Disabled, + + /// + /// Telemetry is written to the debug output window via . + /// Useful for local development in Visual Studio. + /// + Debug, + + /// + /// Telemetry is exported via OTLP to the configured endpoint. + /// + Otlp +} diff --git a/src/CodingWithCalvin.Otel4Vsix/VsixTelemetry.cs b/src/CodingWithCalvin.Otel4Vsix/VsixTelemetry.cs index 7baaaf9..4e78866 100644 --- a/src/CodingWithCalvin.Otel4Vsix/VsixTelemetry.cs +++ b/src/CodingWithCalvin.Otel4Vsix/VsixTelemetry.cs @@ -4,6 +4,7 @@ using System.Diagnostics.Metrics; using System.Linq; using CodingWithCalvin.Otel4Vsix.Exceptions; +using CodingWithCalvin.Otel4Vsix.Exporters; using CodingWithCalvin.Otel4Vsix.Logging; using CodingWithCalvin.Otel4Vsix.Tracing; using Microsoft.Extensions.Logging; @@ -29,6 +30,7 @@ public static class VsixTelemetry private static readonly object _lock = new object(); private static volatile bool _isInitialized; private static TelemetryConfiguration _configuration; + private static TelemetryMode _effectiveMode; private static ActivitySourceProvider _activitySourceProvider; private static Metrics.MetricsProvider _metricsProvider; private static Logging.LoggerProvider _loggerProvider; @@ -42,6 +44,27 @@ public static class VsixTelemetry /// public static bool IsInitialized => _isInitialized; + /// + /// Gets the effective telemetry mode after Auto resolution. + /// + public static TelemetryMode EffectiveMode => _effectiveMode; + + /// + /// Creates a new for fluent configuration. + /// + /// A new builder instance. + /// + /// + /// VsixTelemetry.Configure() + /// .WithServiceName("MyExtension") + /// .WithServiceVersion("1.0.0") + /// .WithOtlpHttp("https://api.honeycomb.io") + /// .WithHeader("x-honeycomb-team", apiKey) + /// .Initialize(); + /// + /// + public static TelemetryBuilder Configure() => new TelemetryBuilder(); + /// /// Gets the for creating traces. /// @@ -116,9 +139,38 @@ public static void Initialize(TelemetryConfiguration configuration) } _configuration = configuration; + _effectiveMode = ResolveEffectiveMode(configuration); + + // If disabled, mark as initialized but don't set up providers + if (_effectiveMode == TelemetryMode.Disabled) + { + _isInitialized = true; + return; + } + InitializeProviders(); _isInitialized = true; + + // Log initialization in Debug mode + if (_effectiveMode == TelemetryMode.Debug) + { + System.Diagnostics.Trace.WriteLine( + $"[{_configuration.ServiceName} v{_configuration.ServiceVersion}] Telemetry initialized (Mode: {_effectiveMode})"); + } + } + } + + private static TelemetryMode ResolveEffectiveMode(TelemetryConfiguration configuration) + { + if (configuration.Mode != TelemetryMode.Auto) + { + return configuration.Mode; } + + // Auto mode: if endpoint is configured, use Otlp; otherwise Debug + return !string.IsNullOrWhiteSpace(configuration.OtlpEndpoint) + ? TelemetryMode.Otlp + : TelemetryMode.Debug; } /// @@ -469,8 +521,8 @@ private static TracerProvider BuildTracerProvider(ResourceBuilder resourceBuilde .AddSource(_configuration.ServiceName) .SetSampler(new TraceIdRatioBasedSampler(_configuration.TraceSamplingRatio)); - // Add OTLP exporter if endpoint is configured - if (!string.IsNullOrWhiteSpace(_configuration.OtlpEndpoint)) + // Add OTLP exporter only in Otlp mode + if (_effectiveMode == TelemetryMode.Otlp && !string.IsNullOrWhiteSpace(_configuration.OtlpEndpoint)) { builder.AddOtlpExporter(options => { @@ -478,10 +530,11 @@ private static TracerProvider BuildTracerProvider(ResourceBuilder resourceBuilde }); } - // Add console exporter if enabled - if (_configuration.EnableConsoleExporter) + // Add debug exporter in Debug mode + if (_effectiveMode == TelemetryMode.Debug) { - builder.AddConsoleExporter(); + builder.AddProcessor(new SimpleActivityExportProcessor( + new DebugActivityExporter(_configuration.ServiceName, _configuration.ServiceVersion))); } return builder.Build(); @@ -493,8 +546,8 @@ private static OpenTelemetry.Metrics.MeterProvider BuildMeterProvider(ResourceBu .SetResourceBuilder(resourceBuilder) .AddMeter(_configuration.ServiceName); - // Add OTLP exporter if endpoint is configured - if (!string.IsNullOrWhiteSpace(_configuration.OtlpEndpoint)) + // Add OTLP exporter only in Otlp mode + if (_effectiveMode == TelemetryMode.Otlp && !string.IsNullOrWhiteSpace(_configuration.OtlpEndpoint)) { builder.AddOtlpExporter(options => { @@ -502,10 +555,11 @@ private static OpenTelemetry.Metrics.MeterProvider BuildMeterProvider(ResourceBu }); } - // Add console exporter if enabled - if (_configuration.EnableConsoleExporter) + // Add debug exporter in Debug mode + if (_effectiveMode == TelemetryMode.Debug) { - builder.AddConsoleExporter(); + builder.AddReader(new PeriodicExportingMetricReader( + new DebugMetricExporter(_configuration.ServiceName, _configuration.ServiceVersion), 10000)); } return builder.Build(); @@ -515,14 +569,15 @@ private static ILoggerFactory BuildLoggerFactory(ResourceBuilder resourceBuilder { return Microsoft.Extensions.Logging.LoggerFactory.Create(builder => { + builder.SetMinimumLevel(LogLevel.Trace); builder.AddOpenTelemetry(options => { options.SetResourceBuilder(resourceBuilder); options.IncludeFormattedMessage = true; options.IncludeScopes = true; - // Add OTLP exporter if endpoint is configured - if (!string.IsNullOrWhiteSpace(_configuration.OtlpEndpoint)) + // Add OTLP exporter only in Otlp mode + if (_effectiveMode == TelemetryMode.Otlp && !string.IsNullOrWhiteSpace(_configuration.OtlpEndpoint)) { options.AddOtlpExporter(exporterOptions => { @@ -530,12 +585,13 @@ private static ILoggerFactory BuildLoggerFactory(ResourceBuilder resourceBuilder }); } - // Add console exporter if enabled - if (_configuration.EnableConsoleExporter) - { - options.AddConsoleExporter(); - } }); + + // Add debug logging in Debug mode + if (_effectiveMode == TelemetryMode.Debug) + { + builder.AddProvider(new DebugLoggerProvider(_configuration.ServiceName, _configuration.ServiceVersion)); + } }); }