Skip to content

OpenTelemetry Integration

TickerQ.Instrumentation.OpenTelemetry provides distributed tracing and structured logging for TickerQ jobs, enabling comprehensive observability in distributed systems.

Sections

Integration

Integrate with observability platforms, APM tools, and monitoring systems.

Quick Start

csharp
using TickerQ.DependencyInjection;
using TickerQ.Instrumentation.OpenTelemetry;
using OpenTelemetry.Trace;

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        tracing.AddSource("TickerQ")
               .AddConsoleExporter()
               .AddJaegerExporter();
    });

// Add TickerQ with OpenTelemetry instrumentation
builder.Services.AddTickerQ(options =>
{
    // Your TickerQ configuration
    options.AddOpenTelemetryInstrumentation(); // 👈 Enable tracing
});

var app = builder.Build();
app.UseTickerQ();
app.Run();

Trace Structure

Activity Hierarchy

TickerQ creates the following activity structure:

tickerq.job.execute.timeticker (main job execution span)
├── tickerq.job.enqueued (when job starts execution)
├── tickerq.job.completed (on successful completion)
├── tickerq.job.failed (on failure)
├── tickerq.job.cancelled (on cancellation)
├── tickerq.job.skipped (when skipped)
├── tickerq.seeding.started (for data seeding)
└── tickerq.seeding.completed (seeding completion)

Activity Names

  • tickerq.job.execute.timeticker - Main TimeTicker execution
  • tickerq.job.execute.crontickeroccurrence - CronTicker occurrence execution
  • tickerq.job.enqueued - Job enqueued event
  • tickerq.job.completed - Job completed event
  • tickerq.job.failed - Job failed event
  • tickerq.job.cancelled - Job cancelled event
  • tickerq.job.skipped - Job skipped event

Activity Tags

TickerQ adds comprehensive tags to activities:

TagDescriptionExample
tickerq.job.idUnique job identifier123e4567-e89b-12d3-a456-426614174000
tickerq.job.typeType of tickerTimeTicker, CronTicker
tickerq.job.functionFunction nameProcessEmails
tickerq.job.priorityJob priorityNormal, High, LongRunning
tickerq.job.machineMachine executing jobweb-server-01
tickerq.job.parent_idParent job IDparent-job-guid
tickerq.job.enqueued_fromWhere job was enqueuedUserController.CreateUser (Program.cs:42)
tickerq.job.is_dueWhether job is duetrue, false
tickerq.job.is_childWhether this is a child jobtrue, false
tickerq.job.retriesMaximum retry attempts3
tickerq.job.current_attemptCurrent retry attempt1, 2, 3
tickerq.job.final_statusFinal execution statusDone, Failed, Cancelled
tickerq.job.final_retry_countFinal retry count2
tickerq.job.execution_time_msExecution time in milliseconds1250
tickerq.job.successWhether execution was successfultrue, false
tickerq.job.error_typeException typeSqlException, TimeoutException
tickerq.job.error_messageError messageConnection timeout
tickerq.job.error_stack_traceFull stack traceat MyService.ProcessData()...
tickerq.job.cancellation_reasonReason for cancellationTask was cancelled
tickerq.job.skip_reasonReason for skippingAnother instance is already running

Integration Examples

With Jaeger

csharp
builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        tracing.AddSource("TickerQ")
               .AddJaegerExporter(options =>
               {
                   options.Endpoint = new Uri("http://localhost:14268/api/traces");
               });
    });

builder.Services.AddTickerQ(options =>
{
    options.AddOpenTelemetryInstrumentation();
});

With Azure Application Insights

csharp
builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        tracing.AddSource("TickerQ")
               .AddAzureMonitorTraceExporter(options =>
               {
                   options.ConnectionString = builder.Configuration["ApplicationInsights:ConnectionString"];
               });
    });

builder.Services.AddTickerQ(options =>
{
    options.AddOpenTelemetryInstrumentation();
});

With Zipkin

csharp
builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        tracing.AddSource("TickerQ")
               .AddZipkinExporter(options =>
               {
                   options.Endpoint = new Uri("http://localhost:9411/api/v2/spans");
               });
    });

builder.Services.AddTickerQ(options =>
{
    options.AddOpenTelemetryInstrumentation();
});

