Skip to content

Multi Targeting

Kieron Lanning edited this page Feb 8, 2026 · 7 revisions

Multi-Target Telemetry Generation

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

Important

v4 Update: v4 fully supports multiple generation targets on a single method! This is a powerful feature for creating comprehensive observability from minimal code.

Interface-Level Multi-Targeting

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
}

Method-Level Multi-Targeting

Multiple Targets on One Method

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

Supported Multi-Target Combinations

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

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
    );
}

ExcludeTargets Values

[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

Inference Rules with Multi-Targeting

⚠️ Important: Inference is Disabled

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

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);
}

Return Types with Multi-Targeting

Activity + Other Targets

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

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

Log + Metric (No 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

Event/Context + Log

Events and Context methods with logs should return void:

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

Common Multi-Targeting Patterns

Pattern 1: Complete Observability Method

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
);

Pattern 2: Event with Context Logging

Add activity events and log them:

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

Pattern 3: Logging with Metrics

Log operations and track distributions:

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

Pattern 4: Selective Parameter Usage

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!
);

Benefits of Multi-Targeting

  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

Diagnostics

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

See Also

Clone this wiki locally