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 introduces 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 and improve developer experience.
Migration Required
Section titled “Migration Required”Before (v3):
using Purview.Telemetry.Activities;using Purview.Telemetry.Logging;using Purview.Telemetry.Metrics;
[ActivitySource("myapp")][Logger][Meter]interface IMyTelemetry{ [Activity] Activity? DoSomething([Baggage]int id);
[Info] void LogMessage(string message);
[Counter] void IncrementCounter([InstrumentMeasurement]int value);}After (v4):
using Purview.Telemetry; // Single namespace!
[ActivitySource("myapp")][Logger][Meter]interface IMyTelemetry{ [Activity] Activity? DoSomething([Baggage]int id);
[Info] void LogMessage(string message);
[Counter] void IncrementCounter([InstrumentMeasurement]int value);}What Changed
Section titled “What Changed”All attributes are now in the unified Purview.Telemetry 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”-
Find and replace imports in your codebase:
- Replace
using Purview.Telemetry.Activities;withusing Purview.Telemetry; - Replace
using Purview.Telemetry.Logging;withusing Purview.Telemetry; - Replace
using Purview.Telemetry.Metrics;withusing Purview.Telemetry;
- Replace
-
Remove duplicate imports - if you had all three imports in a file, you now only need one
-
Rebuild and test your project
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 behavior: Enabled
v4 defaults to OpenTelemetry semantic conventions for generated telemetry names, improving observability and cross-platform compatibility. 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 | Hierarchical with meter prefix | "recordcount" → "myapp.products.record.count" |
| Metric Tag Keys | Lowercased, smashed | snake_case with underscores | "requestcount" → "request_count" |
Examples
Section titled “Examples”Activities
Section titled “Activities”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"Metrics
Section titled “Metrics”Before (v3):
[Meter("MyApp.Products")]interface IProductMetrics{ [Counter] void RecordProductCount([InstrumentMeasurement]int count, [Tag]string productType);}
// Generated meter name: "myapp.products"// Generated instrument name: "recordproductcount"// Generated tag key: "producttype"After (v4 OpenTelemetry mode - DEFAULT):
[Meter("MyApp.Products")]interface IProductMetrics{ [Counter] void RecordProductCount([InstrumentMeasurement]int count, [Tag]string productType);}
// Generated meter name: "MyApp.Products"// Generated instrument name: "myapp.products.record.product.count"// (meter name in lowercase + instrument name in snake_case)// Generated tag key: "product_type"Logging
Section titled “Logging”Before (v3):
[Logger]interface IUserServiceTelemetry{ [Info] void UserLoggedIn(int userId, string userName);}
// Generated log properties: "userid", "username"After (v4 OpenTelemetry mode - DEFAULT):
[Logger]interface IUserServiceTelemetry{ [Info] void UserLoggedIn(int userId, string userName);}
// Generated log properties: "user_id", "user_name"Migration Options
Section titled “Migration Options”You have three migration paths:
Option 1: Adopt OpenTelemetry Naming (Recommended)
Section titled “Option 1: Adopt OpenTelemetry Naming (Recommended)”Best for: New projects, or projects already using OpenTelemetry conventions
Simply upgrade to v4 and update your:
- Dashboard queries to use snake_case keys (
entity_idinstead ofentityid) - Metric queries to use hierarchical names (
myapp.products.record.countinstead ofrecordcount) - Activity source names with proper casing (
MyAppinstead ofmyapp)
Benefits:
- Better interoperability with OpenTelemetry ecosystem
- More readable telemetry data
- Industry-standard naming conventions
- Future-proof for OpenTelemetry tooling
Option 2: Revert to v3 Legacy Naming
Section titled “Option 2: Revert to v3 Legacy Naming”Best for: Existing projects with extensive dashboards/queries that would be costly to update
Use the [TelemetryGeneration] attribute to opt into legacy mode:
Assembly-wide (all interfaces):
using Purview.Telemetry;
[assembly: TelemetryGeneration(NamingConvention = NamingConvention.Legacy)]Per-interface:
using Purview.Telemetry;
[TelemetryGeneration(NamingConvention = NamingConvention.Legacy)][ActivitySource("MyApp")][Logger][Meter]interface IMyTelemetry{ // This interface will use v3-style naming}Result: Generated code will use v3 naming conventions (lowercase, smashed compounds)
Option 3: Mixed Mode (Advanced)
Section titled “Option 3: Mixed Mode (Advanced)”You can use different conventions for different interfaces:
using Purview.Telemetry;
// New interface using OpenTelemetry conventions (default)[ActivitySource("MyApp")]interface INewFeatureTelemetry{ [Activity] Activity? ProcessNewFeature([Tag]int featureId); // Uses OpenTelemetry naming: "feature_id"}
// Legacy interface keeping v3 conventions[TelemetryGeneration(NamingConvention = NamingConvention.Legacy)][ActivitySource("MyApp")]interface ILegacyTelemetry{ [Activity] Activity? ProcessLegacy([Tag]int entityId); // Uses Legacy naming: "entityid"}Available Naming Conventions
Section titled “Available Naming Conventions”public enum NamingConvention{ /// <summary> /// v3 behavior: lowercase, smashed compound words (no separators) /// Example: "entityid", "recordcount", "myapp" /// </summary> Legacy = 0,
/// <summary> /// v4 default: OpenTelemetry conventions /// - Preserves casing for ActivitySource/Activity names /// - Uses snake_case for tags/properties (entity_id) /// - Uses hierarchical dot.separated for metrics (myapp.products.record.count) /// </summary> OpenTelemetry = 1}Migration Checklist
Section titled “Migration Checklist”- Update package reference to v4.0.0-prerelease.6
- Consolidate namespace imports to
using Purview.Telemetry; - Decide on naming convention:
- Option A: Keep OpenTelemetry naming (default) - update dashboards/queries
- Option B: Add
[assembly: TelemetryGeneration(NamingConvention = NamingConvention.Legacy)]to revert to v3 naming - Option C: Mix conventions per-interface using
[TelemetryGeneration]attribute
- Rebuild project and review generated code
- Update monitoring dashboards to match new naming (if using OpenTelemetry mode)
- Update metric queries/alerts to use new hierarchical names (if using OpenTelemetry mode)
- Test telemetry collection in your observability platform
- Update documentation/runbooks with new telemetry names
Impact Assessment
Section titled “Impact Assessment”Low Impact Scenarios:
- New projects with no existing dashboards
- Projects that don’t query telemetry by specific field names
- Projects using generic telemetry queries
Medium Impact Scenarios:
- Projects with basic dashboards that can be easily updated
- Projects with moderate number of metric queries
- Projects using standard OpenTelemetry tools
High Impact Scenarios:
- Projects with extensive custom dashboards
- Projects with many hardcoded metric queries
- Projects with automated alerting based on specific metric names
- Multi-team projects where changing names requires coordination
v1 and v2 to v3
Section titled “v1 and v2 to v3”Logging Event Name Generation
Section titled “Logging Event Name Generation”A change was made to the available options and the default value, to better support the default means of generation used by the Microsoft Logging/ Telemetry source generators.
Previously, the default event name generated for the logging method also included a trimmed down version of the class name, and the method name combined.
For example:
[Logger]interface IServiceTelemetry{ void LogAThing(int theThing);}This is supported by the following changes to the LogPrefixType’s field.
| 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 one of the following:
LoggerGenerationAttribute.DefaultPrefixTypetoLogPrefixType.TrimmedClassNameat the assembly level.LoggerAttribute.PrefixTypetoLogPrefixType.TrimmedClassNameat the interface level.