Skip to content

Commit 98415e6

Browse files
committed
Merge branch 'master' into ivana/toxgen/move-even-more-stuff
2 parents 705d738 + c25d4ff commit 98415e6

File tree

12 files changed

+227
-68
lines changed

12 files changed

+227
-68
lines changed

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
steps:
2121
- name: Get auth token
2222
id: token
23-
uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2
23+
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
2424
with:
2525
app-id: ${{ vars.SENTRY_RELEASE_BOT_CLIENT_ID }}
2626
private-key: ${{ secrets.SENTRY_RELEASE_BOT_PRIVATE_KEY }}

docs/api.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Capturing Data
2525
Enriching Events
2626
================
2727

28+
.. autofunction:: sentry_sdk.api.add_attachment
2829
.. autofunction:: sentry_sdk.api.add_breadcrumb
2930
.. autofunction:: sentry_sdk.api.set_context
3031
.. autofunction:: sentry_sdk.api.set_extra
@@ -63,4 +64,3 @@ Managing Scope (advanced)
6364
.. autofunction:: sentry_sdk.api.push_scope
6465

6566
.. autofunction:: sentry_sdk.api.new_scope
66-

sentry_sdk/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"integrations",
1616
# From sentry_sdk.api
1717
"init",
18+
"add_attachment",
1819
"add_breadcrumb",
1920
"capture_event",
2021
"capture_exception",

sentry_sdk/_log_batcher.py

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from typing import Optional, List, Callable, TYPE_CHECKING, Any
66

77
from sentry_sdk.utils import format_timestamp, safe_repr
8-
from sentry_sdk.envelope import Envelope
8+
from sentry_sdk.envelope import Envelope, Item, PayloadRef
99

1010
if TYPE_CHECKING:
1111
from sentry_sdk._types import Log
@@ -97,34 +97,36 @@ def flush(self):
9797
self._flush()
9898

9999
@staticmethod
100-
def _log_to_otel(log):
100+
def _log_to_transport_format(log):
101101
# type: (Log) -> Any
102-
def format_attribute(key, val):
103-
# type: (str, int | float | str | bool) -> Any
102+
def format_attribute(val):
103+
# type: (int | float | str | bool) -> Any
104104
if isinstance(val, bool):
105-
return {"key": key, "value": {"boolValue": val}}
105+
return {"value": val, "type": "boolean"}
106106
if isinstance(val, int):
107-
return {"key": key, "value": {"intValue": str(val)}}
107+
return {"value": val, "type": "integer"}
108108
if isinstance(val, float):
109-
return {"key": key, "value": {"doubleValue": val}}
109+
return {"value": val, "type": "double"}
110110
if isinstance(val, str):
111-
return {"key": key, "value": {"stringValue": val}}
112-
return {"key": key, "value": {"stringValue": safe_repr(val)}}
113-
114-
otel_log = {
115-
"severityText": log["severity_text"],
116-
"severityNumber": log["severity_number"],
117-
"body": {"stringValue": log["body"]},
118-
"timeUnixNano": str(log["time_unix_nano"]),
119-
"attributes": [
120-
format_attribute(k, v) for (k, v) in log["attributes"].items()
121-
],
111+
return {"value": val, "type": "string"}
112+
return {"value": safe_repr(val), "type": "string"}
113+
114+
if "sentry.severity_number" not in log["attributes"]:
115+
log["attributes"]["sentry.severity_number"] = log["severity_number"]
116+
if "sentry.severity_text" not in log["attributes"]:
117+
log["attributes"]["sentry.severity_text"] = log["severity_text"]
118+
119+
res = {
120+
"timestamp": int(log["time_unix_nano"]) / 1.0e9,
121+
"trace_id": log.get("trace_id", "00000000-0000-0000-0000-000000000000"),
122+
"level": str(log["severity_text"]),
123+
"body": str(log["body"]),
124+
"attributes": {
125+
k: format_attribute(v) for (k, v) in log["attributes"].items()
126+
},
122127
}
123128

124-
if "trace_id" in log:
125-
otel_log["traceId"] = log["trace_id"]
126-
127-
return otel_log
129+
return res
128130

129131
def _flush(self):
130132
# type: (...) -> Optional[Envelope]
@@ -133,10 +135,27 @@ def _flush(self):
133135
headers={"sent_at": format_timestamp(datetime.now(timezone.utc))}
134136
)
135137
with self._lock:
136-
for log in self._log_buffer:
137-
envelope.add_log(self._log_to_otel(log))
138+
if len(self._log_buffer) == 0:
139+
return None
140+
141+
envelope.add_item(
142+
Item(
143+
type="log",
144+
content_type="application/vnd.sentry.items.log+json",
145+
headers={
146+
"item_count": len(self._log_buffer),
147+
},
148+
payload=PayloadRef(
149+
json={
150+
"items": [
151+
self._log_to_transport_format(log)
152+
for log in self._log_buffer
153+
]
154+
}
155+
),
156+
)
157+
)
138158
self._log_buffer.clear()
139-
if envelope.items:
140-
self._capture_func(envelope)
141-
return envelope
142-
return None
159+
160+
self._capture_func(envelope)
161+
return envelope

