Skip to main content

Overview

NuGet.Services.Logging is the centralized logging and telemetry infrastructure library shared across NuGet backend services and the gallery web application. It unifies two telemetry pipelines — structured logging via Serilog and metrics/traces via Azure Application Insights — into a single, consistent setup that any NuGet service can adopt with a few method calls. The entry point is LoggingSetup, which builds a pre-configured Serilog LoggerConfiguration and produces an ILoggerFactory wired to both the Serilog pipeline and, when an instrumentation key is provided, the Application Insights sink. System.Diagnostics.Trace listeners are also redirected into Serilog so that legacy trace-based logging in third-party libraries flows through the same pipeline. The library also provides a complete Application Insights telemetry customization layer. ApplicationInsights.Initialize constructs a TelemetryConfiguration that deliberately avoids the deprecated TelemetryConfiguration.Active singleton and instead returns an ApplicationInsightsConfiguration object — a disposable wrapper holding both the TelemetryConfiguration and a DiagnosticsTelemetryModule — that callers own and manage explicitly. Six telemetry initializers and one Serilog enricher stamp every telemetry item and log event with contextual metadata including machine name, cloud role, deployment ID, deployment label, job name, instance name, and build-time assembly metadata (branch, commit ID, build date). Two telemetry processors — one for requests and one for exceptions — adjust Application Insights behavior to match NuGet’s specific conventions. The project targets both net472 and netstandard2.0. The ExceptionTelemetryProcessor is excluded from the netstandard2.0 build because it depends on System.Web.HttpException, which is only available on .NET Framework.

Role in System

NuGet.Services.Logging is a leaf dependency consumed by higher-level service projects. It references only NuGet.Services.Contracts (for the ITelemetryClient interface) and third-party packages, so it introduces no circular dependencies.

Unified Logging Bootstrap

LoggingSetup.CreateDefaultLoggerConfiguration and CreateLoggerFactory provide a one-call setup that enables Serilog enrichers (machine name, process ID, log context, assembly metadata), an optional console sink, an optional Application Insights sink, and a SerilogTraceListener that captures System.Diagnostics.Trace output.

Application Insights Initialization

ApplicationInsights.Initialize produces a self-contained ApplicationInsightsConfiguration that avoids the deprecated TelemetryConfiguration.Active singleton, wires up TelemetryModules from ApplicationInsights.config if present, and configures a DiagnosticsTelemetryModule with an optional heartbeat interval.

Telemetry Enrichers and Initializers

Six ITelemetryInitializer implementations stamp every Application Insights telemetry item with NuGet-specific context: machine name, cloud role/instance, deployment ID, deployment label, job name and instance name, and build-time assembly metadata (branch, commit, build date).

Telemetry Processors

RequestTelemetryProcessor allows specific HTTP response codes to be declared successful regardless of Application Insights defaults. ExceptionTelemetryProcessor (net472 only) converts HttpException items with status codes below 500 into trace telemetry to reduce noise in exception analysis.

ITelemetryClient Wrapper

TelemetryClientWrapper implements ITelemetryClient (from NuGet.Services.Contracts) by delegating to the Application Insights TelemetryClient. All calls are wrapped in try/catch so that telemetry failures never propagate to the caller.

Duration Metrics

DurationMetric and DurationMetric<TProperties> are IDisposable types that start a Stopwatch on construction and emit an elapsed-seconds metric via ITelemetryClient on disposal, enabling using-block timing of any operation. The extension method TrackDuration on ITelemetryClient provides a fluent API for creating them.

Key Files and Classes

Dependencies

NuGet Package References

Internal Project References

Notable Patterns and Implementation Details

ApplicationInsights.Initialize uses TelemetryConfiguration.CreateDefault() rather than new TelemetryConfiguration() or TelemetryConfiguration.Active. CreateDefault() reads the ApplicationInsights.config file if one is present, preserving any module or channel configuration defined there, while still returning a fresh instance that the caller owns. TelemetryConfiguration.Active is deprecated and is never set.
All six telemetry initializers that extend SupportPropertiesTelemetryInitializer or implement ITelemetryInitializer directly use ISupportProperties instead of the deprecated telemetry.Context.Properties API. This is required because the context-level properties bag is marked obsolete in newer Application Insights SDK versions, and some telemetry types (such as MetricTelemetry) do not populate it at all.
ExceptionTelemetryProcessor is compiled only for net472. It references System.Web.HttpException and is excluded from the netstandard2.0 target via a conditional <Compile Remove> in the project file. Services running on .NET Core or .NET 5+ cannot use this processor.
AzureWebAppTelemetryInitializer must be registered last in the Application Insights telemetry initializer list. The Azure Web App Role Environment initializer (registered automatically by the SDK) sets Cloud.RoleName to the hostname, which includes the -staging suffix for staging slots. If AzureWebAppTelemetryInitializer runs before that initializer, the suffix will not yet be present and will not be stripped.
KnownOperationNameEnricher addresses a cardinality problem specific to NuGet Gallery’s web.config-based URL rewrites: before ASP.NET resolves a route, the operation name can be the verbatim URL path including filled-in parameters, making it unsuitable as a metric dimension. By copying only allow-listed operation names to the KnownOperation property, dashboards and alerts can safely group by KnownOperation without risking high-cardinality aggregation issues.
DurationMetric and DurationMetric<TProperties> are designed for using blocks. The timer starts in the constructor and the metric is emitted in Dispose, so the measured duration equals the lifetime of the using scope. The generic variant defers property serialization to disposal time, which means properties on the object can be mutated during the operation and the final values will be captured in the emitted metric.
The assembly metadata stamped by NuGetAssemblyMetadataEnricher and NuGetAssemblyMetadataTelemetryInitializer (Branch, CommitId, BuildDateUtc) is injected into the entry assembly at build time by a PowerShell script in the NuGetGallery build pipeline via AssemblyMetadataAttribute. When running locally without a CI build, these attributes will typically be absent and the enrichers will silently skip stamping.