Skip to content
Purview Dev logo

Generated Output

Stable Telemetry SourceGenerator Reviewed 2026-10-07 purview-dev/telemetry-sourcegenerator Star on GitHub 36 activity c-sharp distributed-tracing dotnet events high-performance-logging instrumentation logging melt metrics nuget observability open-telemetry open-telemetry-csharp otel roslyn source-generator spans telemetry tracing

This page shows real generated output from the sample application, produced by 5.0.0. The interface:

[ActivitySource]
[Logger]
[Meter]
interface IEntityStoreTelemetry
{
[Activity]
[Info]
[AutoCounter]
Activity? GettingEntityFromStore(int entityId, [Baggage] string serviceUrl);
[Event]
[Trace]
void GetDuration(Activity? activity, int durationInMS);
[Context]
void RetrievedEntity(Activity? activity, float totalValue, int lastUpdatedByUserId);
[Warning]
void EntityNotFound(int entityId);
[Histogram]
void RecordEntitySize(int sizeInBytes);
}

generates one partial EntityStoreTelemetryCore class split across Activity, Logging, and Metric files, plus a DI extension class and an assembly-level [GeneratedTelemetryNames] attribute recording the generated names.

Activities (EntityStoreTelemetryCore.Activity.g.cs)

Section titled “Activities (EntityStoreTelemetryCore.Activity.g.cs)”
// <auto-generated />
#nullable enable
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
internal sealed partial class EntityStoreTelemetryCore : IEntityStoreTelemetry
{
private static readonly global::System.Diagnostics.ActivitySource _activitySource = new global::System.Diagnostics.ActivitySource("sample-weather-app-api");
public static void RecordExceptionInternal(
global::System.Diagnostics.Activity? activity,
global::System.Exception? exception,
bool escape
)
{
if (activity == null || exception == null)
{
return;
}
global::System.Diagnostics.ActivityTagsCollection tagsCollection = new global::System.Diagnostics.ActivityTagsCollection();
tagsCollection.Add("exception.escaped", escape);
tagsCollection.Add("exception.message", exception.Message);
tagsCollection.Add("exception.type", exception.GetType().FullName);
tagsCollection.Add("exception.stacktrace", exception.StackTrace);
global::System.Diagnostics.ActivityEvent recordExceptionEvent = new global::System.Diagnostics.ActivityEvent(name: "exception", timestamp: default, tags: tagsCollection);
activity.AddEvent(recordExceptionEvent);
}
private global::System.Diagnostics.Activity? GettingEntityFromStore_Activity(
int entityId,
string serviceUrl
)
{
if (!_activitySource.HasListeners())
{
return null;
}
global::System.Diagnostics.Activity? activityGettingEntityFromStore = _activitySource.StartActivity("GettingEntityFromStore", global::System.Diagnostics.ActivityKind.Internal, parentId: default, tags: default, links: default, startTime: default);
if (activityGettingEntityFromStore != null)
{
activityGettingEntityFromStore.SetTag("entity_id", entityId);
activityGettingEntityFromStore.SetBaggage("service_url", serviceUrl);
}
return activityGettingEntityFromStore;
}
public global::System.Diagnostics.Activity? GettingEntityFromStore(
int entityId,
string serviceUrl
)
{
var activityResult = GettingEntityFromStore_Activity(entityId, serviceUrl);
GettingEntityFromStore_Logging(entityId, serviceUrl);
GettingEntityFromStore_Metrics(entityId, serviceUrl);
return activityResult;
}
public void RetrievedEntity(
global::System.Diagnostics.Activity? activity,
float totalValue,
int lastUpdatedByUserId
)
{
if (!_activitySource.HasListeners())
{
return;
}
if (activity != null)
{
activity.SetTag("total_value", totalValue);
activity.SetTag("last_updated_by_user_id", lastUpdatedByUserId);
}
}
}

Key points:

  • A HasListeners() fast-path avoids work when no listener is subscribed.
  • Tag/baggage keys use snake_case (entity_id, service_url) — the OpenTelemetry naming convention.
  • Multi-target methods orchestrate the per-target private methods.

Logging (EntityStoreTelemetryCore.Logging.g.cs)

Section titled “Logging (EntityStoreTelemetryCore.Logging.g.cs)”
internal sealed partial class EntityStoreTelemetryCore : IEntityStoreTelemetry
{
private readonly global::Microsoft.Extensions.Logging.ILogger<IEntityStoreTelemetry> _logger;
private static readonly global::System.Action<global::Microsoft.Extensions.Logging.ILogger, int, string, global::System.Exception?> _gettingEntityFromStoreAction = global::Microsoft.Extensions.Logging.LoggerMessage.Define<int, string>(global::Microsoft.Extensions.Logging.LogLevel.Information, new global::Microsoft.Extensions.Logging.EventId(0, "GettingEntityFromStore"), "GettingEntityFromStore: EntityId = {EntityId}, ServiceUrl = {ServiceUrl}");
private static readonly global::System.Action<global::Microsoft.Extensions.Logging.ILogger, int, global::System.Exception?> _entityNotFoundAction = global::Microsoft.Extensions.Logging.LoggerMessage.Define<int>(global::Microsoft.Extensions.Logging.LogLevel.Warning, new global::Microsoft.Extensions.Logging.EventId(0, "EntityNotFound"), "EntityNotFound: EntityId = {EntityId}");
public EntityStoreTelemetryCore(
global::Microsoft.Extensions.Logging.ILogger<IEntityStoreTelemetry> logger,
global::System.Diagnostics.Metrics.IMeterFactory meterFactory
)
{
_logger = logger;
InitializeMeters(meterFactory);
}
private void GettingEntityFromStore_Logging(int entityId, string serviceUrl)
{
if (!_logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Information))
{
return;
}
_gettingEntityFromStoreAction(_logger, entityId, serviceUrl, null);
}
public void EntityNotFound(int entityId)
{
if (!_logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Warning))
{
return;
}
_entityNotFoundAction(_logger, entityId, null);
}
}

