diff --git a/docs/usage/configuration/config_documentation.md b/docs/usage/configuration/config_documentation.md index cd546041b2d4..08356d79c1cf 100644 --- a/docs/usage/configuration/config_documentation.md +++ b/docs/usage/configuration/config_documentation.md @@ -436,6 +436,12 @@ Sub-options for each listener include: * `x_forwarded`: Only valid for an 'http' listener. Set to true to use the X-Forwarded-For header as the client IP. Useful when Synapse is behind a reverse-proxy. +* `request_id_header`: The header used as the basis for the request ID which + is used in logs and tracing to correlate requests. Useful when Synapse is + behind a reverse-proxy. For example, if you use Cloudflare in front of + Synapse, you can set this to `"cf-ray"` to match up requests even when the + Cloudflare layer times out before Synapse is done. + * `resources`: Only valid for an 'http' listener. A list of resources to host on this port. Sub-options for each resource are: diff --git a/synapse/config/server.py b/synapse/config/server.py index c91df636d9a7..813ea91e0ac6 100644 --- a/synapse/config/server.py +++ b/synapse/config/server.py @@ -206,6 +206,7 @@ class HttpListenerConfig: resources: List[HttpResourceConfig] = attr.Factory(list) additional_resources: Dict[str, dict] = attr.Factory(dict) tag: Optional[str] = None + request_id_header: Optional[str] = None @attr.s(slots=True, frozen=True, auto_attribs=True) @@ -887,7 +888,7 @@ def read_gc_thresholds( ) -def parse_listener_def(num: int, listener: Any) -> ListenerConfig: +def parse_listener_def(num: int, listener: JsonDict) -> ListenerConfig: """parse a listener config from the config file""" listener_type = listener["type"] # Raise a helpful error if direct TCP replication is still configured. @@ -928,6 +929,7 @@ def parse_listener_def(num: int, listener: Any) -> ListenerConfig: resources=resources, additional_resources=listener.get("additional_resources", {}), tag=listener.get("tag"), + request_id_header=listener.get("request_id_header"), ) return ListenerConfig(port, bind_addresses, listener_type, tls, http_config) diff --git a/synapse/http/site.py b/synapse/http/site.py index 1155f3f61032..6014867685a9 100644 --- a/synapse/http/site.py +++ b/synapse/http/site.py @@ -72,10 +72,12 @@ def __init__( site: "SynapseSite", *args: Any, max_request_body_size: int = 1024, + request_id_header: Optional[str], **kw: Any, ): super().__init__(channel, *args, **kw) self._max_request_body_size = max_request_body_size + self.request_id_header = request_id_header self.synapse_site = site self.reactor = site.reactor self._channel = channel # this is used by the tests @@ -172,7 +174,13 @@ def set_opentracing_span(self, span: "opentracing.Span") -> None: self._opentracing_span = span def get_request_id(self) -> str: - return "%s-%i" % (self.get_method(), self.request_seq) + if self.request_id_header: + request_id_value = self.getHeader(self.request_id_header) + + if not request_id_value: + request_id_value = self.request_seq + + return "%s-%i" % (self.get_method(), request_id_value) def get_redacted_uri(self) -> str: """Gets the redacted URI associated with the request (or placeholder if the URI @@ -617,6 +625,7 @@ def request_factory(channel: HTTPChannel, queued: bool) -> Request: self, max_request_body_size=max_request_body_size, queued=queued, + request_id_header=config.http_options.request_id_header, ) self.requestFactory = request_factory # type: ignore