diff --git a/sentry_sdk/integrations/logging.py b/sentry_sdk/integrations/logging.py index 15ff2ed233..bfb30fc67b 100644 --- a/sentry_sdk/integrations/logging.py +++ b/sentry_sdk/integrations/logging.py @@ -356,12 +356,14 @@ def _capture_log_from_record(self, client, record): record.levelno, SEVERITY_TO_OTEL_SEVERITY ) project_root = client.options["project_root"] + attrs = self._extra_from_record(record) # type: Any attrs["sentry.origin"] = "auto.logger.log" - if isinstance(record.msg, str): - attrs["sentry.message.template"] = record.msg + + parameters_set = False if record.args is not None: if isinstance(record.args, tuple): + parameters_set = bool(record.args) for i, arg in enumerate(record.args): attrs[f"sentry.message.parameter.{i}"] = ( arg @@ -369,19 +371,28 @@ def _capture_log_from_record(self, client, record): else safe_repr(arg) ) elif isinstance(record.args, dict): + parameters_set = bool(record.args) 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 parameters_set and isinstance(record.msg, str): + # only include template if there is at least one + # sentry.message.parameter.X set + attrs["sentry.message.template"] = record.msg + if record.lineno: attrs["code.line.number"] = record.lineno + if record.pathname: if project_root is not None and record.pathname.startswith(project_root): attrs["code.file.path"] = record.pathname[len(project_root) + 1 :] else: attrs["code.file.path"] = record.pathname + if record.funcName: attrs["code.function.name"] = record.funcName diff --git a/sentry_sdk/logger.py b/sentry_sdk/logger.py index c18cf91ff2..bc98f35155 100644 --- a/sentry_sdk/logger.py +++ b/sentry_sdk/logger.py @@ -22,13 +22,14 @@ def _capture_log(severity_text, severity_number, template, **kwargs): # type: (str, int, str, **Any) -> None client = get_client() - attrs = { - "sentry.message.template": template, - } # type: dict[str, str | bool | float | int] + attrs = {} # type: dict[str, str | bool | float | int] if "attributes" in kwargs: attrs.update(kwargs.pop("attributes")) for k, v in kwargs.items(): attrs[f"sentry.message.parameter.{k}"] = v + if kwargs: + # only attach template if there are parameters + attrs["sentry.message.template"] = template attrs = { k: ( diff --git a/tests/integrations/logging/test_logging.py b/tests/integrations/logging/test_logging.py index 7ecdf42500..7a00ceadd2 100644 --- a/tests/integrations/logging/test_logging.py +++ b/tests/integrations/logging/test_logging.py @@ -571,3 +571,22 @@ def test_sentry_logs_named_parameters_complex_values(sentry_init, capture_envelo assert isinstance(complex_param, str) assert "nested" in complex_param assert "data" in complex_param + + +def test_sentry_logs_no_parameters_no_template(sentry_init, capture_envelopes): + """ + There shouldn't be a template if there are no parameters. + """ + sentry_init(enable_logs=True) + envelopes = capture_envelopes() + + python_logger = logging.Logger("test-logger") + python_logger.warning("Warning about something without any parameters.") + + get_client().flush() + logs = envelopes_to_logs(envelopes) + + assert len(logs) == 1 + + attrs = logs[0]["attributes"] + assert "sentry.message.template" not in attrs diff --git a/tests/integrations/loguru/test_loguru.py b/tests/integrations/loguru/test_loguru.py index 38093d24cb..3d04d7d1ea 100644 --- a/tests/integrations/loguru/test_loguru.py +++ b/tests/integrations/loguru/test_loguru.py @@ -467,3 +467,21 @@ def test_logger_with_all_attributes( "sentry.severity_number": 13, "sentry.severity_text": "warn", } + + +def test_no_parameters_no_template( + sentry_init, capture_envelopes, uninstall_integration, request +): + uninstall_integration("loguru") + request.addfinalizer(logger.remove) + + sentry_init(enable_logs=True) + envelopes = capture_envelopes() + + logger.warning("Logging a hardcoded warning") + sentry_sdk.get_client().flush() + + logs = envelopes_to_logs(envelopes) + + attributes = logs[0]["attributes"] + assert "sentry.message.template" not in attributes diff --git a/tests/test_logs.py b/tests/test_logs.py index b2578d83d5..596a31922e 100644 --- a/tests/test_logs.py +++ b/tests/test_logs.py @@ -254,30 +254,54 @@ def test_logs_message_params(sentry_init, capture_envelopes): sentry_sdk.logger.error( "The recorded error was '{error}'", error=Exception("some error") ) + sentry_sdk.logger.warning("The recorded value was hardcoded.") get_client().flush() logs = envelopes_to_logs(envelopes) assert logs[0]["body"] == "The recorded value was '1'" assert logs[0]["attributes"]["sentry.message.parameter.int_var"] == 1 + assert ( + logs[0]["attributes"]["sentry.message.template"] + == "The recorded value was '{int_var}'" + ) assert logs[1]["body"] == "The recorded value was '2.0'" assert logs[1]["attributes"]["sentry.message.parameter.float_var"] == 2.0 + assert ( + logs[1]["attributes"]["sentry.message.template"] + == "The recorded value was '{float_var}'" + ) assert logs[2]["body"] == "The recorded value was 'False'" assert logs[2]["attributes"]["sentry.message.parameter.bool_var"] is False + assert ( + logs[2]["attributes"]["sentry.message.template"] + == "The recorded value was '{bool_var}'" + ) assert logs[3]["body"] == "The recorded value was 'some string value'" assert ( logs[3]["attributes"]["sentry.message.parameter.string_var"] == "some string value" ) + assert ( + logs[3]["attributes"]["sentry.message.template"] + == "The recorded value was '{string_var}'" + ) assert logs[4]["body"] == "The recorded error was 'some error'" assert ( logs[4]["attributes"]["sentry.message.parameter.error"] == "Exception('some error')" ) + assert ( + logs[4]["attributes"]["sentry.message.template"] + == "The recorded error was '{error}'" + ) + + assert logs[5]["body"] == "The recorded value was hardcoded." + assert "sentry.message.template" not in logs[5]["attributes"] @minimum_python_37