Skip to content
LinkState
Go back

Minimum telemetry to prove the mesh is the outage

Introduction

When a service reports an outage, the first operational question is: Is the failure inside the Envoy sidecar (including the mTLS handshake) or inside the application container? Answering this quickly reduces mean‑time‑to‑identify (MTTI) and prevents wasted effort on the wrong tier. Telemetry that is generated as close to the failure point as possible—Envoy’s access logs, counters, and TLS‑freshness signals—provides the evidence needed to prove or disprove an Envoy‑ or mTLS‑path origin without relying on application‑level instrumentation that may be missing during an outage.


Access‑Log Fields for Telemetry

HTTP Request and Response Fields

Envoy’s access log can be formatted to emit request‑level details that directly map to the request lifecycle:

Field (Envoy % specifier)MeaningWhy it helps isolate Envoy vs. app
%START_TIME%Request receipt timestamp (nanosecond precision)Anchor for correlation windows
%DURATION%Total latency (request received → response sent)High latency may indicate Envoy buffering or TLS handshake delay
%REQ(:method)%HTTP methodConfirms request reached Envoy
%REQ(:path)%Request pathEnables routing‑level filtering
%RESPONSE_CODE%HTTP status code returned by Envoy (or upstream)5xx from Envoy indicates Envoy‑generated error
%UPSTREAM_HOST%Selected upstream cluster endpointShows whether Envoy selected a host
%UPSTREAM_TRANSPORT_FAILURE_REASON%Failure reason if upstream connection failed (e.g., connection_failed, no_healthy_upstream)Direct evidence of Envoy‑side network failure
%DOWNSTREAM_LOCAL_ADDRESS%Listener address Envoy received the connection onUseful for listener‑level debugging
%DOWNSTREAM_REMOTE_ADDRESS%Client addressHelps correlate with client‑side logs
%REQUEST_HEADERS_BYTES% / %RESPONSE_HEADERS_BYTES%Header sizesDetects abnormal header injection or compression issues

TLS Handshake and Certificate Fields

When mTLS is enabled, Envoy can add TLS‑specific fields to the same access‑log line:

Field (Envoy % specifier)MeaningFailure indication
%DOWNSTREAM_TLS_SESSION_ID%TLS session identifierEmpty or rapidly changing may indicate handshake failures
%DOWNSTREAM_TLS_CIPHER%Negotiated cipher suiteTLS_NULL_WITH_NULL_NULL or missing indicates handshake abort
%DOWNSTREAM_TLS_SNI%Server Name Indication presented by clientMismatch with configured SNI can trigger reject
%DOWNSTREAM_LOCAL_CERTIFICATE%PEM‑encoded leaf cert Envoy presentedAbsent → Envoy unable to load cert
%DOWNSTREAM_PEER_CERTIFICATE%PEM‑encoded peer cert presented by clientAbsent → client did not send cert (mTLS failure)
%DOWNSTREAM_PEER_CERT_SUBJECT% / %DOWNSTREAM_PEER_CERT_ISSUER%Subject/Issuer of peer certEnables validation against expected SPIFFE ID
%DOWNSTREAM_PEER_CERT_SERIAL%Serial number of peer certUse for revocation checks
%DOWNSTREAM_PEER_CERT_NOT_BEFORE% / %DOWNSTREAM_PEER_CERT_NOT_AFTER%Validity period of peer certDirect freshness signal
%DOWNSTREAM_TLS_ERROR%TLS error string (if any)Non‑empty → handshake abort reason (e.g., certificate_verify_failed)

Example Access‑Log Configuration for Envoy

A minimal, production‑ready configuration that captures the fields above while keeping cardinality manageable (no per‑request headers or raw bodies). The log is written to /var/log/envoy/access.log and streamed via gRPC to a collector (e.g., Fluent Bit) for real‑time querying.

