Skip to content

Commit

Permalink
Merge branch 'master' into antonpirker/trace_meta_flask
Browse files Browse the repository at this point in the history
  • Loading branch information
antonpirker committed Jun 28, 2023
2 parents 97b1cc8 + 6795295 commit 8094f48
Show file tree
Hide file tree
Showing 10 changed files with 148 additions and 30 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-python@v4
with:
python-version: 3.9
python-version: 3.11

- run: |
pip install virtualenv
Expand Down
2 changes: 1 addition & 1 deletion docs-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
shibuya
sphinx==7.0.1
sphinx-rtd-theme
sphinx-autodoc-typehints[type_comments]>=1.8.0
typing-extensions
16 changes: 9 additions & 7 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

import os
import sys

import typing
from datetime import datetime

# prevent circular imports
import sphinx.builders.html
import sphinx.builders.latex
import sphinx.builders.texinfo
import sphinx.builders.text
import sphinx.ext.autodoc
import urllib3.exceptions
import sphinx.ext.autodoc # noqa: F401
import urllib3.exceptions # noqa: F401

typing.TYPE_CHECKING = True

Expand All @@ -27,7 +27,7 @@
# -- Project information -----------------------------------------------------

project = "sentry-python"
copyright = "2019, Sentry Team and Contributors"
copyright = "2019-{}, Sentry Team and Contributors".format(datetime.now().year)
author = "Sentry Team and Contributors"

release = "1.26.0"
Expand Down Expand Up @@ -87,13 +87,15 @@

on_rtd = os.environ.get("READTHEDOCS", None) == "True"

html_theme = "alabaster"
html_theme = "shibuya"

# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
html_theme_options = {
"github_url": "https://github.com/getsentry/sentry-python",
}

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
Expand Down Expand Up @@ -167,7 +169,7 @@
"sentry-python Documentation",
author,
"sentry-python",
"One line description of project.",
"The official Sentry SDK for Python.",
"Miscellaneous",
)
]
Expand Down
19 changes: 18 additions & 1 deletion sentry_sdk/integrations/celery.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from sentry_sdk.hub import Hub
from sentry_sdk.integrations import Integration, DidNotEnable
from sentry_sdk.integrations.logging import ignore_logger
from sentry_sdk.tracing import TRANSACTION_SOURCE_TASK
from sentry_sdk.tracing import BAGGAGE_HEADER_NAME, TRANSACTION_SOURCE_TASK
from sentry_sdk._types import TYPE_CHECKING
from sentry_sdk.utils import (
capture_internal_exceptions,
Expand Down Expand Up @@ -158,14 +158,31 @@ def apply_async(*args, **kwargs):
# Note: kwargs can contain headers=None, so no setdefault!
# Unsure which backend though.
kwarg_headers = kwargs.get("headers") or {}

existing_baggage = kwarg_headers.get(BAGGAGE_HEADER_NAME)
sentry_baggage = headers.get(BAGGAGE_HEADER_NAME)

combined_baggage = sentry_baggage or existing_baggage
if sentry_baggage and existing_baggage:
combined_baggage = "{},{}".format(
existing_baggage,
sentry_baggage,
)

kwarg_headers.update(headers)
if combined_baggage:
kwarg_headers[BAGGAGE_HEADER_NAME] = combined_baggage

# https://github.com/celery/celery/issues/4875
#
# Need to setdefault the inner headers too since other
# tracing tools (dd-trace-py) also employ this exact
# workaround and we don't want to break them.
kwarg_headers.setdefault("headers", {}).update(headers)
if combined_baggage:
kwarg_headers["headers"][
BAGGAGE_HEADER_NAME
] = combined_baggage

# Add the Sentry options potentially added in `sentry_apply_entry`
# to the headers (done when auto-instrumenting Celery Beat tasks)
Expand Down
17 changes: 15 additions & 2 deletions sentry_sdk/integrations/httpx.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from sentry_sdk import Hub
from sentry_sdk.consts import OP, SPANDATA
from sentry_sdk.integrations import Integration, DidNotEnable
from sentry_sdk.tracing import BAGGAGE_HEADER_NAME
from sentry_sdk.tracing_utils import should_propagate_trace
from sentry_sdk.utils import (
SENSITIVE_DATA_SUBSTITUTE,
Expand Down Expand Up @@ -72,7 +73,13 @@ def send(self, request, **kwargs):
key=key, value=value, url=request.url
)
)
request.headers[key] = value
if key == BAGGAGE_HEADER_NAME and request.headers.get(
BAGGAGE_HEADER_NAME
):
# do not overwrite any existing baggage, just append to it
request.headers[key] += "," + value
else:
request.headers[key] = value

rv = real_send(self, request, **kwargs)

Expand Down Expand Up @@ -119,7 +126,13 @@ async def send(self, request, **kwargs):
key=key, value=value, url=request.url
)
)
request.headers[key] = value
if key == BAGGAGE_HEADER_NAME and request.headers.get(
BAGGAGE_HEADER_NAME
):
# do not overwrite any existing baggage, just append to it
request.headers[key] += "," + value
else:
request.headers[key] = value

rv = await real_send(self, request, **kwargs)

Expand Down
4 changes: 2 additions & 2 deletions sentry_sdk/integrations/huey.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@

try:
from huey.api import Huey, Result, ResultGroup, Task
from huey.exceptions import CancelExecution, RetryTask
from huey.exceptions import CancelExecution, RetryTask, TaskLockedException
except ImportError:
raise DidNotEnable("Huey is not installed")


HUEY_CONTROL_FLOW_EXCEPTIONS = (CancelExecution, RetryTask)
HUEY_CONTROL_FLOW_EXCEPTIONS = (CancelExecution, RetryTask, TaskLockedException)


