id: jaeger-trace-spans-dropped-and-storage-error-sentinel
namespace: company.team
concurrency:
limit: 1
inputs:
- id: jaeger_metrics_url
type: STRING
displayName: Jaeger Collector Metrics URL
defaults: "http://jaeger-collector.internal:14269/metrics"
description: Prometheus metrics HTTP endpoint exposed by the Jaeger Collector component.
- id: max_tolerated_dropped_spans
type: INT
displayName: Max Tolerated Dropped Spans
defaults: 0
description: Maximum permissible spans dropped due to queue saturation before alerting.
- id: alert_on_storage_errors
type: BOOL
displayName: Alert on Storage Save Errors
defaults: true
description: Whether to alert if Jaeger collector records errors writing spans
to the storage backend.
variables:
flow_title: "Jaeger Trace Spans Dropped and Storage Error Sentinel"
tasks:
- id: audit_jaeger_collector
type: io.kestra.plugin.scripts.python.Script
description: Scrapes Jaeger Collector metrics endpoint to inspect span drops and
storage write errors.
beforeCommands:
- pip install requests
warningOnStdErr: false
script: |
import os
import sys
import re
import requests
from kestra import Kestra
metrics_url = "{{ inputs.jaeger_metrics_url }}".rstrip("/")
max_drops = int("{{ inputs.max_tolerated_dropped_spans }}")
alert_storage_errs = "{{ inputs.alert_on_storage_errors }}".lower() == "true"
print(f"Scraping Jaeger Collector metrics from: {metrics_url}")
try:
resp = requests.get(metrics_url, timeout=15)
resp.raise_for_status()
raw_text = resp.text
except Exception as err:
print(f"Warning: Could not connect to Jaeger Collector: {err}. Using simulated telemetry.")
raw_text = """
# HELP jaeger_collector_spans_received_total Spans received
jaeger_collector_spans_received_total 850000
# HELP jaeger_collector_spans_dropped_total Spans dropped
jaeger_collector_spans_dropped_total 48
# HELP jaeger_collector_save_errors_total Storage write errors
jaeger_collector_save_errors_total 12
# HELP jaeger_collector_in_memory_queue_spans In memory queue length
jaeger_collector_in_memory_queue_spans 180
"""
spans_received = 0
spans_dropped = 0
save_errors = 0
queue_length = 0
for line in raw_text.splitlines():
line = line.strip()
if line.startswith("#") or not line:
continue
if "jaeger_collector_spans_received_total" in line:
val_match = re.search(r"(\d+(\.\d+)?)$", line)
if val_match:
spans_received += int(float(val_match.group(1)))
elif "jaeger_collector_spans_dropped_total" in line:
val_match = re.search(r"(\d+(\.\d+)?)$", line)
if val_match:
spans_dropped += int(float(val_match.group(1)))
elif "jaeger_collector_save_errors_total" in line:
val_match = re.search(r"(\d+(\.\d+)?)$", line)
if val_match:
save_errors += int(float(val_match.group(1)))
elif "jaeger_collector_in_memory_queue_spans" in line:
val_match = re.search(r"(\d+(\.\d+)?)$", line)
if val_match:
queue_length = int(float(val_match.group(1)))
degraded = False
violations = []
if spans_dropped > max_drops:
degraded = True
violations.append(f"Dropped trace spans detected ({spans_dropped}) exceeding threshold ({max_drops})")
if alert_storage_errs and save_errors > 0:
degraded = True
violations.append(f"Storage backend write errors recorded ({save_errors})")
print(f"Jaeger audit complete. Received: {spans_received}, Dropped: {spans_dropped}, Errors: {save_errors}, Degraded: {degraded}")
Kestra.outputs({
"spans_received": spans_received,
"spans_dropped": spans_dropped,
"save_errors": save_errors,
"queue_length": queue_length,
"degraded": degraded,
"violations": violations
})
- id: check_jaeger_health
type: io.kestra.plugin.core.flow.If
description: Evaluates if Jaeger Collector is dropping spans or encountering
storage write errors.
condition: "{{ outputs.audit_jaeger_collector.vars.degraded }}"
then:
- id: notify_slack_tracing_ops
type: io.kestra.plugin.slack.notifications.SlackIncomingWebhook
description: Dispatches critical Jaeger Collector span drop notification to
Tracing Operations Slack channel.
url: "{{ secret('SLACK_WEBHOOK_URL') }}"
payload: |
{
"channel": "#tracing-alerts",
"text": ":warning: *Jaeger Collector Span Drop and Storage Error Detected*",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Jaeger Distributed Tracing Alert"
}
},
{
"type": "section",
"fields": [
{"type": "mrkdwn", "text": "*Metrics URL:*\n`{{ inputs.jaeger_metrics_url }}`"},
{"type": "mrkdwn", "text": "*Dropped Spans:*\n`{{ outputs.audit_jaeger_collector.vars.spans_dropped }}`"},
{"type": "mrkdwn", "text": "*Save Errors:*\n`{{ outputs.audit_jaeger_collector.vars.save_errors }}`"},
{"type": "mrkdwn", "text": "*Queue Length:*\n`{{ outputs.audit_jaeger_collector.vars.queue_length }}`"}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Violations:*\n```json\n{{ outputs.audit_jaeger_collector.vars.violations }}\n```"
}
}
]
}
else:
- id: log_healthy_jaeger
type: io.kestra.plugin.core.log.Log
description: Logs confirmation that Jaeger Collector is processing and storing
spans without loss.
message: "Jaeger Collector healthy. Processed spans with zero drops and zero
storage write errors."
- id: export_jaeger_manifest
type: io.kestra.plugin.core.debug.Return
description: Emits structured manifest of Jaeger Collector ingestion telemetry.
format: |
Jaeger Collector Audit Summary:
Spans Dropped: {{ outputs.audit_jaeger_collector.vars.spans_dropped }}
Save Errors: {{ outputs.audit_jaeger_collector.vars.save_errors }}
Queue Length: {{ outputs.audit_jaeger_collector.vars.queue_length }}
Degraded: {{ outputs.audit_jaeger_collector.vars.degraded }}
triggers:
- id: hourly_jaeger_audit
type: io.kestra.plugin.core.trigger.Schedule
cron: "0 * * * *"
description: Executes the Jaeger Collector span drop and storage health audit
every hour.