static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address: { address: 0.0.0.0, port_value: 10000 }
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: backend
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route: { cluster: service_backend }
          http_filters:
          - name: envoy.filters.http.router
          access_log:
          - name: envoy.access_loggers.file
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
              path: /var/log/envoy/access.log
              format: |
                [%START_TIME%] "%REQ(:method)% %REQ(:path)% %PROTOCOL%" \
                %RESPONSE_CODE% %RESPONSE_FLAGS% %DURATION% \
                %UPSTREAM_HOST% %UPSTREAM_TRANSPORT_FAILURE_REASON% \
                %DOWNSTREAM_LOCAL_ADDRESS% %DOWNSTREAM_REMOTE_ADDRESS% \
                %DOWNSTREAM_TLS_SESSION_ID% %DOWNSTREAM_TLS_CIPHER% %DOWNSTREAM_TLS_SNI% \
                %DOWNSTREAM_LOCAL_CERTIFICATE% %DOWNSTREAM_PEER_CERTIFICATE% \
                %DOWNSTREAM_PEER_CERT_SUBJECT% %DOWNSTREAM_PEER_CERT_ISSUER% \
                %DOWNSTREAM_PEER_CERT_SERIAL% %DOWNSTREAM_PEER_CERT_NOT_BEFORE% %DOWNSTREAM_PEER_CERT_NOT_AFTER% \
                %DOWNSTREAM_TLS_ERROR%
          - name: envoy.access_loggers.http_grpc
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.GenericAccessLog
              transport_api_version: V3
              common_config:
                name: envoy_grpc_access_log
              grpc_service:
                envoy_grpc:
                  cluster_name: accesslog_cluster

Note – Envoy does not emit a metric for certificate expiration directly; the expiration must be derived from %DOWNSTREAM_PEER_CERT_NOT_AFTER% (or from SDS). If the access‑log format omits the TLS fields, the mTLS path becomes invisible.


Counter Families for Outage Detection

Request and Response Counters

Envoy exports a hierarchical set of counters under the stats sink (Prometheus, StatsD, etc.). The most relevant families for distinguishing Envoy vs. app failures are:

Counter family (Envoy metric)MeaningInterpretation for outage source
upstream_rq_total{cluster="<name>"}Total requests sent to upstreamBaseline traffic volume
upstream_rq_2xx{cluster="<name>"}2xx responses from upstreamHealthy app path
upstream_rq_5xx{cluster="<name>"}5xx responses from upstreamApp‑generated errors (if Envoy successfully forwarded)
upstream_rq_timeout{cluster="<name>"}Requests timed out waiting for upstreamCould be Envoy timeout or app unresponsiveness; needs correlation with logs
upstream_rq_reset{cluster="<name>"}Requests reset by Envoy (e.g., due to circuit breaker)Envoy‑side back‑pressure or overload
listener_http_downstream_rq_total{listener="<name>"}Total downstream requests receivedConfirms Envoy received traffic
listener_http_downstream_rq_5xx{listener="<name>"}5xx responses generated by Envoy (e.g., due to filter rejection)Direct Envoy failure
server_listener_downstream_cx_total{listener="<name>"}Total downstream connectionsConnection‑level load
server_listener_downstream_cx_rx_bytes_total{listener="<name>"}Bytes received on downstream sideDetects zero‑byte connections (possible TLS handshake stall)
tls_handshake_total{listener="<name>"}TLS handshakes attemptedBaseline for mTLS load
tls_handshake_failed{listener="<name>"}TLS handshakes that failedKey signal: rise indicates mTLS path problem
tls_handshake_success{listener="<name>"}Successful TLS handshakesComplement to failed
ssl_connection_error_total{listener="<name>"}SSL errors (alerts, protocol violations)Granular TLS error classification

Error and Failure Counters

Beyond the request families, Envoy provides error counters that surface internal processing problems:

