Monitor HAProxy Metrics and Logs with OpenTelemetry

SigNoz Cloud - This page applies to SigNoz Cloud editions.
Self-Host - This page applies to self-hosted SigNoz editions.

Overview

Monitor HAProxy in SigNoz with the OpenTelemetry Collector. You collect two signals:

  • Metrics: connection rates, session counts, request throughput, and error counters through the Collector haproxy receiver, which polls the HAProxy stats socket.
  • Logs: HTTP traffic logs through the Collector syslog receiver, parsed into structured fields such as status code, backend, server, and request timers.

HAProxy does not write log files. It emits logs over the syslog protocol through its log directive, so the Collector listens for syslog instead of tailing a file.

Prerequisites

Ensure you have:

  • A running HAProxy instance, with access to edit haproxy.cfg
  • An OpenTelemetry Collector that can read the HAProxy stats socket and accept syslog traffic from HAProxy
  • An instance of SigNoz (either Cloud or Self-Hosted)

Collect HAProxy telemetry

The haproxy receiver polls HAProxy through its stats socket and maps the stats counters to OpenTelemetry metrics.

Step 1: Enable the HAProxy stats socket

Add a stats socket line to the global section of your haproxy.cfg:

haproxy.cfg
global
    stats socket /var/run/haproxy.sock mode 660 level admin

Reload HAProxy to apply the change:

sudo systemctl reload haproxy

The Collector needs read access to this socket. Either run it as a user in the socket's group, or adjust mode and the socket's group ownership to grant access.

Step 2: Create the Collector config file

Create a file named haproxy-metrics-collection-config.yaml with the following content:

haproxy-metrics-collection-config.yaml
receivers:
  haproxy:
    # Plain filesystem path to the stats socket, or an http:// stats URL.
    # Do not prefix the socket path with file://, the receiver dials it as-is.
    endpoint: "${env:HAPROXY_STATS_ENDPOINT}"
    collection_interval: 30s

processors:
  # sets service.name so HAProxy metrics are easy to filter in SigNoz
  resource/haproxy:
    attributes:
      - key: service.name
        value: haproxy
        action: upsert
  batch:
    send_batch_size: 10000
    send_batch_max_size: 11000
    timeout: 10s

exporters:
  # On Collector v0.144.0 and newer, use "otlp_http" to avoid a deprecation warning.
  otlphttp/haproxy:
    endpoint: "${env:OTLP_DESTINATION_ENDPOINT}"
    headers:
      "signoz-ingestion-key": "${env:SIGNOZ_INGESTION_KEY}"

service:
  pipelines:
    metrics/haproxy:
      receivers: [haproxy]
      processors: [resource/haproxy, batch]
      exporters: [otlphttp/haproxy]

Step 3: Deploy the Collector

Set the environment variables the config reads:

# HAProxy stats socket, readable by the otel collector
export HAPROXY_STATS_ENDPOINT="/var/run/haproxy.sock"

# region specific SigNoz cloud ingestion endpoint
export OTLP_DESTINATION_ENDPOINT="https://ingest.<region>.signoz.cloud:443"

# your SigNoz ingestion key
export SIGNOZ_INGESTION_KEY="<your-ingestion-key>"

Start the Collector with the config file:

otelcol-contrib --config haproxy-metrics-collection-config.yaml

Verify these values:

Collecting both signals with one Collector

Run one Collector with both receivers rather than two instances. Merge the two configs into a single file: keep both receivers, both pipelines, and one shared exporter and resource processor.

haproxy-collection-config.yaml
receivers:
  haproxy:
    endpoint: "${env:HAPROXY_STATS_ENDPOINT}"
    collection_interval: 30s

  syslog:
    udp:
      listen_address: "0.0.0.0:54527"
    protocol: rfc5424
    operators:
      # Same operators as the Logs tab above.
      - type: regex_parser
        parse_from: attributes.message
        regex: '^(?P<client_ip>\S+):(?P<client_port>\d+) \[(?P<request_date>[^\]]+)\] (?P<frontend_name>\S+) (?P<backend_name>[^/\s]+)/(?P<server_name>\S+) (?P<time_request>-?\d+)/(?P<time_queue>-?\d+)/(?P<time_connect>-?\d+)/(?P<time_response>-?\d+)/(?P<time_total>\+?-?\d+) (?P<status_code>-?\d+) (?P<bytes_read>\+?\d+) (?P<captured_request_cookie>\S+) (?P<captured_response_cookie>\S+) (?P<termination_state>\S+) (?P<actconn>\d+)/(?P<feconn>\d+)/(?P<beconn>\d+)/(?P<srv_conn>\d+)/(?P<retries>\+?\d+) (?P<srv_queue>\d+)/(?P<backend_queue>\d+)(?: \{(?P<captured_request_headers>[^}]*)\} \{(?P<captured_response_headers>[^}]*)\})? "(?P<http_request>[^"]*)"$'
        severity:
          parse_from: attributes.status_code
          overwrite_text: true
          mapping:
            info:
              - "2xx"
              - "3xx"
            warn: "4xx"
            error: "5xx"
        on_error: send
      - type: move
        if: attributes.message != nil
        from: attributes.message
        to: body
      - type: add
        field: attributes.source
        value: haproxy

