Skip to content
Purview Dev logo

Breaking Changes

Stable Telemetry SourceGenerator Reviewed 2026-10-28 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 documents breaking changes between major versions to help you migrate your code.

Version 4 introduced two major breaking changes: namespace consolidation and OpenTelemetry-aligned naming conventions.

Impact: High — requires code changes in all projects using v3

v4 consolidates all attributes into a single namespace to simplify imports.

Before (v3):

using Purview.Telemetry.Activities;
using Purview.Telemetry.Logging;
using Purview.Telemetry.Metrics;

After (v4):

using Purview.Telemetry; // single namespace
v3 namespacev4 namespaceAffected attributes
Purview.Telemetry.ActivitiesPurview.Telemetry[ActivitySource], [Activity], [Event], [Context], [Baggage]
Purview.Telemetry.LoggingPurview.Telemetry[Logger], [Log], [Debug], [Info], [Warning], [Error], [Critical]
Purview.Telemetry.MetricsPurview.Telemetry[Meter], [Counter], [AutoCounter], [Histogram], [Observable*], [UpDownCounter]
Purview.TelemetryPurview.Telemetry[Tag], [TelemetryGeneration], [Exclude]
  1. Replace using Purview.Telemetry.Activities; / using Purview.Telemetry.Logging; / using Purview.Telemetry.Metrics; with using Purview.Telemetry;.
  2. Remove duplicate imports.
  3. Rebuild and test.

Impact: Medium to High — changes generated telemetry names (may break dashboards/queries)

Introduced in: v4.0.0-alpha.5

Default behaviour: Enabled

v4 defaults to OpenTelemetry semantic conventions for generated telemetry names. This is a breaking change if you rely on specific telemetry names in dashboards, queries, or monitoring tools.

Telemetry typev3 behaviourv4 default (OpenTelemetry)Example change
ActivitySource nameAssembly name lowercasedAssembly name casing preserved"myapp" → "MyApp"
Activity namesMethod name lowercasedMethod name casing preserved"getentity" → "GetEntity"
Tag/baggage keysLowercased, smashed compoundssnake_case with underscores"entityid" → "entity_id"
Metric instrument namesLowercased, smashedInterface prefix + snake_case, no meter prefix"product.recordcount" → "product.record_count" (IProductTelemetry.RecordCount)
Metric tag keysLowercased, smashedsnake_case with underscores"requestcount" → "request_count"

Before (v3):

[ActivitySource("MyApp")]
interface IOrderTelemetry
{
[Activity]
Activity? ProcessingOrder([Baggage]int orderId, [Tag]string customerName);
}
// Generated ActivitySource name: "myapp"
// Generated Activity name: "processingorder"
// Generated tag keys: "orderid", "customername"

After (v4+ OpenTelemetry mode — default):

[ActivitySource("MyApp")]
interface IOrderTelemetry
{
[Activity]
Activity? ProcessingOrder([Baggage]int orderId, [Tag]string customerName);
}
// Generated ActivitySource name: "MyApp"
// Generated Activity name: "ProcessingOrder"
// Generated tag keys: "order_id", "customer_name"

Option 1 — adopt OpenTelemetry naming (recommended): upgrade and update dashboards/queries to use snake_case keys and hierarchical metric names.

Option 2 — revert to v3 legacy naming:

using Purview.Telemetry;
// Revert ALL telemetry to v3 naming (assembly-level)
[assembly: TelemetryGeneration(NamingConvention = NamingConvention.Legacy)]
// Or set per-interface
[TelemetryGeneration(NamingConvention = NamingConvention.Legacy)]
interface IMyTelemetry { }
public enum NamingConvention
{
Legacy = 0, // v3 behaviour: lowercase, smashed compounds
OpenTelemetry = 1 // v4+ default: OTel conventions
}

Option 3 — mixed mode: apply different conventions per interface with [TelemetryGeneration].

Impact: Low — affects projects that consume the generated TelemetryNames class

The generator now records each assembly’s generated activity source and meter names in an assembly-level [GeneratedTelemetryNames] attribute (GenerateTelemetryNamesAttribute, default true), which downstream compilations read to aggregate the names of everything they reference. Because that attribute replaces the per-assembly class as the way names travel, GenerateTelemetryNamesClass now defaults to false.

If a project uses TelemetryNames.MeterNames / TelemetryNames.ActivitySourceNames, opt it back in:

[assembly: TelemetryGeneration(GenerateTelemetryNamesClass = true)]

The class it generates now also includes the names recorded by referenced assemblies, so a host project no longer needs one TelemetryNames reference per library. Set AggregateReferencedTelemetryNames = false for the previous assembly-only behaviour. See Generation.

Impact: Low to Medium — affects the default meter name

In v4, when [Meter(Name = ...)] was not specified, the meter name defaulted to the interface name without the leading I (for example, ICacheServiceTelemetry → CacheServiceTelemetry).

In v5 the default meter name is resolved in this order:

  1. MeterAttribute.Name (interface)
  2. MeterGenerationAttribute.MeterName (assembly)
  3. The assembly name

If you rely on the old interface-name meter default, set the name explicitly:

[Meter("CacheServiceTelemetry")]
interface ICacheServiceTelemetry { }

v5 adds MeterName and MeterNameGenerationType to [MeterGeneration], controlling the default meter name and whether instrument names are prefixed with the meter name. See Metrics.

The new TSG3022 warning recommends returning Activity? from Activity methods. It is a warning, not an error, but plan to move to nullable Activity return types as the Activity can be null when no listeners are active.

Activities targets now honour parameter-level [ExcludeTargets]

Section titled “Activities targets now honour parameter-level [ExcludeTargets]”

[ExcludeTargets(Targets.Activities)] on a parameter is now applied consistently: the parameter is no longer set as a tag, added as baggage, or used as a reserved parameter (tags, parentContext, links, startTime, timestamp, [Escape], [StatusDescription]) for the Activity/ActivityEvent. It stays part of the generated method signature and is still passed to the Logging and Metrics targets.

Impact: Low — the generated Activity tags/baggage change for interfaces that already use [ExcludeTargets(Targets.Activities)] (previously the attribute was ignored for the Activities target). Activities-specific diagnostics for the excluded parameter (TSG3000, TSG3003–TSG3011, TSG3016, TSG3017, TSG3021) are no longer raised.

TSG3021 only applies when the OpenTelemetry exception rules are used

Section titled “TSG3021 only applies when the OpenTelemetry exception rules are used”

TSG3021 (exception event should be named exception) is no longer raised when the exception is not recorded using the OpenTelemetry exception rules — that is, when [Event(UseRecordExceptionRules = false)] is used, when the exception parameter is [Baggage], or when it is excluded from the Activities target. When the Name was set on a logging attribute instead of [Event], the diagnostic now says so explicitly.

Previously, the default event name generated for a logging method included a trimmed-down version of the class name combined with the method name.

[Logger]
interface IServiceTelemetry
{
void LogAThing(int theThing);
}

This is supported by changes to LogPrefixType:

FieldOld behaviourNew behaviour
DefaultGenerated a prefix based on the generated class name.Generates no suffix.
NoSuffixGenerated no suffix.Field removed.
TrimmedClassNamePreviously the default behaviour.New field; generates a suffix based on the generated class name.

To return to the previous behaviour, set LoggerGenerationAttribute.DefaultPrefixType to LogPrefixType.TrimmedClassName (assembly level) or LoggerAttribute.PrefixType to LogPrefixType.TrimmedClassName (interface level).