Skip to content
cccskills
FreePublished on GitHub

infra-opentelemetry-instrumentation

OpenTelemetry instrumentation for data platforms — Python auto-instrumentation (opentelemetry-bootstrap), manual span creation for ETL tasks, trace context propagation across Airflow→Spark→dbt, OTLP exporter configuration, W3C TraceContext headers in Kafka messages, resource attributes (service.name/environment/version), Baggage for partition metadata propagation, Collector pipeline (tail sampling), correlation between traces/logs/metrics, Grafana Tempo integration

The English summary is being prepared. Showing the original description.

ivanshamaev/de-agent-skills22 stars(GitHub 22 + cccskills 0)

Run score Not tested

See how to install

Files included (1)

  • SKILL.md9.9 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

OpenTelemetry Instrumentation

When to Use

  • Tracing end-to-end latency through a multi-component data pipeline
  • Correlating Airflow task execution with Spark job spans
  • Understanding where time is spent in a slow ETL pipeline
  • Adding observability to custom Python ETL code
  • Propagating context (partition date, run ID) across service boundaries

Python Auto-Instrumentation (Zero-Code)

# Install OTel distro + auto-detection
pip install opentelemetry-distro opentelemetry-exporter-otlp

# Auto-detect and install instrumentation for installed packages
opentelemetry-bootstrap -a install
# Installs: opentelemetry-instrumentation-requests, sqlalchemy, psycopg2, kafka-python, etc.
# Run with auto-instrumentation
OTEL_SERVICE_NAME=etl-orders \
OTEL_RESOURCE_ATTRIBUTES="environment=production,team=data-engineering,version=1.2.0" \
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317 \
OTEL_EXPORTER_OTLP_PROTOCOL=grpc \
OTEL_TRACES_EXPORTER=otlp \
OTEL_METRICS_EXPORTER=otlp \
OTEL_LOGS_EXPORTER=otlp \
opentelemetry-instrument python etl_orders.py

Manual Span Creation for ETL Tasks

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource

# Initialize tracer (once per service)
resource = Resource.create({
    "service.name": "etl-orders",
    "service.version": "1.2.0",
    "deployment.environment": "production",
    "team": "data-engineering",
})

provider = TracerProvider(resource=resource)
provider.add_span_processor(
    BatchSpanProcessor(OTLPSpanExporter(endpoint="http://otel-collector:4317"))
)
trace.set_tracer_provider(provider)

tracer = trace.get_tracer("etl.orders")


def run_etl(partition_date: str):
    with tracer.start_as_current_span("etl.run", kind=trace.SpanKind.INTERNAL) as root_span:
        root_span.set_attributes({
            "partition.date": partition_date,
            "pipeline.name": "etl-orders",
        })

        # Sub-span for extraction
        with tracer.start_as_current_span("etl.extract") as extract_span:
            rows = extract_orders(partition_date)
            extract_span.set_attribute("rows.extracted", len(rows))

        # Sub-span for transformation
        with tracer.start_as_current_span("etl.transform") as transform_span:
            transformed = transform_orders(rows)
            transform_span.set_attribute("rows.output", len(transformed))

        # Sub-span for load
        with tracer.start_as_current_span("etl.load") as load_span:
            loaded = load_orders(transformed, partition_date)
            load_span.set_attributes({
                "rows.loaded": loaded,
                "target.table": "silver.orders",
            })

Context Propagation in Airflow Tasks

from opentelemetry import trace, propagate
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator

tracer = trace.get_tracer("airflow.etl")

@task
def extract_task(ds: str, **context) -> dict:
    # Start span, inject context into XCom for downstream tasks
    with tracer.start_as_current_span("airflow.extract") as span:
        span.set_attribute("partition.date", ds)
        span.set_attribute("dag.run_id", context["run_id"])

        result = extract_orders(ds)

        # Propagate trace context to next task via XCom
        carrier = {}
        propagate.inject(carrier)   # injects traceparent header
        return {"data": result, "otel_context": carrier}


@task
def transform_task(extract_result: dict, ds: str) -> dict:
    # Extract and restore trace context from upstream task
    carrier = extract_result.get("otel_context", {})
    ctx = propagate.extract(carrier)

    with tracer.start_as_current_span("airflow.transform", context=ctx) as span:
        span.set_attribute("partition.date", ds)
        transformed = transform_orders(extract_result["data"])
        span.set_attribute("rows.processed", len(transformed))

        carrier = {}
        propagate.inject(carrier)
        return {"data": transformed, "otel_context": carrier}