processors:
  resource/haproxy:
    attributes:
      - key: service.name
        value: haproxy
        action: upsert
  batch:
    send_batch_size: 10000
    send_batch_max_size: 11000
    timeout: 10s

exporters:
  # On Collector v0.144.0 and newer, use "otlp_http" to avoid a deprecation warning.
  otlphttp/haproxy:
    endpoint: "${env:OTLP_DESTINATION_ENDPOINT}"
    headers:
      "signoz-ingestion-key": "${env:SIGNOZ_INGESTION_KEY}"

service:
  pipelines:
    metrics/haproxy:
      receivers: [haproxy]
      processors: [resource/haproxy, batch]
      exporters: [otlphttp/haproxy]
    logs/haproxy:
      receivers: [syslog]
      processors: [resource/haproxy, batch]
      exporters: [otlphttp/haproxy]

Complete Step 1 of both tabs first: enable the stats socket and point HAProxy's log line at this Collector. Then start it with a single --config haproxy-collection-config.yaml.

On Docker, define one otelcol service that mounts this config, joins the shared haproxy-run volume, and publishes 54527/udp. Two separate otelcol services with the same container_name collide, so do not paste both tabs' snippets into the same Compose file.

Validate

After you start the Collector, confirm each signal arrives in SigNoz.

  1. Metrics: Open Metrics Explorer and search for metrics that start with haproxy. (for example haproxy.requests.total and haproxy.sessions.count).
  2. Logs: Open the Logs explorer and add filter service.name = haproxy.
HAProxy metrics in the SigNoz Metrics Explorer
HAProxy metrics in the SigNoz Metrics Explorer
HAProxy HTTP traffic logs in the SigNoz Logs explorer, filtered by service.name = haproxy
HAProxy logs in SigNoz, filtered by service.name = haproxy

Troubleshooting

No metrics arrive

  • Confirm the stats socket exists and the Collector can read it: sudo socat - UNIX-CONNECT:/var/run/haproxy.sock <<< "show stat" should return CSV output.
  • Check the socket's permissions. mode 660 restricts it to the owning user and group, so the Collector process must belong to that group.
  • Confirm HAPROXY_STATS_ENDPOINT is a plain socket path such as /var/run/haproxy.sock. A file:// prefix fails with dial unix file:///...: connect: no such file or directory, because the receiver dials the value as-is. Use http:// only for the stats page.

No logs arrive

  • Confirm HAProxy can reach the Collector on the syslog port. HAProxy fails silently when the log target is unreachable, since syslog over UDP is fire-and-forget.
  • Confirm the receiver's protocol matches the format in the HAProxy log line. An rfc5424 receiver drops RFC3164 messages, and the reverse also fails.
  • Generate traffic through a frontend that has option httplog set. HAProxy logs at the end of each session, so an idle proxy produces no HTTP log lines.
  • In Docker or Kubernetes, confirm you published the syslog port as UDP and that HAProxy targets the Collector's service name.

Log fields are not parsed

  • The regex targets the default option httplog format. A custom log-format breaks it, so update the regex to match your field order.
  • Startup notices and health-check state changes pass through unparsed. Only HTTP log lines match the regex.
  • If long request lines look cut off, raise len on the HAProxy log line.

No data at all

  • Recheck the OTLP_DESTINATION_ENDPOINT region against your SigNoz region. A wrong region drops data with no error.
  • Copy the signoz-ingestion-key value fresh from SigNoz settings.

Limitations

  • Metrics require the stats socket or stats page. The haproxy receiver polls one of them. Without either, it collects nothing.
  • Logs arrive over syslog, not files. HAProxy writes no log files by default, so the Collector needs a reachable syslog listener or a stdout collection path.
  • The parser expects the default option httplog. A custom log-format needs regex changes.
  • Log severity comes from the HTTP status code. 2xx and 3xx map to info, 4xx to warn, and 5xx to error.

Next Steps

Get Help

If you need help with the steps in this topic, please reach out to us on SigNoz Community Slack. If you are a SigNoz Cloud user, please use in product chat support located at the bottom right corner of your SigNoz instance or contact us at cloud-support@signoz.io.

Last updated: July 25, 2026

Edit on GitHub

Was this page helpful?

Your response helps us improve this page.