Event Schema Reference
This schema applies to MTE Relay v5 (MRS and MAR). It is the contract the monitoring integrations on this site build against.
MTE Relay writes JSON lines with zerolog, on two streams:
- stderr: operational logs, such as the startup configuration line, the
per request
"Request completed"line, warnings, and errors. - stdout: structured events, one JSON object per line with a single nested event object.
Container runtimes collect both streams by default. No custom parsing rules are needed in Datadog, New Relic, Loki, CloudWatch Logs Insights, Splunk, or Elastic.
Configuration
| Env var | Default | Effect |
|---|---|---|
LOG_LEVEL | info | Filters both streams. Security events log at info, warn, or error by severity, so high and critical security events survive LOG_LEVEL=warn. |
USE_CONSOLE_LOGS | false | Turns the stderr stream into human readable console lines for local development. Structured events on stdout stay JSON. |
There is no sampling setting: every request produces its performance events.
Common fields
Every line carries time (RFC 3339 with milliseconds, for example
2026-09-29T14:03:07.123Z) and message, and every line except the
perf stats and pprof lines carries level. Operational lines on stderr also
carry caller, the source file and line that wrote the line. Lines the
request handlers write carry correlationId; lines from the state store and
the outbound relay client, and two response path warnings, do not.
A secret that appears in a log line, such as the license key or a
client_id_secret on the startup configuration line, shows its first three
characters followed by asterisks.
SocketX writes the same time format and the same caller field, so one
pipeline can parse both products. Its field names are snake_case where the
relay's operational lines are camelCase; one vocabulary is a later change.
An event line on stdout carries exactly one nested object under one of these
keys: performance_event, security_event, encryption_event, or
error_event. The object has its own correlation_id, which equals the
correlationId of the operational lines for the same request and the
X-MTE-Relay-Request-Id the relay returns to the client. The exception is the
outbound relay client: its relay_<reason> events and its response side
header_mismatch events carry an empty correlation_id.
The relay does not add a service name, product, instance, or version field to
these lines. Attach service and any other tags in your log shipper, as the
vendor guides show. The startup line reports mteVersion, marVersion, and
mrsVersion.
performance_event
request_timing, every request, including pass through requests and errors:correlation_id,request_path,request_method,total_time_ms,status_code(the relay's HTTP status).phase_breakdown, every encrypted request that reached the upstream:correlation_id,request_method,total_time_ms,validation_ms,decryption_ms,proxy_ms,encryption_ms,status_code(the upstream's status),client_id,pair_id,encrypted_data_bytes,decrypted_data_bytes.
On a relay that answers with frames, an encrypted response travels as HTTP
200, so request_timing reports 200 for relayed requests and the relay error
code for relay errors, while phase_breakdown reports the upstream's own
status. On an outbound domain, request_timing reports the status returned to
the calling service, which is the origin's status or the relay's own error
code. Pass through routes report the origin's status.
security_event
Fields: event_type, severity (low, medium, high, critical),
origin, reason, client_id, pair_id, success, correlation_id.
origin is the connection's peer address as ip:port, so behind a load
balancer it is the load balancer's address. TRUSTED_PROXIES does not change
it. Severity maps to the log level: low to info, medium to
warn, high and critical to error.
event_type | Severity | Meaning |
|---|---|---|
auth_invalid_token | low | An invalid or aged token presented at discovery; the relay issued a new client id. SocketX raises the same event. |
pair_invalid_client_id | high, or low when the token only expired | The token was refused at pairing (475). |
keepalive_invalid_client_id | high, or low when the token only expired | The token was refused at keepalive (475). |
invalid_client_id | medium | The token signature failed on an encrypted request (475). |
client_pairs_exhausted | low | The relay holds no pairs for the client, so the client must repair (475). |
pair_not_found | medium | Pair state is missing: expired, poisoned, or never created (470). |
state_restore_failed | high | Decoder state could not be restored. |
replay_rejected | medium | The decoder rejected a replayed or out of order frame (479). |
header_mismatch | high | Encrypted metadata or frame flags disagree with the plaintext envelope (483). |
relay_replay_rejected, relay_header_mismatch | medium | On an outbound domain, the next relay answered 479 or 483. Other next relay errors raise no security event. |
The relay raises no security event when it cannot write pair state. A failed
decoder save on an encrypted request answers 503 state_store_write_failed,
and a failed write at pairing or keepalive answers
503 state_store_unavailable; both show as relayCode 503 on the
"Request completed" line. A failed encoder save after the response has gone
out only logs a Pair poisoned warning, and the next request on that pair gets
470, or 475 if it was the client's last pair.
encryption_event
An audit record for each encrypted request that reached the upstream. The
audit record holds no plaintext. At LOG_LEVEL=debug, stderr lines carry the
decoded request path, and the Proxying to upstream line carries the upstream
URL with its query.
event_type: decryption,direction: inbound_request.
Fields: correlation_id, incoming_protocol, host, origin,
upstream_host, url_payload_hash (SHA-256 of the request path without the
query), url_payload_length, mte_type (MKE or MTE), data_size_bytes,
client_id, pair_id, streaming, success, eeid.
error_event
Fields: event_type, severity, message, reason, error_code,
stack_trace, origin, correlation_id. Current catalog: panic
(critical), a recovered handler panic with its stack trace.
Operational lines worth dashboarding
"Request completed"on stderr, for every request:method,path,statusCode,duration_ms,correlationId, plusupstreamStatuswhen the upstream answered andrelayCodeandrelayReasonwhen the relay answered an error. Echo andHEALTH_CHECK_PATHhits are logged at debug, not info, so a load balancer check does not fill the log.- Perf stats summary lines, only when
ENABLE_PERF_STATS=true.