LogPipeline.devv2.0

Log Extraction & Pipeline Architect

50 Templates Catalog
Web Servers & Reverse Proxies100% Verified RegexZero-Allocation Web Worker

Caddy Web Server Structured JSON Log Parser

Parse Caddy 2 structured JSON access logs with automatic TLS status, status codes, and duration float metrics. Test pattern matching, inspect named capture groups, and export production-ready parser definitions across Fluent Bit, Vector VRL, Datadog Pipelines, Logstash, and OpenTelemetry.

Live Interactive Debugger & Generator

Matches execute locally in-browser via Web Worker
Interactive Test & Config Generator Sandbox
Extraction Pattern (Grok / PCRE Expression)
Pattern Valid (12 fields)
Detected Fields:levelts:floatloggermsgremote_ipremote_portprotomethodhosturistatus:integerduration:float
Raw Log Stream Sandbox(0/3 matched)
No log lines provided. Paste lines or select a preset above.
No matching lines available to generate JSON output.
Parsed 0/3 lines0 ms (0 μs)
ReDoS Risk: SAFE
AdvertisementActive Viewability 30s

Log Architecture & Structural Overview

The Caddy Web Server Structured JSON Log Parser is an essential telemetry stream within the Web Servers & Reverse Proxies ecosystem. Parse Caddy 2 structured JSON access logs with automatic TLS status, status codes, and duration float metrics.

This schema defines a structure of 12 extracted attributes, including 3 numeric metrics and 9 string dimensions. In production observability architectures, these tokens provide high-cardinality indexing keys for telemetry pipelines before shipping to storage backends such as ClickHouse, Elasticsearch, Amazon S3, or Datadog.

Raw Telemetry Ingestion Profile

A typical raw event line for caddy-structured-json averages 244 bytes across 12 tokens. Modern collectors such as Fluent Bit and Vector require zero-backtracking regular expressions to avoid CPU spikes during traffic surges.

Extracted Field Schema & Data Types

The transpiled Grok pattern extracts the following schema fields from each raw event line. Data collectors cast these values according to the typed mappings below.

Field NameInferred TypeDescription & Collector Semantics
levelstringLog severity level.
tsfloatUnix epoch seconds with millisecond float.
loggerstringSubsystem logger identifier.
msgstringLog message summary.
remote_ipstringClient IP.
remote_portstringClient port.
protostringProtocol negotiated.
methodstringHTTP method.
hoststringHost header.
uristringRequested URI.
statusintegerHTTP status code.
durationfloatExecution duration in seconds.

Common Regex Traps & Production Edge Cases

Engineers frequently encounter ingestion failures or pipeline drops due to subtle variations in real-world event logs. Watch out for these verified pitfalls:

1Caddy 2 logs timestamps as decimal epoch seconds (e.g. 1695734400.123). Multiply by 1000 for milliseconds.
2HTTP/3 protocol appears as 'HTTP/3.0' or 'h3'.

Production Collector Setup & Configurations

Pre-configured parser definitions ready to be dropped into your infrastructure repository.

Fluent Bit (parsers.conf)

Format: regex
# ==============================================================================
# Fluent Bit Parser Configuration (parsers.conf)
# ==============================================================================
[PARSER]
    Name        logpipeline_parser
    Format      regex
    Regex       ^\{"level":"(?<level>\b\w+\b)","ts":(?<ts>(?:(?:(?:[+-]?(?:[0-9]+(?:\.[0-9]+)?|\.[0-9]+))))),"logger":"(?<logger>\S+)","msg":"(?<msg>.*?)","request":\{"remote_ip":"(?<remote_ip>(?:(?:(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,7}:|(?:[0-9A-Fa-f]{1,4}:){1,6}:[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,5}(?::[0-9A-Fa-f]{1,4}){1,2}|(?:[0-9A-Fa-f]{1,4}:){1,4}(?::[0-9A-Fa-f]{1,4}){1,3}|(?:[0-9A-Fa-f]{1,4}:){1,3}(?::[0-9A-Fa-f]{1,4}){1,4}|(?:[0-9A-Fa-f]{1,4}:){1,2}(?::[0-9A-Fa-f]{1,4}){1,5}|[0-9A-Fa-f]{1,4}:(?:(?::[0-9A-Fa-f]{1,4}){1,6})|:(?:(?::[0-9A-Fa-f]{1,4}){1,7}|:)|fe80:(?::[0-9A-Fa-f]{0,4}){0,4}%[0-9a-zA-Z]+|::(?:ffff(?::0{1,4})?:)?(?:(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])\.){3}(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])|(?:[0-9A-Fa-f]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])\.){3}(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])))|(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))))","remote_port":"(?<remote_port>(?:[+-]?(?:[0-9]+)))","proto":"(?<proto>\S+)","method":"(?<method>\b\w+\b)","host":"(?<host>\b(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})(?:\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*(?:\.?|\b))","uri":"(?<uri>\S+)"\},"status":(?<status>(?:[+-]?(?:[0-9]+))),"duration":(?<duration>(?:(?:(?:[+-]?(?:[0-9]+(?:\.[0-9]+)?|\.[0-9]+)))))\}$
    Time_Key    timestamp
    Time_Format %Y-%m-%dT%H:%M:%S%z
    Types       ts:float status:integer duration:float

