logger.py 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. # NOTE: this is the logger sentry exposes to users, not some generic logger.
  2. import functools
  3. import time
  4. from typing import Any
  5. from sentry_sdk import get_client
  6. from sentry_sdk.utils import safe_repr
  7. OTEL_RANGES = [
  8. # ((severity level range), severity text)
  9. # https://opentelemetry.io/docs/specs/otel/logs/data-model
  10. ((1, 4), "trace"),
  11. ((5, 8), "debug"),
  12. ((9, 12), "info"),
  13. ((13, 16), "warn"),
  14. ((17, 20), "error"),
  15. ((21, 24), "fatal"),
  16. ]
  17. def _capture_log(severity_text, severity_number, template, **kwargs):
  18. # type: (str, int, str, **Any) -> None
  19. client = get_client()
  20. attrs = {} # type: dict[str, str | bool | float | int]
  21. if "attributes" in kwargs:
  22. attrs.update(kwargs.pop("attributes"))
  23. for k, v in kwargs.items():
  24. attrs[f"sentry.message.parameter.{k}"] = v
  25. if kwargs:
  26. # only attach template if there are parameters
  27. attrs["sentry.message.template"] = template
  28. attrs = {
  29. k: (
  30. v
  31. if (
  32. isinstance(v, str)
  33. or isinstance(v, int)
  34. or isinstance(v, bool)
  35. or isinstance(v, float)
  36. )
  37. else safe_repr(v)
  38. )
  39. for (k, v) in attrs.items()
  40. }
  41. # noinspection PyProtectedMember
  42. client._capture_log(
  43. {
  44. "severity_text": severity_text,
  45. "severity_number": severity_number,
  46. "attributes": attrs,
  47. "body": template.format(**kwargs),
  48. "time_unix_nano": time.time_ns(),
  49. "trace_id": None,
  50. },
  51. )
  52. trace = functools.partial(_capture_log, "trace", 1)
  53. debug = functools.partial(_capture_log, "debug", 5)
  54. info = functools.partial(_capture_log, "info", 9)
  55. warning = functools.partial(_capture_log, "warn", 13)
  56. error = functools.partial(_capture_log, "error", 17)
  57. fatal = functools.partial(_capture_log, "fatal", 21)
  58. def _otel_severity_text(otel_severity_number):
  59. # type: (int) -> str
  60. for (lower, upper), severity in OTEL_RANGES:
  61. if lower <= otel_severity_number <= upper:
  62. return severity
  63. return "default"
  64. def _log_level_to_otel(level, mapping):
  65. # type: (int, dict[Any, int]) -> tuple[int, str]
  66. for py_level, otel_severity_number in sorted(mapping.items(), reverse=True):
  67. if level >= py_level:
  68. return otel_severity_number, _otel_severity_text(otel_severity_number)
  69. return 0, "default"