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

Commit 812ed6b

Browse files
Opentracing across workers (#5771)
Propagate opentracing contexts across workers Also includes some Convenience modifications to opentracing for servlets, notably: - Add boolean to skip the whitelisting check on inject extract methods. - useful when injecting into carriers locally. Otherwise we'd always have to include our own servername and whitelist our servername - start_active_span_from_request instead of header - Add boolean to decide whether to extract context from a request to a servlet
1 parent dbd46de commit 812ed6b

File tree

5 files changed

+123
-83
lines changed

5 files changed

+123
-83
lines changed

changelog.d/5771.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Make Opentracing work in worker mode.

synapse/federation/transport/server.py

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,12 @@
3838
parse_string_from_args,
3939
)
4040
from synapse.logging.context import run_in_background
41-
from synapse.logging.opentracing import start_active_span_from_context, tags
41+
from synapse.logging.opentracing import (
42+
start_active_span,
43+
start_active_span_from_request,
44+
tags,
45+
whitelisted_homeserver,
46+
)
4247
from synapse.types import ThirdPartyInstanceID, get_domain_from_id
4348
from synapse.util.ratelimitutils import FederationRateLimiter
4449
from synapse.util.versionstring import get_version_string
@@ -288,20 +293,28 @@ async def new_func(request, *args, **kwargs):
288293
logger.warn("authenticate_request failed: %s", e)
289294
raise
290295

291-
# Start an opentracing span
292-
with start_active_span_from_context(
293-
request.requestHeaders,
294-
"incoming-federation-request",
295-
tags={
296-
"request_id": request.get_request_id(),
297-
tags.SPAN_KIND: tags.SPAN_KIND_RPC_SERVER,
298-
tags.HTTP_METHOD: request.get_method(),
299-
tags.HTTP_URL: request.get_redacted_uri(),
300-
tags.PEER_HOST_IPV6: request.getClientIP(),
301-
"authenticated_entity": origin,
302-
"servlet_name": request.request_metrics.name,
303-
},
304-
):
296+
request_tags = {
297+
"request_id": request.get_request_id(),
298+
tags.SPAN_KIND: tags.SPAN_KIND_RPC_SERVER,
299+
tags.HTTP_METHOD: request.get_method(),
300+
tags.HTTP_URL: request.get_redacted_uri(),
301+
tags.PEER_HOST_IPV6: request.getClientIP(),
302+
"authenticated_entity": origin,
303+
"servlet_name": request.request_metrics.name,
304+
}
305+
306+
# Only accept the span context if the origin is authenticated
307+
# and whitelisted
308+
if origin and whitelisted_homeserver(origin):
309+
scope = start_active_span_from_request(
310+
request, "incoming-federation-request", tags=request_tags
311+
)
312+
else:
313+
scope = start_active_span(
314+
"incoming-federation-request", tags=request_tags
315+
)
316+
317+
with scope:
305318
if origin:
306319
with ratelimiter.ratelimit(origin) as d:
307320
await d

synapse/http/servlet.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ def register(self, http_server):
300300
http_server.register_paths(
301301
method,
302302
patterns,
303-
trace_servlet(servlet_classname, method_handler),
303+
trace_servlet(servlet_classname)(method_handler),
304304
servlet_classname,
305305
)
306306

synapse/logging/opentracing.py

Lines changed: 79 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,48 @@ def set_fates(clotho, lachesis, atropos, father="Zues", mother="Themis"):
174174

175175
from synapse.config import ConfigError
176176

177+
# Helper class
178+
179+
180+
class _DummyTagNames(object):
181+
"""wrapper of opentracings tags. We need to have them if we
182+
want to reference them without opentracing around. Clearly they
183+
should never actually show up in a trace. `set_tags` overwrites
184+
these with the correct ones."""
185+
186+
INVALID_TAG = "invalid-tag"
187+
COMPONENT = INVALID_TAG
188+
DATABASE_INSTANCE = INVALID_TAG
189+
DATABASE_STATEMENT = INVALID_TAG
190+
DATABASE_TYPE = INVALID_TAG
191+
DATABASE_USER = INVALID_TAG
192+
ERROR = INVALID_TAG
193+
HTTP_METHOD = INVALID_TAG
194+
HTTP_STATUS_CODE = INVALID_TAG
195+
HTTP_URL = INVALID_TAG
196+
MESSAGE_BUS_DESTINATION = INVALID_TAG
197+
PEER_ADDRESS = INVALID_TAG
198+
PEER_HOSTNAME = INVALID_TAG
199+
PEER_HOST_IPV4 = INVALID_TAG
200+
PEER_HOST_IPV6 = INVALID_TAG
201+
PEER_PORT = INVALID_TAG
202+
PEER_SERVICE = INVALID_TAG
203+
SAMPLING_PRIORITY = INVALID_TAG
204+
SERVICE = INVALID_TAG
205+
SPAN_KIND = INVALID_TAG
206+
SPAN_KIND_CONSUMER = INVALID_TAG
207+
SPAN_KIND_PRODUCER = INVALID_TAG
208+
SPAN_KIND_RPC_CLIENT = INVALID_TAG
209+
SPAN_KIND_RPC_SERVER = INVALID_TAG
210+
211+
177212
try:
178213
import opentracing
214+
215+
tags = opentracing.tags
179216
except ImportError:
180217
opentracing = None
218+
tags = _DummyTagNames
181219
try:
182220
from jaeger_client import Config as JaegerConfig
183221
from synapse.logging.scopecontextmanager import LogContextScopeManager
@@ -252,10 +290,6 @@ def init_tracer(config):
252290
scope_manager=LogContextScopeManager(config),
253291
).initialize_tracer()
254292