# ==============================================================================
# Fluent Bit Pipeline Filter (fluent-bit.conf)
# ==============================================================================
[FILTER]
    Name         parser
    Match        *
    Key_Name     log
    Parser       logpipeline_parser
    Reserve_Data On

Vector.dev (Remap VRL)

parse_regex!
# ==============================================================================
# Vector.dev Remap Language (VRL) Transform
# Use inside a 'remap' transform in vector.yaml
# ==============================================================================
.parsed, err = parse_regex(.message, r'^\{"level":"(?<level>\b\w+\b)","ts":(?<ts>(?:(?:(?:[+-]?(?:[0-9]+(?:\.[0-9]+)?|\.[0-9]+))))),"logger":"(?<logger>\S+)","msg":"(?<msg>.*?)","request":\{"remote_ip":"(?<remote_ip>(?:(?:(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,7}:|(?:[0-9A-Fa-f]{1,4}:){1,6}:[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,5}(?::[0-9A-Fa-f]{1,4}){1,2}|(?:[0-9A-Fa-f]{1,4}:){1,4}(?::[0-9A-Fa-f]{1,4}){1,3}|(?:[0-9A-Fa-f]{1,4}:){1,3}(?::[0-9A-Fa-f]{1,4}){1,4}|(?:[0-9A-Fa-f]{1,4}:){1,2}(?::[0-9A-Fa-f]{1,4}){1,5}|[0-9A-Fa-f]{1,4}:(?:(?::[0-9A-Fa-f]{1,4}){1,6})|:(?:(?::[0-9A-Fa-f]{1,4}){1,7}|:)|fe80:(?::[0-9A-Fa-f]{0,4}){0,4}%[0-9a-zA-Z]+|::(?:ffff(?::0{1,4})?:)?(?:(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])\.){3}(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])|(?:[0-9A-Fa-f]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])\.){3}(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])))|(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))))","remote_port":"(?<remote_port>(?:[+-]?(?:[0-9]+)))","proto":"(?<proto>\S+)","method":"(?<method>\b\w+\b)","host":"(?<host>\b(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})(?:\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*(?:\.?|\b))","uri":"(?<uri>\S+)"\},"status":(?<status>(?:[+-]?(?:[0-9]+))),"duration":(?<duration>(?:(?:(?:[+-]?(?:[0-9]+(?:\.[0-9]+)?|\.[0-9]+)))))\}$')

if err == null {
    . = merge(., .parsed)
    del(.parsed)

    # Type coercions
    .ts = to_float!(.ts)
    .status = to_int!(.status)
    .duration = to_float!(.duration)

} else {
    log("LogPipeline parsing warning: " + err, level: "warn")
}

