Generated Output
Generated Output
Section titled “Generated Output”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]emitsAdd(1, ...).PopulateMeterTags/Populate{Method}Tagspartial 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.
See also
Section titled “See also”- Sample Application — the full source
- Generation — controlling class names, DI, and names
- Diagnostics — analyzer rules