From 7c28d144d1e013239e6e2c01783e3808b33914d2 Mon Sep 17 00:00:00 2001 From: Colin Chartier Date: Tue, 1 Apr 2025 12:55:15 -0400 Subject: [PATCH 1/7] fix(ourlogs): Use repr instead of json for message and arguments --- sentry_sdk/integrations/logging.py | 5 ++--- tests/test_logs.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/sentry_sdk/integrations/logging.py b/sentry_sdk/integrations/logging.py index 2114f4867a..0a5a2c888a 100644 --- a/sentry_sdk/integrations/logging.py +++ b/sentry_sdk/integrations/logging.py @@ -1,4 +1,3 @@ -import json import logging from datetime import datetime, timezone from fnmatch import fnmatch @@ -360,14 +359,14 @@ def _capture_log_from_record(client, record): otel_severity_number, otel_severity_text = _python_level_to_otel(record.levelno) attrs = { "sentry.message.template": ( - record.msg if isinstance(record.msg, str) else json.dumps(record.msg) + record.msg if isinstance(record.msg, str) else repr(record.msg) ), } # type: dict[str, str | bool | float | int] if record.args is not None: if isinstance(record.args, tuple): for i, arg in enumerate(record.args): attrs[f"sentry.message.parameters.{i}"] = ( - arg if isinstance(arg, str) else json.dumps(arg) + arg if isinstance(arg, str) else repr(arg) ) if record.lineno: attrs["code.line.number"] = record.lineno diff --git a/tests/test_logs.py b/tests/test_logs.py index 9527fb9807..f116d1eebb 100644 --- a/tests/test_logs.py +++ b/tests/test_logs.py @@ -281,3 +281,17 @@ def test_no_log_infinite_loop(sentry_init, capture_envelopes): python_logger.debug("this is %s a template %s", "1", "2") assert len(envelopes) == 1 + + +@minimum_python_37 +def test_logging_errors(sentry_init, capture_envelopes): + """ + The python logger module should be able to log errors without erroring + """ + sentry_init(_experiments={"enable_sentry_logs": True}) + envelopes = capture_envelopes() + + python_logger = logging.Logger("test-logger") + python_logger.error(Exception("test exc")) + + assert len(envelopes) == 2 From 5da81d5dbe7ad8348d55946278ca3c9c0176e418 Mon Sep 17 00:00:00 2001 From: Colin Chartier Date: Tue, 1 Apr 2025 12:59:51 -0400 Subject: [PATCH 2/7] add another test case --- tests/test_logs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_logs.py b/tests/test_logs.py index f116d1eebb..0e857c4427 100644 --- a/tests/test_logs.py +++ b/tests/test_logs.py @@ -293,5 +293,6 @@ def test_logging_errors(sentry_init, capture_envelopes): python_logger = logging.Logger("test-logger") python_logger.error(Exception("test exc")) + python_logger.error("error is %s", Exception("test exc")) - assert len(envelopes) == 2 + assert len(envelopes) == 4 From 8992f7cfdecb0c76b9c3a8287beb85a39e48e0d4 Mon Sep 17 00:00:00 2001 From: Colin Chartier Date: Tue, 1 Apr 2025 13:08:45 -0400 Subject: [PATCH 3/7] Do not use a repr for template --- sentry_sdk/integrations/logging.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sentry_sdk/integrations/logging.py b/sentry_sdk/integrations/logging.py index 0a5a2c888a..5c7bac939b 100644 --- a/sentry_sdk/integrations/logging.py +++ b/sentry_sdk/integrations/logging.py @@ -357,11 +357,9 @@ def _capture_log_from_record(client, record): # type: (BaseClient, LogRecord) -> None scope = sentry_sdk.get_current_scope() otel_severity_number, otel_severity_text = _python_level_to_otel(record.levelno) - attrs = { - "sentry.message.template": ( - record.msg if isinstance(record.msg, str) else repr(record.msg) - ), - } # type: dict[str, str | bool | float | int] + attrs = {} # type: dict[str, str | bool | float | int] + if isinstance(record.msg, str): + attrs["sentry.message.template"] = record.msg if record.args is not None: if isinstance(record.args, tuple): for i, arg in enumerate(record.args): From 6c2b848094d92ca0d68c505b2d357decf41d6f70 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 2 Apr 2025 09:36:53 +0200 Subject: [PATCH 4/7] More asserts in tests --- tests/test_logs.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/tests/test_logs.py b/tests/test_logs.py index 0e857c4427..4c1b10e6df 100644 --- a/tests/test_logs.py +++ b/tests/test_logs.py @@ -292,7 +292,30 @@ def test_logging_errors(sentry_init, capture_envelopes): envelopes = capture_envelopes() python_logger = logging.Logger("test-logger") - python_logger.error(Exception("test exc")) - python_logger.error("error is %s", Exception("test exc")) + python_logger.error(Exception("test exc 1")) + python_logger.error("error is %s", Exception("test exc 2")) + + error_event_1 = envelopes[0].items[0].payload.json + assert error_event_1["level"] == "error" + + log_event_1 = envelopes[1].items[0].payload.json + assert log_event_1["severityText"] == "error" + # If only logging an exception, there is no "sentry.message.template" or "sentry.message.parameters.0" + assert len(log_event_1["attributes"]) == 10 + assert log_event_1["attributes"][0]["key"] == "code.line.number" + + error_event_2 = envelopes[2].items[0].payload.json + assert error_event_2["level"] == "error" + + log_event_2 = envelopes[3].items[0].payload.json + assert log_event_2["severityText"] == "error" + assert len(log_event_2["attributes"]) == 12 + assert log_event_2["attributes"][0]["key"] == "sentry.message.template" + assert log_event_2["attributes"][0]["value"] == {"stringValue": "error is %s"} + assert log_event_2["attributes"][1]["key"] == "sentry.message.parameters.0" + assert log_event_2["attributes"][1]["value"] == { + "stringValue": "Exception('test exc 2')" + } + assert log_event_2["attributes"][2]["key"] == "code.line.number" assert len(envelopes) == 4 From 73fc8a98ea51b696fca02867bfdc5b30b5f368a4 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 2 Apr 2025 09:38:25 +0200 Subject: [PATCH 5/7] Using safe_repr --- sentry_sdk/integrations/logging.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/integrations/logging.py b/sentry_sdk/integrations/logging.py index 5c7bac939b..7822608de8 100644 --- a/sentry_sdk/integrations/logging.py +++ b/sentry_sdk/integrations/logging.py @@ -5,6 +5,7 @@ import sentry_sdk from sentry_sdk.client import BaseClient from sentry_sdk.utils import ( + safe_repr, to_string, event_from_exception, current_stacktrace, @@ -364,7 +365,7 @@ def _capture_log_from_record(client, record): if isinstance(record.args, tuple): for i, arg in enumerate(record.args): attrs[f"sentry.message.parameters.{i}"] = ( - arg if isinstance(arg, str) else repr(arg) + arg if isinstance(arg, str) else safe_repr(arg) ) if record.lineno: attrs["code.line.number"] = record.lineno From 94731cb4bcb351ea2c1c1ffd817c4bfee4af1503 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 2 Apr 2025 09:39:13 +0200 Subject: [PATCH 6/7] grammar --- tests/test_logs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_logs.py b/tests/test_logs.py index 4c1b10e6df..7ef708ceb1 100644 --- a/tests/test_logs.py +++ b/tests/test_logs.py @@ -300,7 +300,7 @@ def test_logging_errors(sentry_init, capture_envelopes): log_event_1 = envelopes[1].items[0].payload.json assert log_event_1["severityText"] == "error" - # If only logging an exception, there is no "sentry.message.template" or "sentry.message.parameters.0" + # When only logging an exception, there is no "sentry.message.template" or "sentry.message.parameters.0" assert len(log_event_1["attributes"]) == 10 assert log_event_1["attributes"][0]["key"] == "code.line.number" From 2b6fcbc0b1895421b31018587f065331ab4c7762 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Wed, 2 Apr 2025 13:59:28 +0200 Subject: [PATCH 7/7] trigger ci