These methods fit the v1 limits (≤ 6 non-exception parameters, no [ExpandEnumerable]/[LogProperties]), so LoggerGenerationMode.Auto selected v1 LoggerMessage.Define generation with an IsEnabled fast-path. See Logging.

Metrics (EntityStoreTelemetryCore.Metric.g.cs)

Section titled “Metrics (EntityStoreTelemetryCore.Metric.g.cs)”
internal sealed partial class EntityStoreTelemetryCore : IEntityStoreTelemetry
{
private global::System.Diagnostics.Metrics.Meter _meter = default!;
private global::System.Diagnostics.Metrics.Counter<int> _gettingEntityFromStoreInstrument = default!;
private global::System.Diagnostics.Metrics.Histogram<int> _recordEntitySizeInstrument = default!;
public void InitializeMeters(global::System.Diagnostics.Metrics.IMeterFactory meterFactory)
{
if (_meter != null)
{
throw new global::System.Exception("The meters have already been initialized.");
}
global::System.Collections.Generic.Dictionary<string, object?> meterTags = new global::System.Collections.Generic.Dictionary<string, object?>();
PopulateMeterTags(meterTags);
_meter = meterFactory.Create(new global::System.Diagnostics.Metrics.MeterOptions("SampleApp.APIService") {
Version = null,
Tags = meterTags
});
_gettingEntityFromStoreInstrument = _meter.CreateCounter<int>(name: "entity_store.getting_entity_from_store", unit: null, description: null, tags: gettingEntityFromStoreTags);
_recordEntitySizeInstrument = _meter.CreateHistogram<int>(name: "entity_store.record_entity_size", unit: null, description: null, tags: recordEntitySizeTags);
}
partial void PopulateMeterTags(global::System.Collections.Generic.Dictionary<string, object?> meterTags);
partial void PopulateGettingEntityFromStoreTags(global::System.Collections.Generic.Dictionary<string, object?> instrumentTags);
partial void PopulateRecordEntitySizeTags(global::System.Collections.Generic.Dictionary<string, object?> instrumentTags);
private void GettingEntityFromStore_Metrics(int entityId, string serviceUrl)
{
_gettingEntityFromStoreInstrument.Add(1, new global::System.Collections.Generic.KeyValuePair<string, object?>("entity_id", entityId), new global::System.Collections.Generic.KeyValuePair<string, object?>("service_url", serviceUrl));
}
public void RecordEntitySize(int sizeInBytes)
{
_recordEntitySizeInstrument.Record(sizeInBytes);
}
}

Key points:

  • The meter name defaults to the assembly name (SampleApp.APIService).
  • [AutoCounter] emits Add(1, ...).
  • PopulateMeterTags/Populate{Method}Tags partial methods let you add tags at initialisation.

DI registration (EntityStoreTelemetryCoreDIExtension.DependencyInjection.g.cs)

Section titled “DI registration (EntityStoreTelemetryCoreDIExtension.DependencyInjection.g.cs)”
namespace Microsoft.Extensions.DependencyInjection
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
internal static partial class EntityStoreTelemetryCoreDIExtension
{
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddEntityStoreTelemetry(
this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services
)
{
return services.AddSingleton<IEntityStoreTelemetry, global::EntityStoreTelemetryCore>();
}
}
}

Recorded names (SampleApp.APIService.GeneratedTelemetryNames.g.cs)

Section titled “Recorded names (SampleApp.APIService.GeneratedTelemetryNames.g.cs)”
[assembly: global::Purview.Telemetry.GeneratedTelemetryNamesAttribute(new string[] { "SampleApp.APIService" }, new string[] { "SampleApp.APIService" })]

Emitted for every assembly that generates an ActivitySource or a Meter, so downstream assemblies can read the names back out of metadata. Both names default to the assembly name here; an assembly-level [ActivitySourceGeneration] or [MeterGeneration], or a name on the interface attribute, overrides them.

Telemetry names (SampleApp.APIService.TelemetryNames.g.cs)

Section titled “Telemetry names (SampleApp.APIService.TelemetryNames.g.cs)”

Opt in with [assembly: TelemetryGeneration(GenerateTelemetryNamesClass = true)]:

namespace SampleApp.APIService
{
public static partial class TelemetryNames
{
public static readonly string[] MeterNames = new string[] { "SampleApp.APIService", "SampleApp.Shared" };
public static readonly string[] ActivitySourceNames = new string[] { "SampleApp.APIService", "SampleApp.Shared" };
}
}

SampleApp.Shared comes from the referenced project of the same name, whose IWeatherForecastTelemetry declares an ActivitySource and a Meter: the class aggregates this assembly’s names with the names recorded by everything it references. See Generation.