Skip to content
Beta

Multi-targeting allows you to generate multiple types of telemetry (Activities, Logs, and Metrics) from a single interface or even a single method.

Apply multiple generation attributes to an interface to enable all telemetry types:

using Purview.Telemetry;
[ActivitySource("OrderService")]
[Logger]
[Meter("OrderService")]
interface IOrderTelemetry
{
// Methods can use one or more telemetry types
}

v4 supports combining multiple telemetry attributes on a single method. When called, the method emits all specified telemetry types simultaneously:

[ActivitySource("OrderService")]
[Logger]
[Meter]
interface IOrderTelemetry
{
// MULTI-TARGET: Creates Activity + Logs Info + Increments Counter - all from one call!
[Activity]
[Info]
[AutoCounter]
Activity? ProcessingOrder([Baggage]int orderId, [Tag]string customerName);
// MULTI-TARGET: Adds ActivityEvent + Logs as Debug
[Event]
[Debug]
void OrderValidated(Activity? activity, decimal amount);
// SINGLE-TARGET: Only logs
[Warning]
void OrderRejected(int orderId, string reason);
// SINGLE-TARGET: Only metric
[Histogram]
void OrderProcessingDuration([InstrumentMeasurement]int milliseconds);
}

Usage:

// Single method call emits 3 telemetry types!
using var activity = telemetry.ProcessingOrder(123, "Acme Corp");
// ✓ Activity created and started
// ✓ Info log entry written
// ✓ Counter incremented by 1
Combination Supported Example
Activity + Log ✅ Yes [Activity] + [Info]
Activity + Metric ✅ Yes [Activity] + [AutoCounter]
Log + Metric ✅ Yes [Info] + [Histogram]
Activity + Log + Metric ✅ Yes [Activity] + [Info] + [AutoCounter]
Event + Log ✅ Yes [Event] + [Debug]
Context + Log ✅ Yes [Context] + [Trace]

Important Rules:

  • Only one attribute per telemetry family is allowed per method
  • Activities: Use ONE of [Activity], [Event], or [Context]
  • Logging: Use ONE of [Log], [Trace], [Debug], [Info], [Warning], [Error], [Critical]
  • Metrics: Use ONE of [Counter], [AutoCounter], [Histogram], [UpDownCounter], or Observable variants

Invalid Examples:

// ERROR TSG1002: Multiple activity attributes
[Activity]
[Event]
void InvalidMethod(Activity? activity);
// ERROR TSG1002: Multiple logging attributes
[Info]
[Warning]
void InvalidMethod(string message);
// ERROR TSG1002: Multiple metric attributes
[Counter]
[Histogram]
void InvalidMethod([InstrumentMeasurement]int value);

Excluding Parameters from Specific Targets

Section titled “Excluding Parameters from Specific Targets”

When using multi-target methods, you may want certain parameters to only apply to specific telemetry types. Use [ExcludeTargets] to control this:

using Purview.Telemetry;
[ActivitySource("PaymentService")]
[Logger]
[Meter]
interface IPaymentTelemetry
{
[Activity]
[Info]
[Counter]
Activity? ProcessingPayment(
[Baggage]Guid paymentId,
// Exclude verbose message from metrics (would be wasted as tag)
[ExcludeTargets(Targets.Metrics)]
string processingMessage,
// Measurement only applies to metrics
[InstrumentMeasurement]
decimal amount,
// Exclude internal details from Activity baggage
[ExcludeTargets(Targets.Activities)]
[Tag] // Still included in logs and metrics
string internalReference
);
}
[Flags]
public enum Targets
{
Activities = 1,
Logging = 2,
Metrics = 4
}

Usage:

// Exclude from multiple targets
[ExcludeTargets(Targets.Activities | Targets.Metrics)]
string loggingOnlyParameter
// Exclude from single target
[ExcludeTargets(Targets.Logging)]
int metricsAndActivityParameter

When an interface has multiple class-level attributes ([ActivitySource], [Logger], [Meter]), inference is disabled. You must explicitly specify generation targets on each method.

[ActivitySource("MyApp")]
[Logger]
[Meter]
interface IMyTelemetry
{
// ERROR TSG1001: No explicit attribute
void ProcessItem(int id);
// ✅ CORRECT: Explicit attribute
[Info]
void ProcessItem(int id);
// ✅ CORRECT: Excluded from generation
[Exclude]
void ProcessItem(int id);
}

Single-Target Interfaces Support Inference

Section titled “Single-Target Interfaces Support Inference”

If your interface has only ONE generation attribute, inference works normally:

// Single-target: Inference enabled
[Logger]
interface ILogOnlyTelemetry
{
// ✅ Inferred as [Info] because of Exception
void ProcessItem(int id, Exception ex);
// ✅ Inferred as scoped log (returns IDisposable)
IDisposable? ProcessingBatch(int batchId);
}

When combining [Activity] with logs/metrics, return Activity?:

[Activity]
[Info]
[AutoCounter]
Activity? ProcessingOrder(int orderId); // ✅ Returns Activity

When combining logs and metrics without activities, return void or IDisposable?:

[Info]
[Histogram]
void RecordOperation([InstrumentMeasurement]int duration, string operation); // ✅ void
[Info]
[AutoCounter]
IDisposable? ProcessingBatch(int batchId); // ✅ Scoped log + counter

Events and Context methods with logs should return void:

[Event]
[Debug]
void OrderCompleted(Activity? activity, decimal total); // ✅ void

Track everything about an operation:

[Activity] // Distributed tracing
[Info] // Structured logging
[AutoCounter] // Count occurrences
Activity? ProcessingRequest(
[Baggage]string requestId,
[Tag]string endpoint,
[Tag]string method
);

Add activity events and log them:

[Event]
[Debug]
void StepCompleted(
Activity? activity,
[Tag]string stepName,
[Tag]int duration
);

Log operations and track distributions:

[Info]
[Histogram]
void RequestCompleted(
[InstrumentMeasurement]int durationMs,
[Tag]string endpoint,
[Tag]int statusCode
);

Use different parameters for different telemetry:

[Activity]
[Info]
[Counter]
Activity? ApiCall(
[Baggage]string traceId, // Activity baggage only
[ExcludeTargets(Targets.Metrics)]
string verboseMessage, // Activity + Log only
[InstrumentMeasurement]
[ExcludeTargets(Targets.Activities | Targets.Logging)]
int callCount, // Metrics only
[Tag]string endpoint // All three!
);
  1. Less Code - One method definition generates multiple telemetry types
  2. Consistency - Same parameters used across telemetry types (when appropriate)
  3. Atomicity - All telemetry emitted together, no risk of forgetting one
  4. Maintainability - Change once, affects all telemetry types
  5. Performance - Single method call overhead instead of multiple
Diagnostic When Resolution
TSG1001 Method has no explicit attribute in multi-target interface Add [Activity], [Info], [Counter], etc., or [Exclude]
TSG1002 Multiple attributes from same family Use only one Activity, Logging, or Metrics attribute per method
TSG1006 [ExcludeTargets] references missing target Remove [ExcludeTargets] or add the target attribute to the method
TSG1007 [ExcludeTargets] results in invalid config Adjust exclusions to ensure valid parameters remain for each target