Context Propagation in Kafka

from opentelemetry import trace, propagate
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
from confluent_kafka import Producer, Consumer, KafkaMessage

tracer = trace.get_tracer("kafka.producer")

def produce_with_trace(topic: str, key: str, value: bytes, partition_date: str):
    with tracer.start_as_current_span("kafka.send") as span:
        span.set_attributes({
            "messaging.system": "kafka",
            "messaging.destination": topic,
            "messaging.message_key": key,
            "partition.date": partition_date,
        })

        # Inject trace context into Kafka message headers
        headers = {}
        propagate.inject(headers)

        producer = Producer({"bootstrap.servers": "kafka:9092"})
        producer.produce(
            topic=topic,
            key=key,
            value=value,
            headers=[(k, v.encode()) for k, v in headers.items()],
        )
        producer.flush()


def consume_with_trace(msg: KafkaMessage):
    # Restore trace context from Kafka message headers
    headers = {k: v.decode() for k, v in (msg.headers() or [])}
    ctx = propagate.extract(headers)

    with tracer.start_as_current_span("kafka.receive", context=ctx) as span:
        span.set_attributes({
            "messaging.system": "kafka",
            "messaging.source": msg.topic(),
            "messaging.partition": msg.partition(),
        })
        process_message(msg.value())

Baggage for Partition Metadata

from opentelemetry import baggage, context

# Producer: add partition metadata to baggage
ctx = baggage.set_baggage("partition.date", "2024-01-15")
ctx = baggage.set_baggage("pipeline.run_id", "manual__2024-01-15T02:00:00+00:00", context=ctx)
token = context.attach(ctx)

try:
    run_pipeline()
finally:
    context.detach(token)

# Anywhere downstream: read baggage
partition_date = baggage.get_baggage("partition.date")
run_id = baggage.get_baggage("pipeline.run_id")

OTel Collector — Tail Sampling

# Tail sampling: keep only traces with errors or slow operations
processors:
  tail_sampling:
    decision_wait: 30s
    num_traces: 50000
    expected_new_traces_per_sec: 100
    policies:
      # Always keep error traces
      - name: errors-policy
        type: status_code
        status_code: {status_codes: [ERROR]}

      # Keep slow traces (> 5 seconds)
      - name: slow-traces-policy
        type: latency
        latency: {threshold_ms: 5000}

      # Sample 10% of healthy fast traces
      - name: probabilistic-policy
        type: probabilistic
        probabilistic: {sampling_percentage: 10}

Trace-to-Log Correlation

import logging
from opentelemetry import trace

class OtelLogHandler(logging.Handler):
    """Inject trace_id and span_id into every log record."""
    def emit(self, record):
        span = trace.get_current_span()
        if span.is_recording():
            ctx = span.get_span_context()
            record.trace_id = format(ctx.trace_id, "032x")
            record.span_id  = format(ctx.span_id, "016x")
        else:
            record.trace_id = "0" * 32
            record.span_id  = "0" * 16

# Configure logging
logging.basicConfig(
    format='{"time": "%(asctime)s", "level": "%(levelname)s", '
           '"trace_id": "%(trace_id)s", "span_id": "%(span_id)s", '
           '"message": "%(message)s"}',
    handlers=[OtelLogHandler()]
)

Instrumentation Checklist

