# 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:

* [ ] TLS handshake detected (for HTTPS): `"SNI": "domain.com"`
* [ ] Protocol identified: `"l7Protocol": "http1|http2|http3"`
* [ ] TLS probes attached: `"tlsProbeTypesDetected": ["openssl", ...]`
* [ ] Data captured: `"dataEventCount": > 0`
* [ ] Bytes transferred: `"bytesSent": > 0`, `"bytesReceived": > 0`
* [ ] HTTP transaction JSON present with full request/response
* [ ] Connection report shows: `"gotProtocolEvent": true`

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

***

## curl: HTTP/2 over HTTPS

### Complete HTTP Transaction JSON

```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](/appendix/bpf-trace.md) for complete details.

```bash
--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:**

```bash
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:**

```yaml
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

* **BPF Trace Deep Dive**: See [BPF Trace - Advanced Debugging](/appendix/bpf-trace.md) for complete BPF trace field reference
* **Configuration Examples**: See [Configuration Examples](/getting-started/qtap/configuration/configuration-examples.md)
* **Traffic Processing with Plugins**: See [Traffic Processing with Plugins](/getting-started/qtap/configuration/traffic-processing-with-plugins.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.qpoint.io/appendix/healthy-capture-patterns.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