sentry_sdk/api.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ def overload(x):
5151
# When changing this, update __all__ in __init__.py too
5252
__all__ = [
5353
"init",
54+
"add_attachment",
5455
"add_breadcrumb",
5556
"capture_event",
5657
"capture_exception",
@@ -184,6 +185,20 @@ def capture_exception(
184185
return get_current_scope().capture_exception(error, scope=scope, **scope_kwargs)
185186

186187

188+
@scopemethod
189+
def add_attachment(
190+
bytes=None, # type: Union[None, bytes, Callable[[], bytes]]
191+
filename=None, # type: Optional[str]
192+
path=None, # type: Optional[str]
193+
content_type=None, # type: Optional[str]
194+
add_to_transactions=False, # type: bool
195+
):
196+
# type: (...) -> None
197+
return get_isolation_scope().add_attachment(
198+
bytes, filename, path, content_type, add_to_transactions
199+
)
200+
201+
187202
@scopemethod
188203
def add_breadcrumb(
189204
crumb=None, # type: Optional[Breadcrumb]

sentry_sdk/envelope.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,6 @@ def add_sessions(
106106
# type: (...) -> None
107107
self.add_item(Item(payload=PayloadRef(json=sessions), type="sessions"))
108108

109-
def add_log(
110-
self, log # type: Any
111-
):
112-
# type: (...) -> None
113-
self.add_item(Item(payload=PayloadRef(json=log), type="otel_log"))
114-
115109
def add_item(
116110
self, item # type: Item
117111
):
@@ -278,7 +272,7 @@ def data_category(self):
278272
return "transaction"
279273
elif ty == "event":
280274
return "error"
281-
elif ty == "otel_log":
275+
elif ty == "log":
282276
return "log"
283277
elif ty == "client_report":
284278
return "internal"

sentry_sdk/feature_flags.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def add_feature_flag(flag, result):
6464
Records a flag and its value to be sent on subsequent error events.
6565
We recommend you do this on flag evaluations. Flags are buffered per Sentry scope.
6666
"""
67-
flags = sentry_sdk.get_current_scope().flags
67+
flags = sentry_sdk.get_isolation_scope().flags
6868
flags.set(flag, result)
6969

7070
span = sentry_sdk.get_current_span()

sentry_sdk/integrations/logging.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ def _capture_log_from_record(client, record):
355355
# type: (BaseClient, LogRecord) -> None
356356
scope = sentry_sdk.get_current_scope()
357357
otel_severity_number, otel_severity_text = _python_level_to_otel(record.levelno)
358+
project_root = client.options["project_root"]
358359
attrs = {
359360
"sentry.origin": "auto.logger.log",
360361
} # type: dict[str, str | bool | float | int]
@@ -374,7 +375,10 @@ def _capture_log_from_record(client, record):
374375
if record.lineno:
375376
attrs["code.line.number"] = record.lineno
376377
if record.pathname:
377-
attrs["code.file.path"] = record.pathname
378+
if project_root is not None and record.pathname.startswith(project_root):
379+
attrs["code.file.path"] = record.pathname[len(project_root) + 1 :]
380+
else:
381+
attrs["code.file.path"] = record.pathname
378382
if record.funcName:
379383
attrs["code.function.name"] = record.funcName
380384

tests/integrations/fastapi/test_fastapi.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
from fastapi.testclient import TestClient
1111
from fastapi.middleware.trustedhost import TrustedHostMiddleware
1212

13+
import sentry_sdk
1314
from sentry_sdk import capture_message
15+
from sentry_sdk.feature_flags import add_feature_flag
1416
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
1517
from sentry_sdk.integrations.fastapi import FastApiIntegration
1618
from sentry_sdk.integrations.starlette import StarletteIntegration
@@ -714,3 +716,41 @@ async def subapp_route():
714716
assert event["transaction"] == "/subapp"
715717
else:
716718
assert event["transaction"].endswith("subapp_route")
719+
720+
721+
@pytest.mark.asyncio
722+
async def test_feature_flags(sentry_init, capture_events):
723+
sentry_init(
724+
traces_sample_rate=1.0,
725+
integrations=[StarletteIntegration(), FastApiIntegration()],
726+
)
727+
728+
events = capture_events()
729+
730+
app = FastAPI()
731+
732+
@app.get("/error")
733+
async def _error():
734+
add_feature_flag("hello", False)
735+
736+
with sentry_sdk.start_span(name="test-span"):
737+
with sentry_sdk.start_span(name="test-span-2"):
738+
raise ValueError("something is wrong!")
739+
740+
try:
741+
client = TestClient(app)
742+
client.get("/error")
743+
except ValueError:
744+
pass
745+
746+
found = False
747+
for event in events:
748+
if "exception" in event.keys():
749+
assert event["contexts"]["flags"] == {
750+
"values": [
751+
{"flag": "hello", "result": False},
752+
]
753+
}
754+
found = True
755+
756+
assert found, "No event with exception found"

tests/test_basics.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,14 +1151,12 @@ def recurse():
11511151
(event,) = events
11521152

11531153
assert event["exception"]["values"][0]["stacktrace"] is None
1154-
assert event["_meta"] == {
1155-
"exception": {
1156-
"values": {"0": {"stacktrace": {"": {"rem": [["!config", "x"]]}}}}
1157-
}
1154+
assert event["_meta"]["exception"] == {
1155+
"values": {"0": {"stacktrace": {"": {"rem": [["!config", "x"]]}}}}
11581156
}
11591157

11601158
# On my machine, it takes about 100-200ms to capture the exception,
11611159
# so this limit should be generous enough.
11621160
assert (
1163-
capture_end_time - capture_start_time < 10**9
1161+
capture_end_time - capture_start_time < 10**9 * 2
11641162
), "stacktrace capture took too long, check that frame limit is set correctly"

0 commit comments

Comments
 (0)