With OTLP (OpenTelemetry Protocol)

csharp
builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        tracing.AddSource("TickerQ")
               .AddOtlpExporter(options =>
               {
                   options.Endpoint = new Uri("http://localhost:4317");
               });
    });

builder.Services.AddTickerQ(options =>
{
    options.AddOpenTelemetryInstrumentation();
});

Structured Logging

TickerQ OpenTelemetry integration provides structured logging through ILogger:

Log Output Examples

[INF] TickerQ Job enqueued: TimeTicker - ProcessEmails (123e4567-e89b-12d3-a456-426614174000) from ExecutionTaskHandler
[INF] TickerQ Job completed: ProcessEmails (123e4567-e89b-12d3-a456-426614174000) in 1250ms - Success: True
[ERR] TickerQ Job failed: ProcessEmails (123e4567-e89b-12d3-a456-426614174000) - Retry 1 - Connection timeout
[INF] TickerQ Job completed: ProcessEmails (123e4567-e89b-12d3-a456-426614174000) in 2500ms - Success: False
[WRN] TickerQ Job cancelled: ProcessEmails (123e4567-e89b-12d3-a456-426614174000) - Task was cancelled
[INF] TickerQ Job skipped: ProcessEmails (123e4567-e89b-12d3-a456-426614174000) - Another CronOccurrence is already running!
[INF] TickerQ start seeding data: TimeTicker (production-node-01)
[INF] TickerQ completed seeding data: TimeTicker (production-node-01)

Logging Frameworks

Works with any logging framework that integrates with ILogger:

Serilog

csharp
builder.Host.UseSerilog((context, config) =>
{
    config.WriteTo.Console()
          .WriteTo.File("logs/tickerq-.txt", rollingInterval: RollingInterval.Day)
          .Enrich.FromLogContext();
});

NLog

csharp
builder.Logging.ClearProviders();
builder.Logging.AddNLog();

Application Insights

csharp
builder.Services.AddApplicationInsightsTelemetry();

Parent-Child Relationships

TickerQ maintains trace relationships between parent and child jobs:

tickerq.job.execute.timeticker (parent)
└── tickerq.job.execute.timeticker (child)
    └── tickerq.job.execute.timeticker (grandchild)

Child job traces are linked to parent traces using the tickerq.job.parent_id tag.

Performance Considerations

Minimal Overhead

  • Activities are only created when OpenTelemetry listeners are active
  • Uses structured logging with minimal string allocations
  • No performance impact when tracing is disabled

Conditional Tracing

Tracer automatically handles cases where no listeners are registered:

csharp
// No overhead if no listeners
using var activity = _instrumentation.StartJobActivity("my-job", context);
// Activity will be null if no listeners

Filtering and Sampling

Configure sampling for TickerQ traces:

csharp
builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        tracing.AddSource("TickerQ")
               .SetSampler(new TraceIdRatioBasedSampler(0.1)) // Sample 10% of traces
               .AddJaegerExporter();
    });

Or filter specific jobs:

csharp
tracing.AddSource("TickerQ")
       .AddProcessor(new SimpleActivityExportProcessor(new CustomExporter()))
       .AddJaegerExporter();

Best Practices

1. Correlation with Application Traces

Ensure TickerQ traces are correlated with your application traces:

csharp
// In your job function
using var activity = Activity.Current; // Get current activity
if (activity != null)
{
    activity.SetTag("custom.tag", "value");
}

2. Filter High-Volume Jobs

Consider sampling or filtering for high-frequency jobs:

csharp
// Sample only 1% of high-frequency jobs
tracing.SetSampler(new TraceIdRatioBasedSampler(0.01));

3. Use Structured Logging

Leverage structured logging for better querying:

csharp
_logger.LogInformation(
    "Job {JobId} completed in {ElapsedMs}ms with status {Status}",
    jobId, elapsedMs, status);

4. Monitor Trace Volume

Monitor trace volume in your observability platform to avoid overwhelming your tracing backend.

Requirements

  • .NET 8.0 or later
  • OpenTelemetry 1.7.0 or later
  • TickerQ.Utilities (automatically included)

Next Steps

Built by Albert Kunushevci