# ==============================================================================
# vector.yaml Pipeline Component
# ==============================================================================
transforms:
  parse_logs:
    type: remap
    inputs: ["source_logs"]
    source: |
      .parsed, err = parse_regex(.message, r'^\{"level":"(?<level>\b\w+\b)","ts":(?<ts>(?:(?:(?:[+-]?(?:[0-9]+(?:\.[0-9]+)?|\.[0-9]+))))),"logger":"(?<logger>\S+)","msg":"(?<msg>.*?)","request":\{"remote_ip":"(?<remote_ip>(?:(?:(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,7}:|(?:[0-9A-Fa-f]{1,4}:){1,6}:[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,5}(?::[0-9A-Fa-f]{1,4}){1,2}|(?:[0-9A-Fa-f]{1,4}:){1,4}(?::[0-9A-Fa-f]{1,4}){1,3}|(?:[0-9A-Fa-f]{1,4}:){1,3}(?::[0-9A-Fa-f]{1,4}){1,4}|(?:[0-9A-Fa-f]{1,4}:){1,2}(?::[0-9A-Fa-f]{1,4}){1,5}|[0-9A-Fa-f]{1,4}:(?:(?::[0-9A-Fa-f]{1,4}){1,6})|:(?:(?::[0-9A-Fa-f]{1,4}){1,7}|:)|fe80:(?::[0-9A-Fa-f]{0,4}){0,4}%[0-9a-zA-Z]+|::(?:ffff(?::0{1,4})?:)?(?:(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])\.){3}(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])|(?:[0-9A-Fa-f]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])\.){3}(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])))|(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))))","remote_port":"(?<remote_port>(?:[+-]?(?:[0-9]+)))","proto":"(?<proto>\S+)","method":"(?<method>\b\w+\b)","host":"(?<host>\b(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})(?:\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*(?:\.?|\b))","uri":"(?<uri>\S+)"\},"status":(?<status>(?:[+-]?(?:[0-9]+))),"duration":(?<duration>(?:(?:(?:[+-]?(?:[0-9]+(?:\.[0-9]+)?|\.[0-9]+)))))\}$')
      if err == null {
        . = merge(., .parsed)
        del(.parsed)
      }

Datadog Log Pipeline Grok Parser

match_rules
# ==============================================================================
# Datadog Log Processing Pipeline Grok Parser
# Navigate to: Logs -> Configuration -> Pipelines -> Add Processor -> Grok Parser
# ==============================================================================

# Match Rule:
rule \{"level":"%{WORD:level}","ts":%{NUMBER:ts},"logger":"%{NOTSPACE:logger}","msg":"%{DATA:msg}","request":\{"remote_ip":"%{IP:remote_ip}","remote_port":"%{INT:remote_port}","proto":"%{NOTSPACE:proto}","method":"%{WORD:method}","host":"%{HOSTNAME:host}","uri":"%{NOTSPACE:uri}"\},"status":%{INT:status},"duration":%{NUMBER:duration}\}

# Complete Datadog Pipeline Processor JSON:
{
  "type": "grok-parser",
  "name": "LogPipeline Grok Parser",
  "is_enabled": true,
  "source": "message",
  "samples": [],
  "grok": {
    "match_rules": "rule \\{\"level\":\"%{WORD:level}\",\"ts\":%{NUMBER:ts},\"logger\":\"%{NOTSPACE:logger}\",\"msg\":\"%{DATA:msg}\",\"request\":\\{\"remote_ip\":\"%{IP:remote_ip}\",\"remote_port\":\"%{INT:remote_port}\",\"proto\":\"%{NOTSPACE:proto}\",\"method\":\"%{WORD:method}\",\"host\":\"%{HOSTNAME:host}\",\"uri\":\"%{NOTSPACE:uri}\"\\},\"status\":%{INT:status},\"duration\":%{NUMBER:duration}\\}",
    "support_rules": ""
  }
}

# Target Fields Created:
# level (string), ts (float), logger (string), msg (string), remote_ip (string), remote_port (string), proto (string), method (string), host (string), uri (string), status (integer), duration (float)

OpenTelemetry Collector (transform processor)

regex_parser
# ==============================================================================
# OpenTelemetry Collector Configuration (otel-collector-config.yaml)
# Option 1: Filelog Receiver with regex_parser Operator
# ==============================================================================
receivers:
  filelog:
    include: [ /var/log/**/*.log ]
    start_at: beginning
    operators:
      - type: regex_parser
        id: logpipeline_regex_parser
        regex: '^\{"level":"(?<level>\b\w+\b)","ts":(?<ts>(?:(?:(?:[+-]?(?:[0-9]+(?:\.[0-9]+)?|\.[0-9]+))))),"logger":"(?<logger>\S+)","msg":"(?<msg>.*?)","request":\{"remote_ip":"(?<remote_ip>(?:(?:(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,7}:|(?:[0-9A-Fa-f]{1,4}:){1,6}:[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,5}(?::[0-9A-Fa-f]{1,4}){1,2}|(?:[0-9A-Fa-f]{1,4}:){1,4}(?::[0-9A-Fa-f]{1,4}){1,3}|(?:[0-9A-Fa-f]{1,4}:){1,3}(?::[0-9A-Fa-f]{1,4}){1,4}|(?:[0-9A-Fa-f]{1,4}:){1,2}(?::[0-9A-Fa-f]{1,4}){1,5}|[0-9A-Fa-f]{1,4}:(?:(?::[0-9A-Fa-f]{1,4}){1,6})|:(?:(?::[0-9A-Fa-f]{1,4}){1,7}|:)|fe80:(?::[0-9A-Fa-f]{0,4}){0,4}%[0-9a-zA-Z]+|::(?:ffff(?::0{1,4})?:)?(?:(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])\.){3}(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])|(?:[0-9A-Fa-f]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])\.){3}(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])))|(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))))","remote_port":"(?<remote_port>(?:[+-]?(?:[0-9]+)))","proto":"(?<proto>\S+)","method":"(?<method>\b\w+\b)","host":"(?<host>\b(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})(?:\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*(?:\.?|\b))","uri":"(?<uri>\S+)"\},"status":(?<status>(?:[+-]?(?:[0-9]+))),"duration":(?<duration>(?:(?:(?:[+-]?(?:[0-9]+(?:\.[0-9]+)?|\.[0-9]+)))))\}$'
        timestamp:
          parse_from: attributes.timestamp
          layout: '%Y-%m-%dT%H:%M:%S%z'

