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

Add basic opentracing support #5544

Merged
merged 74 commits into from
Jul 11, 2019
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
5afd89a
Configure and initialise tracer
JorikSchellekens Jun 21, 2019
b43e3af
Scope manager using LogContexts
JorikSchellekens Jun 21, 2019
297c5fe
jaeger is a dependency now
JorikSchellekens Jun 21, 2019
45d412e
Carrier inject and extraction for Twisted Headers
JorikSchellekens Jun 21, 2019
fc069fe
Trace federation requests on the way in and out.
JorikSchellekens Jun 21, 2019
227447d
Create logcontext for new scope.
JorikSchellekens Jun 21, 2019
f35bde3
Remove scope from logcontext if logcontext is top level
JorikSchellekens Jun 24, 2019
7d9c3d3
Disable tracer if not configured
JorikSchellekens Jun 24, 2019
37607eb
typo
JorikSchellekens Jun 24, 2019
ee2f21f
Remove dependence on jaeger internals
JorikSchellekens Jun 24, 2019
ed5d00f
bools
JorikSchellekens Jun 24, 2019
a69e6aa
Set service name
JorikSchellekens Jun 24, 2019
6a6f97a
:Explicitely state that the tracer is disabled
JorikSchellekens Jun 24, 2019
ce21e41
Merge branch 'develop' into joriks/opentracing
JorikSchellekens Jun 25, 2019
98c8038
Black is the new black
JorikSchellekens Jun 25, 2019
889afc1
Newsfile
JorikSchellekens Jun 25, 2019
db10eeb
Code style
JorikSchellekens Jun 25, 2019
4e1eaf2
Use the new config setup.
JorikSchellekens Jun 25, 2019
8ca185d
Generate config.
JorikSchellekens Jun 25, 2019
0009fdd
Copyright
JorikSchellekens Jun 25, 2019
e90bfa7
Rename config to opentracing
JorikSchellekens Jun 25, 2019
640381e
Remove user whitelisting
JorikSchellekens Jun 25, 2019
0f57aac
Empty whitelist by default
JorikSchellekens Jun 25, 2019
dd2f06d
User ConfigError instead of RuntimeError
JorikSchellekens Jun 25, 2019
bae3b5b
Use isinstance
JorikSchellekens Jun 25, 2019
9c39f0e
Use tag constants for opentracing.
JorikSchellekens Jun 25, 2019
bae63e9
Remove debug comment and no need to explicitely record error
JorikSchellekens Jun 25, 2019
4925558
Typo?...
JorikSchellekens Jun 25, 2019
8104779
Two errors a "s(c)entry"
JorikSchellekens Jun 25, 2019
609d97b
Docstrings!
JorikSchellekens Jun 25, 2019
04781e9
typos
JorikSchellekens Jun 25, 2019
088176e
Remove debugging brainslip
JorikSchellekens Jun 25, 2019
dde4618
Homeserver Whitlisting
JorikSchellekens Jun 25, 2019
34235a4
Better opentracing config comment
JorikSchellekens Jun 25, 2019
1d591af
linting
JorikSchellekens Jun 25, 2019
37a650c
Inclue worker name in service_name
JorikSchellekens Jun 26, 2019
4bf9e11
Make opentracing an optional dependency
JorikSchellekens Jun 26, 2019
941b655
Neater config retreival
JorikSchellekens Jul 2, 2019
9654277
Typoed
JorikSchellekens Jul 2, 2019
9ce4a3d
Clean up dummy tags
JorikSchellekens Jul 2, 2019
b008aa4
Instantiate tracing as object instead of global class
JorikSchellekens Jul 2, 2019
e27516b
Inlcude opentracing as a homeserver member.
JorikSchellekens Jul 2, 2019
9d2734b
Thread opentracing to the request level
JorikSchellekens Jul 2, 2019
df255d7
Reference opetnracing through hs
JorikSchellekens Jul 2, 2019
64f8510
Instantiate dummy opentracin g for tests.
JorikSchellekens Jul 2, 2019
69561e0
About to revert, just keeping the unfinished changes just in case
JorikSchellekens Jul 2, 2019
4656403
Revert back to global state, commit number:
JorikSchellekens Jul 2, 2019
a60572f
Use class level methods in tracerutils
JorikSchellekens Jul 2, 2019
bc6b236
Start and stop requests spans in a place where we
JorikSchellekens Jul 2, 2019
68e4595
Seen it, isort it
JorikSchellekens Jul 2, 2019
70a501a
Merge remote-tracking branch 'origin/develop' into joriks/opentracing
JorikSchellekens Jul 2, 2019
9bfe90f
Make sure to close the active span.
JorikSchellekens Jul 3, 2019
800e707
I'm getting black and blue from this.
JorikSchellekens Jul 3, 2019
fd37a69
Merge remote-tracking branch 'origin/develop' into joriks/opentracing
JorikSchellekens Jul 3, 2019
b09aa24
Logger formatting
JorikSchellekens Jul 3, 2019
374e81c
Outdated comment
JorikSchellekens Jul 3, 2019
e111dcd
Import opentracing at the top
JorikSchellekens Jul 3, 2019
14376e7
Return a contextmanager
JorikSchellekens Jul 3, 2019
24b3834
Start tracing client requests from the servlet
JorikSchellekens Jul 4, 2019
386285b
Return noop context manager if not tracing
JorikSchellekens Jul 4, 2019
3dde444
Explicitely say that these are federation requests
JorikSchellekens Jul 4, 2019
b5cd7df
Include servlet name in client requests
JorikSchellekens Jul 4, 2019
f93b60d
Use context manager
JorikSchellekens Jul 4, 2019
6d45cd0
Merge remote-tracking branch 'origin/develop' into joriks/opentracing
JorikSchellekens Jul 4, 2019
6ab84bb
Move opentracing to logging/
JorikSchellekens Jul 4, 2019
9a3c98a
Seen it, isort it again!
JorikSchellekens Jul 4, 2019
0c19608
Ignore twisted return exceptions on context exit
JorikSchellekens Jul 5, 2019
7414549
Merge branch 'develop' into joriks/opentracing
JorikSchellekens Jul 8, 2019
18a902f
Escape the scope
JorikSchellekens Jul 10, 2019
49038b1
Scopes should be entered to make them useful.
JorikSchellekens Jul 10, 2019
00e08bd
Nicer decorator names
JorikSchellekens Jul 10, 2019
06baa49
Just one init, init?
JorikSchellekens Jul 10, 2019
b07a4c6
Don't need to close something that isn't open
JorikSchellekens Jul 10, 2019
6f3109e
Docs make you smarter
JorikSchellekens Jul 10, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
15 changes: 15 additions & 0 deletions docs/sample_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1352,3 +1352,18 @@ password_config:
# module: "my_custom_project.SuperRulesSet"
# config:
# example_option: 'things'