255-
# Set up tags to be opentracing's tags
256-
global tags
257-
tags = opentracing.tags
258-
259293

260294
# Whitelisting
261295

@@ -334,8 +368,8 @@ def start_active_span_follows_from(operation_name, contexts):
334368
return scope
335369

336370

337-
def start_active_span_from_context(
338-
headers,
371+
def start_active_span_from_request(
372+
request,
339373
operation_name,
340374
references=None,
341375
tags=None,
@@ -344,9 +378,9 @@ def start_active_span_from_context(
344378
finish_on_close=True,
345379
):
346380
"""
347-
Extracts a span context from Twisted Headers.
381+
Extracts a span context from a Twisted Request.
348382
args:
349-
headers (twisted.web.http_headers.Headers)
383+
headers (twisted.web.http.Request)
350384
351385
For the other args see opentracing.tracer
352386
@@ -360,7 +394,9 @@ def start_active_span_from_context(
360394
if opentracing is None:
361395
return _noop_context_manager()
362396

363-
header_dict = {k.decode(): v[0].decode() for k, v in headers.getAllRawHeaders()}
397+
header_dict = {
398+
k.decode(): v[0].decode() for k, v in request.requestHeaders.getAllRawHeaders()
399+
}
364400
context = opentracing.tracer.extract(opentracing.Format.HTTP_HEADERS, header_dict)
365401

366402
return opentracing.tracer.start_active_span(
@@ -448,7 +484,7 @@ def set_operation_name(operation_name):
448484

449485

450486
@only_if_tracing
451-
def inject_active_span_twisted_headers(headers, destination):
487+
def inject_active_span_twisted_headers(headers, destination, check_destination=True):
452488
"""
453489
Injects a span context into twisted headers in-place
454490
@@ -467,7 +503,7 @@ def inject_active_span_twisted_headers(headers, destination):
467503
https://github.com/jaegertracing/jaeger-client-python/blob/master/jaeger_client/constants.py
468504
"""
469505

470-
if not whitelisted_homeserver(destination):
506+
if check_destination and not whitelisted_homeserver(destination):
471507
return
472508

473509
span = opentracing.tracer.active_span
@@ -479,7 +515,7 @@ def inject_active_span_twisted_headers(headers, destination):
479515

480516

481517
@only_if_tracing
482-
def inject_active_span_byte_dict(headers, destination):
518+
def inject_active_span_byte_dict(headers, destination, check_destination=True):
483519
"""
484520
Injects a span context into a dict where the headers are encoded as byte
485521
strings
@@ -511,7 +547,7 @@ def inject_active_span_byte_dict(headers, destination):
511547

512548

513549
@only_if_tracing
514-
def inject_active_span_text_map(carrier, destination=None):
550+
def inject_active_span_text_map(carrier, destination, check_destination=True):
515551
"""
516552
Injects a span context into a dict
517553
@@ -532,7 +568,7 @@ def inject_active_span_text_map(carrier, destination=None):
532568
https://github.com/jaegertracing/jaeger-client-python/blob/master/jaeger_client/constants.py
533569
"""
534570

535-
if destination and not whitelisted_homeserver(destination):
571+
if check_destination and not whitelisted_homeserver(destination):
536572
return
537573

538574
opentracing.tracer.inject(
@@ -689,65 +725,43 @@ def _tag_args_inner(self, *args, **kwargs):
689725
return _tag_args_inner
690726

691727

692-
def trace_servlet(servlet_name, func):
728+
def trace_servlet(servlet_name, extract_context=False):
693729
"""Decorator which traces a serlet. It starts a span with some servlet specific
694-
tags such as the servlet_name and request information"""
695-
if not opentracing:
696-
return func
730+
tags such as the servlet_name and request information
697731
698-
@wraps(func)
699-
@defer.inlineCallbacks
700-
def _trace_servlet_inner(request, *args, **kwargs):
701-
with start_active_span(
702-
"incoming-client-request",
703-
tags={
732+
Args:
733+
servlet_name (str): The name to be used for the span's operation_name
734+
extract_context (bool): Whether to attempt to extract the opentracing
735+
context from the request the servlet is handling.
736+
737+
"""
738+
739+
def _trace_servlet_inner_1(func):
740+
if not opentracing:
741+
return func
742+
743+
@wraps(func)
744+
@defer.inlineCallbacks
745+
def _trace_servlet_inner(request, *args, **kwargs):
746+
request_tags = {
704747
"request_id": request.get_request_id(),
705748
tags.SPAN_KIND: tags.SPAN_KIND_RPC_SERVER,
706749
tags.HTTP_METHOD: request.get_method(),
707750
tags.HTTP_URL: request.get_redacted_uri(),
708751
tags.PEER_HOST_IPV6: request.getClientIP(),
709-
"servlet_name": servlet_name,
710-
},
711-
):
712-
result = yield defer.maybeDeferred(func, request, *args, **kwargs)
713-
return result
714-
715-
return _trace_servlet_inner
716-
717-
718-
# Helper class
719-
752+
}
720753

721-
class _DummyTagNames(object):
722-
"""wrapper of opentracings tags. We need to have them if we
723-
want to reference them without opentracing around. Clearly they
724-
should never actually show up in a trace. `set_tags` overwrites
725-
these with the correct ones."""
754+
if extract_context:
755+
scope = start_active_span_from_request(
756+
request, servlet_name, tags=request_tags
757+
)
758+
else:
759+
scope = start_active_span(servlet_name, tags=request_tags)
726760

727-
INVALID_TAG = "invalid-tag"
728-
COMPONENT = INVALID_TAG
729-
DATABASE_INSTANCE = INVALID_TAG
730-
DATABASE_STATEMENT = INVALID_TAG
731-
DATABASE_TYPE = INVALID_TAG
732-
DATABASE_USER = INVALID_TAG
733-
ERROR = INVALID_TAG
734-
HTTP_METHOD = INVALID_TAG
735-
HTTP_STATUS_CODE = INVALID_TAG
736-
HTTP_URL = INVALID_TAG
737-
MESSAGE_BUS_DESTINATION = INVALID_TAG
738-
PEER_ADDRESS = INVALID_TAG
739-
PEER_HOSTNAME = INVALID_TAG
740-
PEER_HOST_IPV4 = INVALID_TAG
741-
PEER_HOST_IPV6 = INVALID_TAG
742-
PEER_PORT = INVALID_TAG
743-
PEER_SERVICE = INVALID_TAG
744-
SAMPLING_PRIORITY = INVALID_TAG
745-
SERVICE = INVALID_TAG
746-
SPAN_KIND = INVALID_TAG
747-
SPAN_KIND_CONSUMER = INVALID_TAG
748-
SPAN_KIND_PRODUCER = INVALID_TAG
749-
SPAN_KIND_RPC_CLIENT = INVALID_TAG
750-
SPAN_KIND_RPC_SERVER = INVALID_TAG
761+
with scope:
762+
result = yield defer.maybeDeferred(func, request, *args, **kwargs)
763+
return result
751764

765+
return _trace_servlet_inner
752766

753-
tags = _DummyTagNames
767+
return _trace_servlet_inner_1

synapse/replication/http/_base.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
from twisted.internet import defer
2424

25+
import synapse.logging.opentracing as opentracing
2526
from synapse.api.errors import (
2627
CodeMessageException,
2728
HttpResponseException,
@@ -165,8 +166,12 @@ def send_request(**kwargs):
165166
# have a good idea that the request has either succeeded or failed on
166167
# the master, and so whether we should clean up or not.
167168
while True:
169+
headers = {}
170+
opentracing.inject_active_span_byte_dict(
171+
headers, None, check_destination=False
172+
)
168173
try:
169-
result = yield request_func(uri, data)
174+
result = yield request_func(uri, data, headers=headers)
170175
break
171176
except CodeMessageException as e:
172177
if e.code != 504 or not cls.RETRY_ON_TIMEOUT:
@@ -205,7 +210,14 @@ def register(self, http_server):
205210
args = "/".join("(?P<%s>[^/]+)" % (arg,) for arg in url_args)
206211
pattern = re.compile("^/_synapse/replication/%s/%s$" % (self.NAME, args))
207212

208-
http_server.register_paths(method, [pattern], handler, self.__class__.__name__)
213+
http_server.register_paths(
214+
method,
215+
[pattern],
216+
opentracing.trace_servlet(self.__class__.__name__, extract_context=True)(
217+
handler
218+
),
219+
self.__class__.__name__,
220+
)
209221

210222
def _cached_handler(self, request, txn_id, **kwargs):
211223
"""Called on new incoming requests when caching is enabled. Checks

0 commit comments

Comments
 (0)