Skip to content

Commit 1f39f82

Browse files
feat(logs): Record discarded log bytes
1 parent 5de66e2 commit 1f39f82

File tree

4 files changed

+178
-8
lines changed

4 files changed

+178
-8
lines changed

sentry_sdk/_log_batcher.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ def add(
8686
self._record_lost_func(
8787
reason="queue_overflow",
8888
data_category="log_item",
89+
item=LogBatcher._log_to_transport_format(log),
8990
quantity=1,
9091
)
9192
return None

sentry_sdk/transport.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,11 @@ def record_lost_event(
266266
)
267267
self.record_lost_event(reason, "span", quantity=span_count)
268268

269+
elif data_category == "log_item" and item:
270+
# Also record size of lost logs in bytes
271+
bytes_size = len(item.get_bytes())
272+
self.record_lost_event(reason, "log_byte", quantity=bytes_size)
273+
269274
elif data_category == "attachment":
270275
# quantity of 0 is actually 1 as we do not want to count
271276
# empty attachments as actually empty.

tests/test_logs.py

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import time
55
from typing import List, Any, Mapping, Union
66
import pytest
7+
from unittest import mock
78

89
import sentry_sdk
910
import sentry_sdk.logger
@@ -450,7 +451,7 @@ def test_logs_with_literal_braces(
450451

451452
@minimum_python_37
452453
def test_batcher_drops_logs(sentry_init, monkeypatch):
453-
sentry_init(enable_logs=True)
454+
sentry_init(enable_logs=True, server_name="test-server", release="1.0.0")
454455
client = sentry_sdk.get_client()
455456

456457
def no_op_flush():
@@ -469,5 +470,52 @@ def record_lost_event(reason, data_category=None, item=None, *, quantity=1):
469470
sentry_sdk.logger.info("This is a 'info' log...")
470471

471472
assert len(lost_event_calls) == 5
473+
474+
expected_dropped_item = {
475+
"body": "This is a 'info' log...",
476+
"level": "info",
477+
"timestamp": mock.ANY,
478+
"trace_id": mock.ANY,
479+
"attributes": {
480+
"sentry.environment": {
481+
"type": "string",
482+
"value": "production",
483+
},
484+
"sentry.release": {
485+
"type": "string",
486+
"value": "1.0.0",
487+
},
488+
"sentry.sdk.name": {
489+
"type": "string",
490+
"value": mock.ANY,
491+
},
492+
"sentry.sdk.version": {
493+
"type": "string",
494+
"value": VERSION,
495+
},
496+
"sentry.severity_number": {
497+
"type": "integer",
498+
"value": 9,
499+
},
500+
"sentry.severity_text": {
501+
"type": "string",
502+
"value": "info",
503+
},
504+
"sentry.trace.parent_span_id": {
505+
"type": "string",
506+
"value": mock.ANY,
507+
},
508+
"server.address": {
509+
"type": "string",
510+
"value": "test-server",
511+
},
512+
},
513+
}
514+
472515
for lost_event_call in lost_event_calls:
473-
assert lost_event_call == ("queue_overflow", "log_item", None, 1)
516+
assert lost_event_call == (
517+
"queue_overflow",
518+
"log_item",
519+
expected_dropped_item,
520+
1,
521+
)

tests/test_transport.py

Lines changed: 122 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
Hub,
2626
)
2727
from sentry_sdk._compat import PY37, PY38
28-
from sentry_sdk.envelope import Envelope, Item, parse_json
28+
from sentry_sdk.envelope import Envelope, Item, parse_json, PayloadRef
2929
from sentry_sdk.transport import (
3030
KEEP_ALIVE_SOCKET_OPTIONS,
3131
_parse_rate_limits,
@@ -591,7 +591,110 @@ def test_complex_limits_without_data_category(
591591

592592

593593
@pytest.mark.parametrize("response_code", [200, 429])
594-
def test_log_item_limits(capturing_server, response_code, make_client):
594+
@pytest.mark.parametrize(
595+
"item",
596+
[
597+
Item(payload=b"{}", type="log"),
598+
Item(
599+
type="log",
600+
content_type="application/vnd.sentry.items.log+json",
601+
headers={
602+
"item_count": 2,
603+
},
604+
payload=PayloadRef(
605+
json={
606+
"items": [
607+
{
608+
"body": "This is a 'info' log...",
609+
"level": "info",
610+
"timestamp": datetime(
611+
2025, 1, 1, tzinfo=timezone.utc
612+
).timestamp(),
613+
"trace_id": "00000000-0000-0000-0000-000000000000",
614+
"attributes": {
615+
"sentry.environment": {
616+
"value": "production",
617+
"type": "string",
618+
},
619+
"sentry.release": {
620+
"value": "1.0.0",
621+
"type": "string",
622+
},
623+
"sentry.sdk.name": {
624+
"value": "sentry.python",
625+
"type": "string",
626+
},
627+
"sentry.sdk.version": {
628+
"value": "2.45.0",
629+
"type": "string",
630+
},
631+
"sentry.severity_number": {
632+
"value": 9,
633+
"type": "integer",
634+
},
635+
"sentry.severity_text": {
636+
"value": "info",
637+
"type": "string",
638+
},
639+
"server.address": {
640+
"value": "test-server",
641+
"type": "string",
642+
},
643+
},
644+
},
645+
{
646+
"body": "The recorded value was '2.0'",
647+
"level": "warn",
648+
"timestamp": datetime(
649+
2025, 1, 1, tzinfo=timezone.utc
650+
).timestamp(),
651+
"trace_id": "00000000-0000-0000-0000-000000000000",
652+
"attributes": {
653+
"sentry.message.parameter.float_var": {
654+
"value": 2.0,
655+
"type": "double",
656+
},
657+
"sentry.message.template": {
658+
"value": "The recorded value was '{float_var}'",
659+
"type": "string",
660+
},
661+
"sentry.sdk.name": {
662+
"value": "sentry.python",
663+
"type": "string",
664+
},
665+
"sentry.sdk.version": {
666+
"value": "2.45.0",
667+
"type": "string",
668+
},
669+
"server.address": {
670+
"value": "test-server",
671+
"type": "string",
672+
},
673+
"sentry.environment": {
674+
"value": "production",
675+
"type": "string",
676+
},
677+
"sentry.release": {
678+
"value": "1.0.0",
679+
"type": "string",
680+
},
681+
"sentry.severity_number": {
682+
"value": 13,
683+
"type": "integer",
684+
},
685+
"sentry.severity_text": {
686+
"value": "warn",
687+
"type": "string",
688+
},
689+
},
690+
},
691+
]
692+
}
693+
),
694+
),
695+
],
696+
)
697+
def test_log_item_limits(capturing_server, response_code, item, make_client):
595698
client = make_client()
596699
capturing_server.respond_with(
597700
code=response_code,
@@ -601,7 +704,7 @@ def test_log_item_limits(capturing_server, response_code, make_client):
601704
)
602705

603706
envelope = Envelope()
604-
envelope.add_item(Item(payload=b"{}", type="log"))
707+
envelope.add_item(item)
605708
client.transport.capture_envelope(envelope)
606709
client.flush()
607710

@@ -622,9 +725,22 @@ def test_log_item_limits(capturing_server, response_code, make_client):
622725
envelope = capturing_server.captured[1].envelope
623726
assert envelope.items[0].type == "client_report"
624727
report = parse_json(envelope.items[0].get_bytes())
625-
assert report["discarded_events"] == [
626-
{"category": "log_item", "reason": "ratelimit_backoff", "quantity": 1},
627-
]
728+
729+
assert {
730+
"category": "log_item",
731+
"reason": "ratelimit_backoff",
732+
"quantity": 1,
733+
} in report["discarded_events"]
734+
735+
expected_lost_bytes = 1243
736+
if item.payload.bytes == b"{}":
737+
expected_lost_bytes = 2
738+
739+
assert {
740+
"category": "log_byte",
741+
"reason": "ratelimit_backoff",
742+
"quantity": expected_lost_bytes,
743+
} in report["discarded_events"]
628744

629745

630746
def test_hub_cls_backwards_compat():

0 commit comments

Comments
 (0)