#!/bin/bash # This file is part of the rsyslog project, released under ASL 2.0 ## omotel-trace-correlation.sh -- test trace correlation support for omotel module ## ## Tests that trace_id, span_id, and trace_flags are extracted from message ## properties and included in the OTLP export. Uses mmjsonparse to extract ## trace properties from JSON messages. . ${srcdir:=.}/diag.sh init # Check if omotel module is available require_plugin omotel require_plugin mmjsonparse export NUMMESSAGES=10 export EXTRA_EXIT=otel export SEQ_CHECK_OPTIONS=-d # Download and prepare OTEL Collector download_otel_collector prepare_otel_collector start_otel_collector # Read the port from the port file if [ ! -f ${RSYSLOG_DYNNAME}.otel_port.file ]; then echo "ERROR: OTEL Collector port file not found: ${RSYSLOG_DYNNAME}.otel_port.file" error_exit 1 fi otel_port=$(cat ${RSYSLOG_DYNNAME}.otel_port.file) if [ -z "$otel_port" ]; then echo "ERROR: OTEL Collector port is empty" error_exit 1 fi echo "Using OTEL Collector port: $otel_port" # Create input file with syslog messages containing JSON with trace context cat > ${RSYSLOG_DYNNAME}.input << 'INPUTFILE' <14>1 2024-01-01T12:00:00.000Z testhost testapp 12345 - [test@123] {"trace_id":"4bf92f3577b34da6a3ce929d0e0e4736","span_id":"00f067aa0ba902b7","trace_flags":"01","message":"test message 00000001"} <14>1 2024-01-01T12:00:00.000Z testhost testapp 12345 - [test@123] {"trace_id":"4bf92f3577b34da6a3ce929d0e0e4736","span_id":"00f067aa0ba902b7","trace_flags":"01","message":"test message 00000002"} <14>1 2024-01-01T12:00:00.000Z testhost testapp 12345 - [test@123] {"trace_id":"4bf92f3577b34da6a3ce929d0e0e4736","span_id":"00f067aa0ba902b7","trace_flags":"01","message":"test message 00000003"} <14>1 2024-01-01T12:00:00.000Z testhost testapp 12345 - [test@123] {"trace_id":"4bf92f3577b34da6a3ce929d0e0e4736","span_id":"00f067aa0ba902b7","trace_flags":"01","message":"test message 00000004"} <14>1 2024-01-01T12:00:00.000Z testhost testapp 12345 - [test@123] {"trace_id":"4bf92f3577b34da6a3ce929d0e0e4736","span_id":"00f067aa0ba902b7","trace_flags":"01","message":"test message 00000005"} <14>1 2024-01-01T12:00:00.000Z testhost testapp 12345 - [test@123] {"trace_id":"4bf92f3577b34da6a3ce929d0e0e4736","span_id":"00f067aa0ba902b7","trace_flags":"01","message":"test message 00000006"} <14>1 2024-01-01T12:00:00.000Z testhost testapp 12345 - [test@123] {"trace_id":"4bf92f3577b34da6a3ce929d0e0e4736","span_id":"00f067aa0ba902b7","trace_flags":"01","message":"test message 00000007"} <14>1 2024-01-01T12:00:00.000Z testhost testapp 12345 - [test@123] {"trace_id":"4bf92f3577b34da6a3ce929d0e0e4736","span_id":"00f067aa0ba902b7","trace_flags":"01","message":"test message 00000008"} <14>1 2024-01-01T12:00:00.000Z testhost testapp 12345 - [test@123] {"trace_id":"4bf92f3577b34da6a3ce929d0e0e4736","span_id":"00f067aa0ba902b7","trace_flags":"01","message":"test message 00000009"} <14>1 2024-01-01T12:00:00.000Z testhost testapp 12345 - [test@123] {"trace_id":"4bf92f3577b34da6a3ce929d0e0e4736","span_id":"00f067aa0ba902b7","trace_flags":"01","message":"test message 00000010"} INPUTFILE generate_conf add_conf ' template(name="otlpBody" type="string" string="%msg%") module(load="../plugins/mmjsonparse/.libs/mmjsonparse") module(load="../plugins/omotel/.libs/omotel") # Parse JSON to extract trace properties action(type="mmjsonparse" mode="find-json") # Export with trace correlation action( name="omotel-http" type="omotel" template="otlpBody" endpoint="http://127.0.0.1:'$otel_port'" path="/v1/logs" trace_id.property="trace_id" span_id.property="span_id" trace_flags.property="trace_flags" batch.max_items="100" batch.timeout.ms="1000" ) ' startup injectmsg_file ${RSYSLOG_DYNNAME}.input shutdown_when_empty wait_shutdown # Stop OTEL Collector to ensure file exporter flushes data stop_otel_collector # Give OTEL Collector a moment to flush the output file after shutdown if [ -n "$TESTTOOL_DIR" ] && [ -f "$TESTTOOL_DIR/msleep" ]; then $TESTTOOL_DIR/msleep 1000 else sleep 1 fi # Extract data from OTEL Collector output otel_collector_get_data python3 - "$RSYSLOG_DYNNAME.otel-output.json" <<'PY' import json import sys path = sys.argv[1] try: # OTEL Collector file exporter writes newline-delimited JSON (one JSON object per line) records = [] with open(path, "r", encoding="utf-8") as f: for line in f: line = line.strip() if not line: continue payload = json.loads(line) if "resourceLogs" in payload: for resource_log in payload["resourceLogs"]: if "scopeLogs" in resource_log: for scope_log in resource_log["scopeLogs"]: if "logRecords" in scope_log: records.extend(scope_log["logRecords"]) except Exception as exc: sys.stderr.write(f"omotel-trace-correlation: failed to parse OTLP output: {exc}\n") sys.exit(1) if not records: sys.stderr.write("omotel-trace-correlation: OTLP output did not contain any logRecords\n") sys.exit(1) sys.stdout.write(f"omotel-trace-correlation: found {len(records)} log records in OTLP output\n") # Expected trace values expected_trace_id = "4bf92f3577b34da6a3ce929d0e0e4736" expected_span_id = "00f067aa0ba902b7" expected_trace_flags = 1 sys.stdout.write(f"omotel-trace-correlation: expected traceId='{expected_trace_id}', spanId='{expected_span_id}', flags={expected_trace_flags}\n") # Verify trace correlation fields for idx, record in enumerate(records): trace_id = record.get("traceId") span_id = record.get("spanId") trace_flags = record.get("flags") sys.stdout.write(f"omotel-trace-correlation: record {idx}: traceId={trace_id}, spanId={span_id}, flags={trace_flags}\n") if trace_id is None: sys.stderr.write(f"omotel-trace-correlation: record {idx} missing traceId\n") sys.exit(1) if trace_id != expected_trace_id: sys.stderr.write(f"omotel-trace-correlation: record {idx} traceId mismatch: expected '{expected_trace_id}', got '{trace_id}'\n") sys.exit(1) if span_id is None: sys.stderr.write(f"omotel-trace-correlation: record {idx} missing spanId\n") sys.exit(1) if span_id != expected_span_id: sys.stderr.write(f"omotel-trace-correlation: record {idx} spanId mismatch: expected '{expected_span_id}', got '{span_id}'\n") sys.exit(1) if trace_flags is None: sys.stderr.write(f"omotel-trace-correlation: record {idx} missing flags\n") sys.exit(1) if trace_flags != expected_trace_flags: sys.stderr.write(f"omotel-trace-correlation: record {idx} flags mismatch: expected {expected_trace_flags}, got {trace_flags}\n") sys.exit(1) sys.stdout.write(f"omotel-trace-correlation: verified {len(records)} records with trace correlation\n") PY exit_test