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.
NuGet.Services.Contracts   (defines ITelemetryClient interface)
        |
        v
NuGet.Services.Logging     (implements ITelemetryClient, configures Serilog + App Insights)
        |
        +-- NuGetGallery                  (gallery web app)
        |
        +-- NuGetGallery.Services         (gallery service layer)
        |
        +-- NuGet.Jobs.Common             (shared background job infrastructure)
        |
        +-- NuGet.Services.Metadata.Catalog  (catalog processing pipeline)

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

FileClass / TypePurpose
LoggingSetup.csLoggingSetupStatic factory with CreateDefaultLoggerConfiguration (Serilog setup with enrichers) and CreateLoggerFactory (wires Serilog to ILoggerFactory and optionally to Application Insights; adds SerilogTraceListener to System.Diagnostics.Trace).
ApplicationInsights.csApplicationInsightsStatic factory with Initialize(string) and Initialize(string, TimeSpan) overloads. Creates a TelemetryConfiguration via TelemetryConfiguration.CreateDefault(), registers TelemetryContextInitializer, initializes all TelemetryModules from config, and sets up DiagnosticsTelemetryModule.
ApplicationInsightsConfiguration.csApplicationInsightsConfigurationDisposable wrapper returned by ApplicationInsights.Initialize. Holds TelemetryConfiguration and DiagnosticsTelemetryModule as public properties; Dispose flushes both in order.
TelemetryClientWrapper.csTelemetryClientWrapperImplements ITelemetryClient by delegating to TelemetryClient. All TrackException, TrackMetric, and Flush calls suppress exceptions so telemetry errors are never fatal. The timestamped TrackMetric overload constructs a MetricTelemetry directly to preserve the caller-supplied timestamp.
DurationMetric.csDurationMetric, DurationMetric<TProperties>IDisposable helpers that record operation duration in seconds. The generic variant accepts a typed properties object and a serialization delegate, evaluated lazily on disposal.
NuGetAssemblyMetadata.csNuGetAssemblyMetadataReads AssemblyInformationalVersionAttribute and AssemblyMetadataAttribute entries (Branch, CommitId, BuildDateUtc) from a given Assembly at construction time.
NuGetAssemblyMetadataEnricher.csNuGetAssemblyMetadataEnricherSerilog ILogEventEnricher that stamps log events with a structured NuGetStartingAssemblyMetadata property. Caches the LogEventProperty after first use and skips enrichment when no metadata is present.
Extensions/TelemetryClientExtensions.csTelemetryClientExtensionsExtension methods on ITelemetryClient for creating DurationMetric instances via TrackDuration.
Extensions/NuGetAssemblyMetadataEnricherExtensions.csNuGetAssemblyMetadataEnricherExtensionsExtends Serilog’s LoggerEnrichmentConfiguration with WithNuGetAssemblyMetadata(Assembly) for use in LoggerConfiguration fluent chains.
Extensions/DiagnosticsTelemetryModuleExtensions.csDiagnosticsTelemetryModuleExtensionsAdds AddOrSetHeartbeatProperty to DiagnosticsTelemetryModule, which tries AddHeartbeatProperty first and falls back to SetHeartbeatProperty if the property already exists.
TelemetryInitializers/TelemetryContextInitializer.csTelemetryContextInitializerSets Device.Id, Cloud.RoleInstance, Device.OperatingSystem, Cloud.RoleName, and Component.Version on every telemetry item from Environment.MachineName and the entry assembly name/version. All reads are guarded with try/catch.
TelemetryInitializers/AzureWebAppTelemetryInitializer.csAzureWebAppTelemetryInitializerStrips the -staging suffix from Cloud.RoleName so that production and staging deployment slots are treated as the same role in Application Insights. Must be added last in the initializer list to override the built-in Azure Web App initializer.
TelemetryInitializers/SupportPropertiesTelemetryInitializer.csSupportPropertiesTelemetryInitializerAbstract base for initializers that add a single name/value string property to every ISupportProperties telemetry item. Skips items that already contain the property to handle repeated initializer invocations correctly.
TelemetryInitializers/MachineNameTelemetryInitializer.csMachineNameTelemetryInitializerExtends SupportPropertiesTelemetryInitializer to add MachineName from Environment.MachineName; returns empty string on failure.
TelemetryInitializers/DeploymentIdTelemetryEnricher.csDeploymentIdTelemetryEnricherExtends SupportPropertiesTelemetryInitializer to add CloudDeploymentId from a caller-supplied value.
TelemetryInitializers/DeploymentLabelEnricher.csDeploymentLabelEnricherExtends SupportPropertiesTelemetryInitializer to add DeploymentLabel from a caller-supplied value.
TelemetryInitializers/JobPropertiesTelemetryInitializer.csJobPropertiesTelemetryInitializerStamps every ISupportProperties telemetry item with JobName, InstanceName, and zero or more caller-supplied global dimensions.
TelemetryInitializers/KnownOperationNameEnricher.csKnownOperationNameEnricherFor RequestTelemetry items, checks whether the operation name appears in a caller-supplied allow-list and, if so, copies it to a KnownOperation property to enable low-cardinality aggregation.
TelemetryInitializers/NuGetAssemblyMetadataTelemetryInitializer.csNuGetAssemblyMetadataTelemetryInitializerApplication Insights counterpart to NuGetAssemblyMetadataEnricher. Stamps ISupportProperties telemetry items with AssemblyInformationalVersion, Branch, CommitId, and BuildDateUtc properties, skipping any that are already set.
TelemetryProcessors/RequestTelemetryProcessor.csRequestTelemetryProcessorAllows callers to populate SuccessfulResponseCodes with HTTP status codes that should be marked Success = true on RequestTelemetry regardless of what Application Insights would decide by default.
TelemetryProcessors/ExceptionTelemetryProcessor.csExceptionTelemetryProcessor (net472 only)Intercepts ExceptionTelemetry wrapping an HttpException with a status code below 500 and converts it to a TraceTelemetry at Warning severity, preserving all context properties and the serialized exception JSON.

Dependencies

NuGet Package References

PackagePurpose
Microsoft.ApplicationInsightsCore Application Insights SDK: TelemetryClient, TelemetryConfiguration, ITelemetryInitializer, ITelemetryProcessor, DiagnosticsTelemetryModule, and all telemetry data contract types.
Microsoft.Extensions.LoggingILoggerFactory abstraction that LoggingSetup.CreateLoggerFactory returns, enabling integration with ASP.NET Core and generic host DI.
Serilog.Extensions.LoggingBridges Serilog into Microsoft.Extensions.Logging via loggerFactory.AddSerilog().
Serilog.Sinks.ApplicationInsightsSerilog sink that forwards structured log events to Application Insights as trace telemetry.
Serilog.Sinks.ConsoleOptional console sink enabled by withConsoleLogger = true in CreateDefaultLoggerConfiguration.
Serilog.Enrichers.EnvironmentProvides Enrich.WithMachineName() used in CreateDefaultLoggerConfiguration.
Serilog.Enrichers.ProcessProvides Enrich.WithProcessId() used in CreateDefaultLoggerConfiguration.
SerilogTraceListenerA TraceListener implementation that routes System.Diagnostics.Trace calls into Serilog; added by CreateLoggerFactory if not already registered.
Newtonsoft.JsonUsed in ExceptionTelemetryProcessor (net472) to serialize HttpException details into the exception property of converted trace telemetry.

Internal Project References

ProjectPurpose
NuGet.Services.ContractsProvides the ITelemetryClient interface that TelemetryClientWrapper implements and that DurationMetric and TelemetryClientExtensions accept.

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.