Skip to content

feat(logs): Add support for dict args #4478

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions sentry_sdk/integrations/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,13 @@ def _capture_log_from_record(self, client, record):
if isinstance(arg, (str, float, int, bool))
else safe_repr(arg)
)
elif isinstance(record.args, dict):
for key, value in record.args.items():
attrs[f"sentry.message.parameter.{key}"] = (
value
if isinstance(value, (str, float, int, bool))
else safe_repr(value)
)
if record.lineno:
attrs["code.line.number"] = record.lineno
if record.pathname:
Expand Down
79 changes: 79 additions & 0 deletions tests/integrations/logging/test_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -492,3 +492,82 @@ def test_logger_with_all_attributes(sentry_init, capture_envelopes):
"sentry.severity_number": 13,
"sentry.severity_text": "warn",
}


def test_sentry_logs_named_parameters(sentry_init, capture_envelopes):
"""
The python logger module should capture named parameters from dictionary arguments in Sentry logs.
"""
sentry_init(_experiments={"enable_logs": True})
envelopes = capture_envelopes()

python_logger = logging.Logger("test-logger")
python_logger.info(
"%(source)s call completed, %(input_tk)i input tk, %(output_tk)i output tk (model %(model)s, cost $%(cost).4f)",
{
"source": "test_source",
"input_tk": 100,
"output_tk": 50,
"model": "gpt-4",
"cost": 0.0234,
},
)

get_client().flush()
logs = envelopes_to_logs(envelopes)

assert len(logs) == 1
attrs = logs[0]["attributes"]

# Check that the template is captured
assert (
attrs["sentry.message.template"]
== "%(source)s call completed, %(input_tk)i input tk, %(output_tk)i output tk (model %(model)s, cost $%(cost).4f)"
)

# Check that dictionary arguments are captured as named parameters
assert attrs["sentry.message.parameter.source"] == "test_source"
assert attrs["sentry.message.parameter.input_tk"] == 100
assert attrs["sentry.message.parameter.output_tk"] == 50
assert attrs["sentry.message.parameter.model"] == "gpt-4"
assert attrs["sentry.message.parameter.cost"] == 0.0234

# Check other standard attributes
assert attrs["logger.name"] == "test-logger"
assert attrs["sentry.origin"] == "auto.logger.log"
assert logs[0]["severity_number"] == 9 # info level
assert logs[0]["severity_text"] == "info"


def test_sentry_logs_named_parameters_complex_values(sentry_init, capture_envelopes):
"""
The python logger module should handle complex values in named parameters using safe_repr.
"""
sentry_init(_experiments={"enable_logs": True})
envelopes = capture_envelopes()

python_logger = logging.Logger("test-logger")
complex_object = {"nested": {"data": [1, 2, 3]}, "tuple": (4, 5, 6)}
python_logger.warning(
"Processing %(simple)s with %(complex)s data",
{
"simple": "simple_value",
"complex": complex_object,
},
)

get_client().flush()
logs = envelopes_to_logs(envelopes)

assert len(logs) == 1
attrs = logs[0]["attributes"]

# Check that simple values are kept as-is
assert attrs["sentry.message.parameter.simple"] == "simple_value"

# Check that complex values are converted using safe_repr
assert "sentry.message.parameter.complex" in attrs
complex_param = attrs["sentry.message.parameter.complex"]
assert isinstance(complex_param, str)
assert "nested" in complex_param
assert "data" in complex_param
Loading