Skip to content

Commit 4a34301

Browse files
committed
feat(ourlogs): Add alpha version of logger
1 parent a97c53c commit 4a34301

File tree

6 files changed

+102
-1
lines changed

6 files changed

+102
-1
lines changed

sentry_sdk/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"start_transaction",
4646
"trace",
4747
"monitor",
48+
"logger",
4849
]
4950

5051
# Initialize the debug support after everything is loaded

sentry_sdk/_types.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ class SDKInfo(TypedDict):
207207
]
208208

209209
Hint = Dict[str, Any]
210+
Log = Dict[str, Any]
210211

211212
Breadcrumb = Dict[str, Any]
212213
BreadcrumbHint = Dict[str, Any]
@@ -217,6 +218,7 @@ class SDKInfo(TypedDict):
217218
ErrorProcessor = Callable[[Event, ExcInfo], Optional[Event]]
218219
BreadcrumbProcessor = Callable[[Breadcrumb, BreadcrumbHint], Optional[Breadcrumb]]
219220
TransactionProcessor = Callable[[Event, Hint], Optional[Event]]
221+
LogProcessor = Callable[[Log, Hint], Optional[Log]]
220222

221223
TracesSampler = Callable[[SamplingContext], Union[float, int, bool]]
222224

sentry_sdk/client.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import json
12
import os
3+
import time
24
import uuid
35
import random
46
import socket
@@ -8,6 +10,7 @@
810
from typing import TYPE_CHECKING, List, Dict, cast, overload
911
import warnings
1012

13+
from sentry_sdk import get_current_scope
1114
from sentry_sdk._compat import PY37, check_uwsgi_thread_support
1215
from sentry_sdk.utils import (
1316
AnnotatedValue,
@@ -206,6 +209,10 @@ def capture_event(self, *args, **kwargs):
206209
# type: (*Any, **Any) -> Optional[str]
207210
return None
208211

212+
def capture_log(self, severity_text, severity_number, template, **kwargs):
213+
# type: (str, int, str, **Any) -> None
214+
pass
215+
209216
def capture_session(self, *args, **kwargs):
210217
# type: (*Any, **Any) -> None
211218
return None
@@ -847,6 +854,69 @@ def capture_event(
847854

848855
return return_value
849856

857+
def capture_log(self, severity_text, severity_number, template, **kwargs):
858+
# type: (str, int, str, **Any) -> None
859+
if not self.options.get("enable_sentry_logs", False):
860+
return
861+
862+
headers = {
863+
"sent_at": format_timestamp(datetime.now(timezone.utc)),
864+
} # type: dict[str, object]
865+
866+
def format_attribute(key: str, val: int | float | str | bool):
867+
if isinstance(val, int):
868+
return {"key": key, "value": {"int_value": val}}
869+
if isinstance(val, str):
870+
return {"key": key, "value": {"string_value": val}}
871+
if isinstance(val, float):
872+
return {"key": key, "value": {"double_value": val}}
873+
if isinstance(val, bool):
874+
return {"key": key, "value": {"bool_value": val}}
875+
return {"key": key, "value": {"string_value": json.dumps(val)}}
876+
877+
attrs = {
878+
"sentry.message.template": template,
879+
}
880+
if (extra_attrs := kwargs.get("attributes")) is not None:
881+
attrs.update(extra_attrs)
882+
if (env := self.options.get("environment")) is not None:
883+
attrs["sentry.environment"] = env
884+
if (release := self.options.get("release")) is not None:
885+
attrs["sentry.release"] = release
886+
for k, v in kwargs.items():
887+
attrs[f"sentry.message.parameters.{k}"] = v
888+
889+
log = {
890+
"severity_text": severity_text,
891+
"severity_number": severity_number,
892+
"body": template.format(**kwargs),
893+
"attributes": attrs,
894+
"time_unix_nano": time.time_ns(),
895+
}
896+
897+
if (ctx := get_current_scope().get_active_propagation_context()) is not None:
898+
headers["trace_id"] = ctx.trace_id
899+
log["trace_id"] = ctx.trace_id
900+
envelope = Envelope(headers=headers)
901+
902+
before_send_log = self.options.get("before_send_log")
903+
if before_send_log is not None:
904+
log = before_send_log(log)
905+
906+
# convert to otel form - otel_log has a different schema than just 'log'
907+
log["body"] = {"string_value": log["body"]}
908+
log["attributes"] = [
909+
format_attribute(k, v) for (k, v) in log["attributes"].items()
910+
]
911+
912+
envelope.add_log(log) # TODO: batch these
913+
if self.spotlight:
914+
self.spotlight.capture_envelope(envelope)
915+
916+
if self.transport is not None:
917+
self.transport.capture_envelope(envelope)
918+
return None
919+
850920
def capture_session(
851921
self, session # type: Session
852922
):

sentry_sdk/consts.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class CompressionAlgo(Enum):
5353
TransactionProcessor,
5454
MetricTags,
5555
MetricValue,
56+
LogProcessor,
5657
)
5758

5859
# Experiments are feature flags to enable and disable certain unstable SDK
@@ -539,6 +540,8 @@ def __init__(
539540
proxy_headers=None, # type: Optional[Dict[str, str]]
540541
instrumenter=INSTRUMENTER.SENTRY, # type: Optional[str]
541542
before_send_transaction=None, # type: Optional[TransactionProcessor]
543+
enable_sentry_logs=False, # type: bool
544+
before_send_log=None, # type: Optional[LogProcessor]
542545
project_root=None, # type: Optional[str]
543546
enable_tracing=None, # type: Optional[bool]
544547
include_local_variables=True, # type: Optional[bool]

sentry_sdk/envelope.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from typing import List
1616
from typing import Iterator
1717

18-
from sentry_sdk._types import Event, EventDataCategory
18+
from sentry_sdk._types import Event, EventDataCategory, Log
1919

2020

2121
def parse_json(data):
@@ -102,6 +102,12 @@ def add_sessions(
102102
# type: (...) -> None
103103
self.add_item(Item(payload=PayloadRef(json=sessions), type="sessions"))
104104

105+
def add_log(
106+
self, log # type: Log
107+
):
108+
# type: (...) -> None
109+
self.add_item(Item(payload=PayloadRef(json=log), type="otel_log"))
110+
105111
def add_item(
106112
self, item # type: Item
107113
):

sentry_sdk/logger.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# NOTE: this is the logger sentry exposes to users, not some generic logger.
2+
import functools
3+
from typing import Any, Optional
4+
5+
from sentry_sdk import get_client
6+
7+
8+
def capture_log(severity_text, severity_number, template, **kwargs):
9+
# type: (str, int, str, **Any) -> Optional[str]
10+
client = get_client()
11+
return client.capture_log(severity_text, severity_number, template, **kwargs)
12+
13+
14+
trace = functools.partial(capture_log, "trace", 1)
15+
debug = functools.partial(capture_log, "debug", 5)
16+
info = functools.partial(capture_log, "info", 9)
17+
warn = functools.partial(capture_log, "warn", 13)
18+
error = functools.partial(capture_log, "error", 17)
19+
fatal = functools.partial(capture_log, "fatal", 21)

0 commit comments

Comments
 (0)