class HueyIntegration(Integration):
Expand Down
8 changes: 4 additions & 4 deletions sentry_sdk/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -605,11 +605,11 @@ def _drop(cause, ty):

contexts = event.setdefault("contexts", {})

if has_tracing_enabled(options):
if self._span is not None:
if contexts.get("trace") is None:
if has_tracing_enabled(options) and self._span is not None:
contexts["trace"] = self._span.get_trace_context()
else:
contexts["trace"] = self.get_trace_context()
else:
contexts["trace"] = self.get_trace_context()

exc_info = hint.get("exc_info")
if exc_info is not None:
Expand Down
42 changes: 30 additions & 12 deletions tests/integrations/celery/test_celery.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

from celery import Celery, VERSION
from celery.bin import worker
from celery.signals import task_success

try:
from unittest import mock # python 3.3 and above
Expand Down Expand Up @@ -360,7 +359,7 @@ def dummy_task(self):
# TODO: This test is hanging when running test with `tox --parallel auto`. Find out why and fix it!
@pytest.mark.skip
@pytest.mark.forked
def test_redis_backend_trace_propagation(init_celery, capture_events_forksafe, tmpdir):
def test_redis_backend_trace_propagation(init_celery, capture_events_forksafe):
celery = init_celery(traces_sample_rate=1.0, backend="redis", debug=True)

events = capture_events_forksafe()
Expand Down Expand Up @@ -493,17 +492,36 @@ def test_task_headers(celery):
"sentry-monitor-check-in-id": "123abc",
}

@celery.task(name="dummy_task")
def dummy_task(x, y):
return x + y

def crons_task_success(sender, **kwargs):
headers = _get_headers(sender)
assert headers == sentry_crons_setup

task_success.connect(crons_task_success)
@celery.task(name="dummy_task", bind=True)
def dummy_task(self, x, y):
return _get_headers(self)

# This is how the Celery Beat auto-instrumentation starts a task
# in the monkey patched version of `apply_async`
# in `sentry_sdk/integrations/celery.py::_wrap_apply_async()`
dummy_task.apply_async(args=(1, 0), headers=sentry_crons_setup)
result = dummy_task.apply_async(args=(1, 0), headers=sentry_crons_setup)
assert result.get() == sentry_crons_setup


def test_baggage_propagation(init_celery):
celery = init_celery(traces_sample_rate=1.0, release="abcdef")

@celery.task(name="dummy_task", bind=True)
def dummy_task(self, x, y):
return _get_headers(self)

with start_transaction() as transaction:
result = dummy_task.apply_async(
args=(1, 0),
headers={"baggage": "custom=value"},
).get()

assert sorted(result["baggage"].split(",")) == sorted(
[
"sentry-release=abcdef",
"sentry-trace_id={}".format(transaction.trace_id),
"sentry-environment=production",
"sentry-sample_rate=1.0",
"custom=value",
]
)
40 changes: 40 additions & 0 deletions tests/integrations/httpx/test_httpx.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,46 @@ def test_outgoing_trace_headers(sentry_init, httpx_client):
)


@pytest.mark.parametrize(
"httpx_client",
(httpx.Client(), httpx.AsyncClient()),
)
def test_outgoing_trace_headers_append_to_baggage(sentry_init, httpx_client):
sentry_init(
traces_sample_rate=1.0,
integrations=[HttpxIntegration()],
release="d08ebdb9309e1b004c6f52202de58a09c2268e42",
)

url = "http://example.com/"
responses.add(responses.GET, url, status=200)

with start_transaction(
name="/interactions/other-dogs/new-dog",
op="greeting.sniff",
trace_id="01234567890123456789012345678901",
) as transaction:
if asyncio.iscoroutinefunction(httpx_client.get):
response = asyncio.get_event_loop().run_until_complete(
httpx_client.get(url, headers={"baGGage": "custom=data"})
)
else:
response = httpx_client.get(url, headers={"baGGage": "custom=data"})

request_span = transaction._span_recorder.spans[-1]
assert response.request.headers[
"sentry-trace"
] == "{trace_id}-{parent_span_id}-{sampled}".format(
trace_id=transaction.trace_id,
parent_span_id=request_span.span_id,
sampled=1,
)
assert (
response.request.headers["baggage"]
== "custom=data,sentry-trace_id=01234567890123456789012345678901,sentry-environment=production,sentry-release=d08ebdb9309e1b004c6f52202de58a09c2268e42,sentry-transaction=/interactions/other-dogs/new-dog,sentry-sample_rate=1.0"
)


@pytest.mark.parametrize(
"httpx_client,trace_propagation_targets,url,trace_propagated",
[
Expand Down
28 changes: 28 additions & 0 deletions tests/integrations/huey/test_huey.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,34 @@ def retry_task(context):
assert len(huey) == 0


@pytest.mark.parametrize("lock_name", ["lock.a", "lock.b"], ids=["locked", "unlocked"])
def test_task_lock(capture_events, init_huey, lock_name):
huey = init_huey()

task_lock_name = "lock.a"
should_be_locked = task_lock_name == lock_name

@huey.task()
@huey.lock_task(task_lock_name)
def maybe_locked_task():
pass

events = capture_events()

with huey.lock_task(lock_name):
assert huey.is_locked(task_lock_name) == should_be_locked
result = execute_huey_task(huey, maybe_locked_task)

(event,) = events

assert event["transaction"] == "maybe_locked_task"
assert event["tags"]["huey_task_id"] == result.task.id
assert (
event["contexts"]["trace"]["status"] == "aborted" if should_be_locked else "ok"
)
assert len(huey) == 0


def test_huey_enqueue(init_huey, capture_events):
huey = init_huey()

Expand Down

0 comments on commit 8094f48

Please sign in to comment.