# ==============================================================================
# Option 2: Transform Processor (OTel Transformation Language - OTTL)
# ==============================================================================
processors:
  transform:
    error_mode: ignore
    log_statements:
      - context: log
        statements:
          - merge_maps(attributes, extract_patterns(body, "^\\{\"level\":\"(?<level>\\b\\w+\\b)\",\"ts\":(?<ts>(?:(?:(?:[+-]?(?:[0-9]+(?:\\.[0-9]+)?|\\.[0-9]+))))),\"logger\":\"(?<logger>\\S+)\",\"msg\":\"(?<msg>.*?)\",\"request\":\\{\"remote_ip\":\"(?<remote_ip>(?:(?:(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,7}:|(?:[0-9A-Fa-f]{1,4}:){1,6}:[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,5}(?::[0-9A-Fa-f]{1,4}){1,2}|(?:[0-9A-Fa-f]{1,4}:){1,4}(?::[0-9A-Fa-f]{1,4}){1,3}|(?:[0-9A-Fa-f]{1,4}:){1,3}(?::[0-9A-Fa-f]{1,4}){1,4}|(?:[0-9A-Fa-f]{1,4}:){1,2}(?::[0-9A-Fa-f]{1,4}){1,5}|[0-9A-Fa-f]{1,4}:(?:(?::[0-9A-Fa-f]{1,4}){1,6})|:(?:(?::[0-9A-Fa-f]{1,4}){1,7}|:)|fe80:(?::[0-9A-Fa-f]{0,4}){0,4}%[0-9a-zA-Z]+|::(?:ffff(?::0{1,4})?:)?(?:(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])\\.){3}(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])|(?:[0-9A-Fa-f]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])\\.){3}(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])))|(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))))\",\"remote_port\":\"(?<remote_port>(?:[+-]?(?:[0-9]+)))\",\"proto\":\"(?<proto>\\S+)\",\"method\":\"(?<method>\\b\\w+\\b)\",\"host\":\"(?<host>\\b(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})(?:\\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*(?:\\.?|\\b))\",\"uri\":\"(?<uri>\\S+)\"\\},\"status\":(?<status>(?:[+-]?(?:[0-9]+))),\"duration\":(?<duration>(?:(?:(?:[+-]?(?:[0-9]+(?:\\.[0-9]+)?|\\.[0-9]+)))))\\}$"), "insert")

service:
  pipelines:
    logs:
      receivers: [filelog]
      processors: [transform]
      exporters: [otlp]

Logstash Filter Configuration

filter.grok
# ==============================================================================
# Logstash Pipeline Configuration (/etc/logstash/conf.d/logpipeline.conf)
# ==============================================================================
filter {
  grok {
    match => { "message" => "\{\"level\":\"%{WORD:level}\",\"ts\":%{NUMBER:ts:float},\"logger\":\"%{NOTSPACE:logger}\",\"msg\":\"%{DATA:msg}\",\"request\":\{\"remote_ip\":\"%{IP:remote_ip}\",\"remote_port\":\"%{INT:remote_port}\",\"proto\":\"%{NOTSPACE:proto}\",\"method\":\"%{WORD:method}\",\"host\":\"%{HOSTNAME:host}\",\"uri\":\"%{NOTSPACE:uri}\"\},\"status\":%{INT:status:integer},\"duration\":%{NUMBER:duration:float}\}" }
    tag_on_failure => [ "_grokparsefailure" ]
  }

  date {
    match => [ "timestamp", "ISO8601", "dd/MMM/yyyy:HH:mm:ss Z" ]
    target => "@timestamp"
    remove_field => [ "timestamp" ]
  }
}