CounterMeaningRelevance
http_conn_manager_total{code="503"}503 responses from HTTP connection manager (often due to no healthy upstream)Envoy‑generated 503 → Envoy or upstream health
http_conn_manager_total{code="502"}502 responses (bad gateway)Envoy received invalid response from upstream
cluster_upstream_cx_connect_fail_total{cluster="<name>"}Failed TCP connections to upstream hostsNetwork or Envoy‑side connection pool exhaustion
cluster_upstream_cx_rx_bytes_total{cluster="<name>"}Bytes received from upstreamZero bytes while requests are sent → blackhole
listener_udp_listener_datagram_rx_errors_total{listener="<name>"}UDP datagram receive errorsNot relevant for HTTP/mTLS but useful for sidecar UDP telemetry

Example Counter Configuration for Envoy

Envoy exposes these counters automatically when a stats_sink is configured. A minimal Prometheus scrape setup using the StatsD sink (replace with Prometheus exporter as needed):

admin:
  access_log_path: /tmp/admin_access.log
  address:
    socket_address: { address: 0.0.0.0, port_value: 9901 }

stats_sinks:
- name: envoy.metrics
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.statsd.v3.StatsdSink
    tcp_cluster_name: statsd_cluster
    prefix: envoy

If Prometheus is used via the envoy-prometheus exporter, the same counters appear under the envoy namespace.

Note – Envoy does not expose a counter for “certificate expired” or “certificate about to expire”. Those must be derived from the access‑log TLS fields or from an external secret‑management health check.


Certificate Freshness Signals

Certificate Expiration and Renewal Monitoring

The mTLS path is healthy only if both sides present a valid, non‑expired certificate. Envoy provides the peer certificate’s notAfter timestamp via the access‑log field %DOWNSTREAM_PEER_CERT_NOT_AFTER%. To turn this into a usable signal:

  1. Parse the ISO‑8601 timestamp from each access‑log line (or from a periodic SDS dump).
  2. Compute time_to_expiry = notAfter - now.
  3. Emit a gauge (e.g., mtls_peer_cert_time_to_expiry_seconds) to a monitoring system.

If the gauge drops below a threshold (e.g., 86 400 s for 1 day), an alert fires indicating an impending cert expiry.

Certificate Validation and Verification Signals

Beyond expiration, validation failures are captured by:

Example Certificate Freshness Signal Configuration

Assuming a sidecar Fluent Bit agent tails Envoy’s access log and emits metrics to Prometheus via the prometheus_exporter output:

[INPUT]
    Name tail
    Path /var/log/envoy/access.log
    Parser envoy_access_log
    Tag envoy.access

[PARSER]
    Name   envoy_access_log
    Format regex
    Regex  ^\[(?<start_time>[^\]]+)\] "(?<method>[^ ]+) (?<path>[^ ]+) (?<proto>[^ ]+)" (?<status>\d+) (?<flags>\S+) (?<duration>\d+) (?<upstream_host>\S+) (?<upstream_fail>\S+) (?<downstream_local>\S+) (?<downstream_remote>\S+) (?<tls_session_id>\S+) (?<tls_cipher>\S+) (?<tls_sni>\S+) (?<local_cert>\S+) (?<peer_cert>\S+) (?<peer_subject>\S+) (?<peer_issuer>\S+) (?<peer_serial>\S+) (?<not_before>\S+) (?<not_after>\S+) (?<tls_error>\S+)$
    Time_Key    start_time
    Time_Format %Y-%m-%dT%H:%M:%S.%L%z

[OUTPUT]
    Name            prometheus_exporter
    Match           envoy.access
    Listen          0.0.0.0
    Port            2020
    Metrics         mtls_peer_cert_time_to_expiry_seconds
    # Add additional metric definitions as needed

The parser extracts the not_after field; a downstream Fluent Bit filter (or a Prometheus exporter script) can compute the time‑to‑expiry gauge and expose it for alerting.


By combining these access‑log fields, counter families, and certificate‑freshness signals within a tight correlation window (e.g., 5‑second buckets), operators can quickly determine whether an outage originates in Envoy or the mTLS path, independent of application logs or traces that may be missing during a failure.


Share this post on:

Previous Post
How much reordering can Linux TCP actually absorb
Next Post
Forward plugin concurrency under retry storms