What a Healthy Capture Looks Like

This guide shows what successful Qtap captures look like for different binaries and scenarios. Use these patterns as a reference when troubleshooting capture issues.

Understanding Healthy Captures

A healthy Qtap capture successfully:

  1. ✅ Detects the TLS handshake (SNI, TLS version)

  2. ✅ Identifies the HTTP protocol (http1, http2, http3)

  3. ✅ Captures process metadata (PID, binary path)

  4. ✅ Parses HTTP request/response headers and body

  5. ✅ Reports accurate byte counts and duration

Key indicator: "l7Protocol": "http1" or "http2" (NOT "other")


Quick Reference: Healthy Capture Checklist

When reviewing Qtap logs, a healthy capture shows:

If ANY of these are missing, investigate further with --bpf-trace.


curl: HTTP/2 over HTTPS

Complete HTTP Transaction JSON

{
    "metadata": {
        "process_id": "160239",
        "process_exe": "/usr/bin/curl",
        "bytes_sent": 41,
        "bytes_received": 395,
        "connection_id": "d3t55gg7p3qr49f8485g",
        "endpoint_id": "httpbin.org"
    },
    "request": {
        "method": "GET",
        "url": "https://httpbin.org/get",
        "scheme": "https",
        "path": "/get",
        "authority": "httpbin.org",
        "protocol": "http2",
        "request_id": "d3t55gg7p3qr49f84870",
        "user_agent": "curl/8.10.1",
        "headers": {
            ":authority": "httpbin.org",
            ":method": "GET",
            ":path": "/get",
            ":scheme": "https",
            "Accept": "*/*",
            "User-Agent": "curl/8.10.1"
        }
    },
    "response": {
        "status": 200,
        "content_type": "application/json",
        "headers": {
            ":status": "200",
            "Access-Control-Allow-Credentials": "true",
            "Access-Control-Allow-Origin": "*",
            "Content-Length": "255",
            "Content-Type": "application/json",
            "Date": "Thu, 23 Oct 2025 16:07:40 GMT",
            "Server": "gunicorn/19.9.0"
        },
        "body": "ewogICJhcmdzIjoge30sIAog..."
    },
    "transaction_time": "2025-10-23T16:07:41.023379679Z",
    "duration_ms": 10731,
    "direction": "egress-external"
}

Debug Log Indicators (curl)

TLS Handshake Detection:

processing tls handshake event
  "exe": "/usr/bin/curl"
  "SNI":"httpbin.org"
  "Version":772     # TLS 1.3
  "ALPNs":["h2","http/1.1"]

Protocol Detection:

processing protocol event
  "exe": "/usr/bin/curl"
  "Protocol":"http2"
  "IsTLS":true

Connection Metadata:

event store submission
  "l7Protocol":"http2"     ✅ NOT "other"
  "tlsProbeTypesDetected":["openssl","openssl",...]
  "tlsVersion":772
  "bytesSent":901
  "bytesReceived":4867

Connection Report:

connection report
  "gotTLSClientHelloEvent": true     ✅
  "gotProtocolEvent": true            ✅
  "dataEventCount": 6                 ✅ Non-zero
  "handler": "raw"
  "strategy": "observe"

wget: HTTP/1.1 over HTTPS

Debug Log Indicators (wget)

TLS Handshake:

processing tls handshake event
  "exe": "/usr/bin/wget"
  "SNI":"httpbin.org"
  "Version":772
  "ALPNs":null     # wget doesn't advertise ALPN

Protocol Detection:

processing protocol event
  "exe": "/usr/bin/wget"
  "Protocol":"http1"     ✅ HTTP/1.1 detected
  "IsTLS":true

HTTP Request Captured:

http request
  "exe": "/usr/bin/wget"
  "protocol": "http1"
  "is_tls": true
  "request": {
    "Method":"GET"
    "RequestURI":"/headers"
    "Proto":"HTTP/1.1"
    "Host":"httpbin.org"
    "Headers": {
      "Accept":["*/*"]
      "User-Agent":["Wget/1.25.0"]
      "Connection":["Keep-Alive"]
    }
  }

HTTP Response Captured:

http response
  "exe": "/usr/bin/wget"
  "protocol": "http1"
  "status_code": 200
  "status": "OK"
  "version": "HTTP/1.1"

Python requests: HTTP/1.1 over HTTPS

Debug Log Indicators (Python)

TLS Handshake:

processing tls handshake event
  "exe": "/usr/local/bin/python3.11"
  "SNI":"httpbin.org"
  "Version":772

Protocol Detection:

processing protocol event
  "exe": "/usr/local/bin/python3.11"
  "Protocol":"http1"
  "IsTLS":true

HTTP Transaction:

http request
  "exe": "/usr/local/bin/python3.11"
  "protocol": "http1"
  "is_tls": true
  "request": {
    "Method":"GET"
    "Host":"httpbin.org"
    "Headers": {
      "User-Agent":["python-requests/2.31.0"]
      "Accept-Encoding":["gzip, deflate"]
    }
  }

Debug Log Patterns

Healthy Capture Sequence

  1. Socket Connection

    socket connect event
      "conn_id": "..."
      "cookie": 12345
      "exe": "/usr/bin/curl"
  2. TLS Detection (for HTTPS)

    processing tls handshake event
      "SNI": "example.com"
      "Version": 772  # TLS 1.3
  3. Protocol Detection

    processing protocol event
      "Protocol": "http2" | "http1"
      "IsTLS": true
  4. HTTP Capture

    object store submission
      "artifact": {
        "type": "http_transaction"
        "contentType": "application/json"
        "bytes": 1237
      }
  5. Connection Finalization

    socket close event
      "write_bytes": 901
      "read_bytes": 4867
    
    connection report
      "gotTLSClientHelloEvent": true
      "gotProtocolEvent": true
      "dataEventCount": 6

Key Fields to Check

✅ Success Indicators

  • "l7Protocol": "http1" or "http2" (NOT "other")

  • "tlsProbeTypesDetected": ["openssl", ...] for HTTPS

  • "gotTLSClientHelloEvent": true for HTTPS

  • "gotProtocolEvent": true

  • "dataEventCount": > 0

  • "bytesSent": > 0 and "bytesReceived": > 0

❌ Failure Indicators

  • "l7Protocol": "other" - HTTP parsing failed

  • "gotProtocolEvent": false - Protocol not detected

  • "dataEventCount": 0 - No data captured

  • "write_bytes": 0, "read_bytes": 0 - No traffic seen


BPF Trace Patterns

When to Use BPF Trace

Use --bpf-trace for deep troubleshooting of capture issues. See BPF Trace - Advanced Debugging for complete details.

--bpf-trace="mod:socket,exe.contains:curl"

What it shows:

  • Individual syscall invocations (read, write, writev)

  • File descriptor (FD) numbers per operation

  • Data transfer sizes per syscall

  • TLS/SSL detection decisions per-FD

Healthy BPF Trace Example (curl)

Connection Metadata

event store submission
  "connectionId": "d3t57dg7p3qra5r14cg0"
  "endpointId": "httpbin.org"
  "tlsProbeTypesDetected": ["openssl","openssl","openssl","openssl","openssl"]
  "l7Protocol": "http2"     ✅
  "tlsVersion": 772
  "socketProtocol": "tcp"
  "bytesSent": 901
  "bytesReceived": 4867

HTTP Transaction Artifact

object store submission
  "artifact": {
    "type": "http_transaction"
    "contentType": "application/json"
    "bytes": 1237
    "connectionId": "d3t57dg7p3qra5r14cg0"
    "endpointId": "httpbin.org"
  }

Troubleshooting: What to Look For

Problem: HTTP Parsing Fails (l7Protocol: "other")

Check:

  1. Was TLS detected? Look for tlsProbeTypesDetected

  2. Was protocol detected? Check gotProtocolEvent: true

  3. Was data captured? Check dataEventCount > 0

Common causes:

  • Non-standard HTTP libraries (custom TLS wrappers)

  • Binary not using OpenSSL/BoringSSL (see tlsProbeTypesDetected)

  • Compression or encryption before TLS layer

Problem: No Capture at All

Check:

  1. Is process filtered? Look for filter matches in debug logs

  2. Is Qtap running? Check for eBPF program loaded and listening

  3. Is traffic direction captured? Verify direction config matches traffic

Debug command:

docker logs qtap-container 2>&1 | grep -E "exe|protocol|l7Protocol"

Problem: Missing Headers or Body

Check capture level:

  • none: No capture

  • summary: Basic info only (no headers/body)

  • details: Headers included (no body)

  • full: Everything (headers + body)

Verify plugin config:

plugins:
  - type: http_capture
    config:
      level: full     # Must be 'full' to see body

Problem: Zero Bytes Captured

Check:

  1. Connection report: write_bytes and read_bytes should be > 0

  2. Socket close event: Should show non-zero byte counts

  3. BPF trace: Look for syscall data transfer events

Common causes:

  • Qtap started after traffic (must start before)

  • Connection reused from before Qtap started

  • Traffic on different network interface


Additional Resources

Last updated