Breaking Changes
Breaking Changes
Section titled “Breaking Changes”This page documents breaking changes between major versions to help you migrate your code.
Table of contents
Section titled “Table of contents”v3 to v4
Section titled “v3 to v4”Version 4 introduced two major breaking changes: namespace consolidation and OpenTelemetry-aligned naming conventions.
Namespace consolidation
Section titled “Namespace consolidation”Impact: High — requires code changes in all projects using v3
v4 consolidates all attributes into a single namespace to simplify imports.
Migration required
Section titled “Migration required”Before (v3):
using Purview.Telemetry.Activities;using Purview.Telemetry.Logging;using Purview.Telemetry.Metrics;After (v4):
using Purview.Telemetry; // single namespace| v3 namespace | v4 namespace | Affected attributes |
|---|---|---|
Purview.Telemetry.Activities | Purview.Telemetry | [ActivitySource], [Activity], [Event], [Context], [Baggage] |
Purview.Telemetry.Logging | Purview.Telemetry | [Logger], [Log], [Debug], [Info], [Warning], [Error], [Critical] |
Purview.Telemetry.Metrics | Purview.Telemetry | [Meter], [Counter], [AutoCounter], [Histogram], [Observable*], [UpDownCounter] |
Purview.Telemetry | Purview.Telemetry | [Tag], [TelemetryGeneration], [Exclude] |
Migration steps
Section titled “Migration steps”- Replace
using Purview.Telemetry.Activities;/using Purview.Telemetry.Logging;/using Purview.Telemetry.Metrics;withusing Purview.Telemetry;. - Remove duplicate imports.
- Rebuild and test.
OpenTelemetry-aligned naming
Section titled “OpenTelemetry-aligned naming”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.
What changed
Section titled “What changed”| Telemetry type | v3 behaviour | v4 default (OpenTelemetry) | Example change |
|---|---|---|---|
| ActivitySource name | Assembly name lowercased | Assembly name casing preserved | "myapp" → "MyApp" |
| Activity names | Method name lowercased | Method name casing preserved | "getentity" → "GetEntity" |
| Tag/baggage keys | Lowercased, smashed compounds | snake_case with underscores | "entityid" → "entity_id" |
| Metric instrument names | Lowercased, smashed | Interface prefix + snake_case, no meter prefix | "product.recordcount" → "product.record_count" (IProductTelemetry.RecordCount) |
| Metric tag keys | Lowercased, smashed | snake_case with underscores | "requestcount" → "request_count" |
Examples
Section titled “Examples”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"Migration options
Section titled “Migration options”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].
v5.0.1 to v5.0.2
Section titled “v5.0.1 to v5.0.2”Impact: Low — affects projects that consume the generated TelemetryNames class
TelemetryNames is now opt-in
Section titled “TelemetryNames is now opt-in”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.
v4 to v5
Section titled “v4 to v5”Impact: Low to Medium — affects the default meter name
Meter default-name resolution
Section titled “Meter default-name resolution”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:
MeterAttribute.Name(interface)MeterGenerationAttribute.MeterName(assembly)- The assembly name
If you rely on the old interface-name meter default, set the name explicitly:
[Meter("CacheServiceTelemetry")]interface ICacheServiceTelemetry { }MeterGenerationAttribute additions
Section titled “MeterGenerationAttribute additions”v5 adds MeterName and MeterNameGenerationType to [MeterGeneration], controlling the default meter name and whether instrument names are prefixed with the meter name. See Metrics.
Activity return type should be nullable
Section titled “Activity return type should be nullable”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.
v1 and v2 to v3
Section titled “v1 and v2 to v3”Logging event-name generation
Section titled “Logging event-name generation”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:
| Field | Old behaviour | New behaviour |
|---|---|---|
Default | Generated a prefix based on the generated class name. | Generates no suffix. |
NoSuffix | Generated no suffix. | Field removed. |
TrimmedClassName | Previously 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).