-
Notifications
You must be signed in to change notification settings - Fork 3
Breaking Changes
This page documents breaking changes between major versions to help you migrate your code.
Version 4 introduces 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 and improve developer experience.
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);
}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]
|
-
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
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.
| 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"
|
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"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"Important
In OpenTelemetry mode, metric instrument names automatically include the meter name as a prefix (converted to lowercase with dots), following OpenTelemetry best practices for hierarchical metric naming.
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"You have three migration paths:
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
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)
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"
}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
}- Update package reference to v4.0.0-prerelease.1
- 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
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
Tip
For high-impact scenarios, consider using NamingConvention.Legacy initially, then gradually migrating to OpenTelemetry conventions during a planned maintenance window.
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);
}Important
In v1 and v2, the default event name for LogAThing would have been Service.LogAThing. As of v3, the default has changed to LogAThing.
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.
Important
Consider helping children around the world affected by conflict. You can donate any amount to War Child here - any amount can help save a life.
Purview Telemetry Source Generator v4.0.0-prerelease.1 | Home | Getting Started | FAQ | Breaking Changes | GitHub