Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Commit

Permalink
Add basic opentracing support (#5544)
Browse files Browse the repository at this point in the history
  • Loading branch information
anoadragon453 committed Feb 17, 2020
2 parents ade1224 + 38a6d3e commit 619b3cd
Show file tree
Hide file tree
Showing 12 changed files with 633 additions and 12 deletions.
1 change: 1 addition & 0 deletions changelog.d/5544.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added opentracing and configuration options.
17 changes: 17 additions & 0 deletions docs/sample_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1597,3 +1597,20 @@ password_config:
# module: "my_custom_project.SuperRulesSet"
# config:
# example_option: 'things'


## Opentracing ##
# These settings enable opentracing which implements distributed tracing
# This allows you to observe the causal chain of events across servers
# including requests, key lookups etc. across any server running
# synapse or any other other services which supports opentracing.
# (specifically those implemented with jaeger)

#opentracing:
# # Enable / disable tracer
# tracer_enabled: false
# # The list of homeservers we wish to expose our current traces to.
# # The list is a list of regexes which are matched against the
# # servername of the homeserver
# homeserver_whitelist:
# - ".*"
3 changes: 3 additions & 0 deletions synapse/app/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,9 @@ def handle_sighup(*args, **kwargs):
# Load the certificate from disk.
refresh_certificate(hs)

# Start the tracer
synapse.logging.opentracing.init_tracer(hs.config)

# It is now safe to start your Synapse.
hs.start_listening(listeners)
hs.get_datastore().start_profiling()
Expand Down
2 changes: 2 additions & 0 deletions synapse/config/homeserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
from .stats import StatsConfig
from .third_party_event_rules import ThirdPartyRulesConfig
from .tls import TlsConfig
from .tracer import TracerConfig
from .user_directory import UserDirectoryConfig
from .voip import VoipConfig
from .workers import WorkerConfig
Expand Down Expand Up @@ -75,5 +76,6 @@ class HomeServerConfig(
ServerNoticesConfig,
RoomDirectoryConfig,
ThirdPartyRulesConfig,
TracerConfig,
):
pass
50 changes: 50 additions & 0 deletions synapse/config/tracer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
# Copyright 2019 The Matrix.org Foundation C.I.C.d
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from ._base import Config, ConfigError


class TracerConfig(Config):
def read_config(self, config, **kwargs):
self.tracer_config = config.get("opentracing")

self.tracer_config = config.get("opentracing", {"tracer_enabled": False})

if self.tracer_config.get("tracer_enabled", False):
# The tracer is enabled so sanitize the config
# If no whitelists are given
self.tracer_config.setdefault("homeserver_whitelist", [])

if not isinstance(self.tracer_config.get("homeserver_whitelist"), list):
raise ConfigError("Tracer homesererver_whitelist config is malformed")

def generate_config_section(cls, **kwargs):
return """\
## Opentracing ##
# These settings enable opentracing which implements distributed tracing
# This allows you to observe the causal chain of events across servers
# including requests, key lookups etc. across any server running
# synapse or any other other services which supports opentracing.
# (specifically those implemented with jaeger)
#opentracing:
# # Enable / disable tracer
# tracer_enabled: false
# # The list of homeservers we wish to expose our current traces to.
# # The list is a list of regexes which are matched against the
# # servername of the homeserver
# homeserver_whitelist:
# - ".*"
"""
26 changes: 21 additions & 5 deletions synapse/federation/transport/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from twisted.internet import defer

import synapse
import synapse.logging.opentracing as opentracing
from synapse.api.errors import Codes, FederationDeniedError, SynapseError
from synapse.api.room_versions import RoomVersions
from synapse.api.urls import (
Expand Down Expand Up @@ -288,14 +289,29 @@ def new_func(request, *args, **kwargs):
logger.warn("authenticate_request failed: %s", e)
raise

if origin:
with ratelimiter.ratelimit(origin) as d:
yield d
# Start an opentracing span
with opentracing.start_active_span_from_context(
request.requestHeaders,
"incoming-federation-request",
tags={
"request_id": request.get_request_id(),
opentracing.tags.SPAN_KIND: opentracing.tags.SPAN_KIND_RPC_SERVER,
opentracing.tags.HTTP_METHOD: request.get_method(),
opentracing.tags.HTTP_URL: request.get_redacted_uri(),
opentracing.tags.PEER_HOST_IPV6: request.getClientIP(),
"authenticated_entity": origin,
},
):
if origin:
with ratelimiter.ratelimit(origin) as d:
yield d
response = yield func(
origin, content, request.args, *args, **kwargs
)
else:
response = yield func(
origin, content, request.args, *args, **kwargs
)
else:
response = yield func(origin, content, request.args, *args, **kwargs)

defer.returnValue(response)

Expand Down
28 changes: 24 additions & 4 deletions synapse/http/matrixfederationclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from twisted.web._newclient import ResponseDone
from twisted.web.http_headers import Headers

import synapse.logging.opentracing as opentracing
import synapse.metrics
import synapse.util.retryutils
from synapse.api.errors import (
Expand Down Expand Up @@ -339,9 +340,25 @@ def _send_request(
else:
query_bytes = b""

headers_dict = {b"User-Agent": [self.version_string_bytes]}
# Retreive current span
scope = opentracing.start_active_span(
"outgoing-federation-request",
tags={
opentracing.tags.SPAN_KIND: opentracing.tags.SPAN_KIND_RPC_CLIENT,
opentracing.tags.PEER_ADDRESS: request.destination,
opentracing.tags.HTTP_METHOD: request.method,
opentracing.tags.HTTP_URL: request.path,
},
finish_on_close=True,
)

# Inject the span into the headers
headers_dict = {}
opentracing.inject_active_span_byte_dict(headers_dict, request.destination)

with limiter:
headers_dict[b"User-Agent"] = [self.version_string_bytes]

with limiter, scope:
# XXX: Would be much nicer to retry only at the transaction-layer
# (once we have reliable transactions in place)
if long_retries:
Expand Down Expand Up @@ -419,6 +436,10 @@ def _send_request(
response.phrase.decode("ascii", errors="replace"),
)

opentracing.set_tag(
opentracing.tags.HTTP_STATUS_CODE, response.code
)

if 200 <= response.code < 300:
pass
else:
Expand Down Expand Up @@ -499,8 +520,7 @@ def _send_request(
_flatten_response_never_received(e),
)
raise

defer.returnValue(response)
defer.returnValue(response)

def build_auth_headers(
self, destination, method, url_bytes, content=None, destination_is=None
Expand Down
7 changes: 6 additions & 1 deletion synapse/http/servlet.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from canonicaljson import json

from synapse.api.errors import Codes, SynapseError
from synapse.logging.opentracing import trace_servlet

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -290,7 +291,11 @@ def register(self, http_server):
for method in ("GET", "PUT", "POST", "OPTIONS", "DELETE"):
if hasattr(self, "on_%s" % (method,)):
method_handler = getattr(self, "on_%s" % (method,))
http_server.register_paths(method, patterns, method_handler)
http_server.register_paths(
method,
patterns,
trace_servlet(self.__class__.__name__, method_handler),
)

else:
raise NotImplementedError("RestServlet must register something.")
8 changes: 6 additions & 2 deletions synapse/logging/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ class LoggingContext(object):
"alive",
"request",
"tag",
"scope",
]

thread_local = threading.local()
Expand Down Expand Up @@ -238,6 +239,7 @@ def __init__(self, name=None, parent_context=None, request=None):
self.request = None
self.tag = ""
self.alive = True
self.scope = None

self.parent_context = parent_context

Expand Down Expand Up @@ -322,10 +324,12 @@ def copy_to(self, record):
another LoggingContext
"""

# 'request' is the only field we currently use in the logger, so that's
# all we need to copy
# we track the current request
record.request = self.request

# we also track the current scope:
record.scope = self.scope

def start(self):
if get_thread_id() != self.main_thread:
logger.warning("Started logcontext %s on different thread", self)
Expand Down
Loading

0 comments on commit 619b3cd

Please sign in to comment.