[ ] opentelemetry-distro installed and bootstrap run
[ ] OTEL_SERVICE_NAME set (service.name, not default "unknown_service")
[ ] OTEL_RESOURCE_ATTRIBUTES includes environment and team
[ ] OTLP exporter pointing to OTel Collector (not directly to backend)
[ ] Manual spans on critical ETL operations (extract/transform/load)
[ ] span.set_attribute() with row counts and partition dates
[ ] Context propagated via XCom in Airflow task chains
[ ] Kafka message headers carry traceparent/tracestate
[ ] trace_id injected into log records for correlation
[ ] Tail sampling configured (don't store 100% of healthy traces)
[ ] Grafana Tempo datasource linked to Loki for log correlation

Anti-Patterns

  1. Service name = "unknown_service" — all traces aggregated under one name; always set OTEL_SERVICE_NAME uniquely per component.
  2. Exporting directly from app to Tempo/Jaeger — bypasses sampling and processing; always route through OTel Collector.
  3. No context propagation between Airflow tasks — each task starts a new disconnected trace; inject context via XCom to link the full pipeline trace.
  4. Storing 100% of traces without sampling — trace storage costs scale with throughput; use tail sampling to keep errors and slow traces.
  5. No attributes on spans — spans without attributes (rows processed, partition date, table name) are useless for debugging; annotate spans with business-relevant data.

References

  • OTel Python: opentelemetry.io/docs/languages/python/
  • OTel Collector: opentelemetry.io/docs/collector/
  • Context propagation: opentelemetry.io/docs/concepts/context-propagation/
  • Grafana Tempo: grafana.com/docs/tempo/latest/
  • Related skills: [[infra-observability-stack-review]], [[dataops-airflow-observability]], [[de-production-readiness]]

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

AIOps autonomous incident response agent — LLM-driven diagnosis loop (Claude tool-use agent with kubectl/SQL/Prometheus tools), alert-to-action pipeline (PagerDuty webhook → agent trigger), automated RCA generation (failure taxonomy + log correlation), self-healing action executor with approval gate, incident severity classification, runbook automation (structured YAML runbooks executed by agent), escalation logic, incident timeline auto-generation, Slack integration for human-in-the-loop approvals

The English summary is being prepared. Showing the original description.

ivanshamaev/de-agent-skills22Updated May 20, 2026

AIOps capacity planning agent — predictive resource forecasting (Prophet/linear regression on Prometheus metrics), Kubernetes HPA/VPA right-sizing recommendations, node capacity headroom calculator, Kafka partition and broker capacity model, Spark executor sizing from job history, data lake storage growth forecast (S3/GCS), cluster autoscaler simulation, automatic HPA threshold tuning, cost-per-job attribution, capacity planning report generator

The English summary is being prepared. Showing the original description.

ivanshamaev/de-agent-skills22Updated May 20, 2026

AIOps infrastructure anomaly detection — statistical baseline (z-score/IQR/seasonal decomposition), Prometheus anomaly rules (predict_linear/stddev_over_time/MAD), ML-based detection (Isolation Forest/Prophet/LSTM autoencoder on metrics), metric correlation clustering, automated alert threshold tuning from historical data, Kubernetes resource anomaly detection, data pipeline anomaly signals (row count z-score/freshness drift/throughput drop), Python sklearn/Prophet integration, Grafana anomaly panel

The English summary is being prepared. Showing the original description.

ivanshamaev/de-agent-skills22Updated May 20, 2026

AIOps observability copilot — natural language to PromQL/LogQL translation, alert explanation in plain English (what fired/why/impact), Grafana dashboard auto-generation from service topology, anomaly narrative generation from metric patterns, on-call context enrichment (related alerts/recent deploys/runbook links), log pattern clustering for noise reduction, SLO status explanation, automated weekly observability health digest

The English summary is being prepared. Showing the original description.

ivanshamaev/de-agent-skills22Updated May 20, 2026

AIOps continuous platform optimization agent — autonomous optimization loop (observe→analyze→recommend→apply), Kubernetes resource rightsizing (VPA recommendation reader + apply), Kafka consumer lag auto-scale, Spark job configuration tuning from history, idle resource detection (unused deployments/PVCs/topics), compaction and VACUUM scheduling, auto-ANALYZE stale statistics, cost savings attribution, optimization audit trail, human approval gate for high-risk changes

The English summary is being prepared. Showing the original description.

ivanshamaev/de-agent-skills22Updated May 20, 2026

AIOps autonomous query cost analyzer — Trino system.runtime.query_history cost SQL (CPU time/peak memory/bytes scanned), ClickHouse system.query_log top expensive queries, Spark UI cost attribution by user/job/DAG, BigQuery INFORMATION_SCHEMA.JOBS cost analysis, LLM-driven query rewrite recommendations (missing partition filters/broadcast hints/aggregation pushdown), cost anomaly detection (user over daily budget), automated query tagging, chargeback report by team/project

The English summary is being prepared. Showing the original description.

ivanshamaev/de-agent-skills22Updated May 20, 2026

See all skills by ivanshamaev

Report a problem with this skill