design/envoy-json-logging.md
Status: Draft
This proposal is a design for adding to Contour the ability to configure Envoy to output JSON logs. It is intended to allow some customisability of the JSON output while providing sensible defaults.
This feature was requested some time ago in #624. Since that time, two changes have come to Contour that assist with this design:
Contour is intended to be an opinionated piece of software, that sets defaults that you probably don't want to touch. Until recently, we did not have a good way to allow the required customization of this feature while still providing good defaults. Now that we have the configuration file available, it is the logical place for this configuration to sit.
This proposal will add three things to Contour:
The translation table is present because of Contour's overarching design goal: We want to ensure that you cannot create an invalid Envoy configuration. The translation table is to ensure that the log template fields are parseable Envoy config. Allowing the direct specification of Envoy template config is very risky, mistakes there will crash your Envoy deployment.
A new stanza will be added to the config file:
logging:
A new struct will be created to hold this stanza, LoggingConfig.
The logging config will look like this:
logging:
format: json
json-fields:
- protocol
- duration
- request_method
- ...more fields
- @timestamp
formatThere are two valid options here: json and envoy.
envoy will use Envoy's default format.
Having only format: json present will set the Envoy logs to JSON format, with the default fields specified in the json-fields section.
Having format: json with custom json-fields will set the logs to only those fields. If you want to not log the HTTP method, that's on you.
json-fieldsjson-fields is an optional field, and has no effect if logging.format is not json.
By default, json-fields is set as follows:
logging:
format: json
json-fields:
- @timestamp
- downstream_remote_address
- path
- authority
- protocol
- upstream_service_time
- upstream_local_address
- duration
- downstream_local_address
- user_agent
- response_code
- response_flags
- method
- request_id
- upstream_host
- client_ip
- requested_server_name
- bytes_received
- bytes_sent
- upstream_cluster
- x-forwarded-for
Users can specify a subset of these fields, and Envoy will be configured to only log the specified fields.
See the Field Translations section for the exact Envoy spec the fields translate to.
The initial canonical field translations will be as follows:
var translations := map[string]string{
"@timestamp": "%START_TIME%",
"authority": "%REQ(:AUTHORITY)%",
"bytes_received": "%BYTES_RECEIVED%",
"bytes_sent": "%BYTES_SENT%",
"downstream_local_address": "%DOWNSTREAM_LOCAL_ADDRESS%",
"downstream_remote_address": "%DOWNSTREAM_REMOTE_ADDRESS%",
"duration": "%DURATION%",
"method": "%REQ(:METHOD)%",
"path": "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%",
"protocol": "%PROTOCOL%",
"request_id": "%REQ(X-REQUEST-ID)%",
"requested_server_name": "%REQUESTED_SERVER_NAME%",
"response_code": "%RESPONSE_CODE%",
"response_flags": "%RESPONSE_FLAGS%",
"uber_trace_id": "%REQ(UBER-TRACE-ID)%",
"upstream_cluster": "%UPSTREAM_CLUSTER%",
"upstream_host": "%UPSTREAM_HOST%",
"upstream_local_address": "%UPSTREAM_LOCAL_ADDRESS%",
"upstream_service_time": "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%",
"user_agent": "%REQ(USER-AGENT)%",
"x_forwarded_for": "%REQ(X-FORWARDED-FOR)%",
"client_ip": "%REQ(X-ENVOY-EXTERNAL-ADDRESS)%",
}