## Tracer ##

#tracer:
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved
# # Enable / disable tracer
# tracer_enabled: false
# # The list of users who's requests will be traced
# # The list is a list of regex which is matched against the user_id
# user_whitelist:
# - "*"
# # The list of homeservers we wish to trace across
# # The list is a list of regex which is matched against the homeserver name
# homeserver_whitelist:
# - "*"
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved
3 changes: 3 additions & 0 deletions synapse/app/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,9 @@ def handle_sighup(*args, **kwargs):
# Load the certificate from disk.
refresh_certificate(hs)

# Start the tracer
synapse.config.tracer.init_tracing(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
85 changes: 85 additions & 0 deletions synapse/config/tracer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# -*- coding: utf-8 -*-
# Copyright 2018 New Vector Ltd
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved
#
# 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.

import logging

from jaeger_client import Config as JaegerConfig

from synapse.util.scopecontextmanager import LogContextScopeManager

from ._base import Config

logger = logging.getLogger(__name__)


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

if self.tracer_config is None:
# If the tracer is not configured we assume it is disabled
self.tracer_config = {"tracer_enabled": False}
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved

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("user_whitelist", ["*"])
self.tracer_config.setdefault("homeserver_whitelist", ["*"])
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved

if type(self.tracer_config.get("user_whitelist")) != list:
raise RuntimeError("Tracer user_whitelist config is malformed")
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved
if type(self.tracer_config.get("homeserver_whitelist")) != list:
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved
raise RuntimeError("Tracer homesererver_whitelist config is malformed")

def generate_config_section(cls, **kwargs):
return """\
## Tracer ##

#tracer:
# # Enable / disable tracer
# tracer_enabled: false
# # The list of users who's requests will be traced
# # The list is a list of regex which is matched against the user_id
# user_whitelist:
# - "*"
# # The list of homeservers we wish to trace across
# # The list is a list of regex which is matched against the homeserver name
# homeserver_whitelist:
# - "*"
"""


def init_tracing(config):
"""Initialise the JaegerClient tracer

Args:
config (Config)
The config used by the homserver. Here it's used to set the service
name to the homeserver's.
"""

if config.tracer_config.get("tracer_enabled", False):
jaeger_config = JaegerConfig(
config={"sampler": {"type": "const", "param": 1}, "logging": True},
service_name=config.server_name,
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved
scope_manager=LogContextScopeManager(config),
)
else: # The tracer is not configured so we instantiate a noop tracer
jaeger_config = JaegerConfig(
config={"sampler": {"type": "const", "param": 0}},
service_name=config.server_name,
)

return jaeger_config.initialize_tracer()
37 changes: 34 additions & 3 deletions synapse/http/matrixfederationclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@
from six.moves import urllib

import attr
import opentracing
import treq
from canonicaljson import encode_canonical_json
from opentracing.propagation import Format
from prometheus_client import Counter
from signedjson.sign import sign_json
from zope.interface import implementer
Expand Down Expand Up @@ -339,9 +341,26 @@ def _send_request(
else:
query_bytes = b""

headers_dict = {b"User-Agent": [self.version_string_bytes]}
# Retreive current span
scope = opentracing.tracer.start_active_span(
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved
"outgoing-federation-request",
tags={
"span.kind": "client", # With respect to this request's role in an rpc
"peer.address": request.destination,
"http.method": request.method,
"http.url": request.path,
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved
},
finish_on_close=True,
)

# Inject the span into the headers
headers_dict = {}
opentracing.tracer.inject(scope.span, Format.HTTP_HEADERS, headers_dict)
headers_dict = {k.encode(): [v.encode()] for k, v in headers_dict.items()}

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

with limiter:
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 +438,12 @@ def _send_request(
response.phrase.decode("ascii", errors="replace"),
)

logger.info(
"Setting response code on span {} *********".format(
opentracing.tracer.active_span
)
)
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved
scope.span.set_tag("http.status_code", response.code)
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved
if 200 <= response.code < 300:
pass
else:
Expand Down Expand Up @@ -498,9 +523,15 @@ def _send_request(
url_str,
_flatten_response_never_received(e),
)
logger.info(
"Setting response code on span {} *********".format(
opentracing.tracer.active_span
)
)
# scope.span.set_tag("error", True)
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved
raise

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

def build_auth_headers(
self, destination, method, url_bytes, content=None, destination_is=None
Expand Down
26 changes: 25 additions & 1 deletion synapse/http/site.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@
import logging
import time

import opentracing

from twisted.web.server import Request, Site

from synapse.http import redact_uri
from synapse.http.request_metrics import RequestMetrics, requests_counter
from synapse.util.logcontext import LoggingContext, PreserveLoggingContext
from synapse.util.tracerutils import extract_span_context

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -211,6 +214,7 @@ def _started_processing(self, servlet_name):

This will log the request's arrival. Once the request completes,
be sure to call finished_processing.
It will also start a span for this request.
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved

Args:
servlet_name (str): the name of the servlet which will be
Expand All @@ -233,6 +237,20 @@ def _started_processing(self, servlet_name):
self.get_redacted_uri(),
)

# Start a span
span_context = extract_span_context(self.requestHeaders)
opentracing.tracer.start_active_span(
"incoming-federation-request",
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved
tags={
"request_id": self.get_request_id(),
"span.kind": "server",
"http.method": self.get_method(),
"http.url": self.get_redacted_uri(),
"peer.ipv6": self.getClientIP(),
},
child_of=span_context,
)

def _finished_processing(self):
"""Log the completion of this request and update the metrics
"""
Expand All @@ -252,7 +270,7 @@ def _finished_processing(self):
# the time between receiving the request and the request handler finishing
processing_time = self._processing_finished_time - self.start_time

# the time between the request handler finishing and the response being sent
# the time between the reb'\x80\x03ctwisted.web.http_headers\nHeaders\nq\x00)\x81q\x01}q\x02X\x0b\x00\x00\x00_rawHeadersq\x03}q\x04(C\x04hostq\x05]q\x06C\x0elocalhost:8081q\x07aC\nuser-agentq\x08]q\tCLMozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:67.0) Gecko/20100101 Firefox/67.0q\naC\x06acceptq\x0b]q\x0cC\x10application/jsonq\raC\x0faccept-languageq\x0e]q\x0fC\x0een-GB,en;q=0.5q\x10aC\x0faccept-encodingq\x11]q\x12C\rgzip, deflateq\x13aC\x06originq\x14]q\x15C\x04nullq\x16aC\nconnectionq\x17]q\x18C\nkeep-aliveq\x19ausb.'quest handler finishing and the response being sent
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved
# to the client (nb may be negative)
response_send_time = self.finish_time - self._processing_finished_time

Expand Down Expand Up @@ -302,6 +320,12 @@ def _finished_processing(self):
usage.evt_db_fetch_count,
)

scope = opentracing.tracer.scope_manager.active
if scope is not None:
# finish the span if it's there.
scope.span.set_tag("peer.address", authenticated_entity)
scope.__exit__(None, None, None)

try:
self.request_metrics.stop(self.finish_time, self.code, self.sentLength)
except Exception as e:
Expand Down
1 change: 1 addition & 0 deletions synapse/python_dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
# Twisted 18.7.0 requires attrs>=17.4.0
"attrs>=17.4.0",
"netaddr>=0.7.18",
"jaeger-client>=4.0.0",
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved
"Jinja2>=2.9",
"bleach>=1.4.3",
]
Expand Down
8 changes: 6 additions & 2 deletions synapse/util/logcontext.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ class LoggingContext(object):
"alive",
"request",
"tag",
"scope",
]

thread_local = threading.local()
Expand Down Expand Up @@ -228,6 +229,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 @@ -312,10 +314,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
JorikSchellekens marked this conversation as resolved.
Show resolved Hide resolved

def start(self):
if threading.current_thread() is not self.main_thread:
logger.warning("Started logcontext %s on different thread", self)
Expand Down
Loading