Skip to content
Draft
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
25 changes: 15 additions & 10 deletions sentry_sdk/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,28 +222,33 @@ class SDKInfo(TypedDict):
# TODO: Make a proper type definition for this (PRs welcome!)
Hint = Dict[str, Any]

AttributeValue = (
str | bool | float | int | list[str] | list[bool] | list[float] | list[int]
)
Attributes = dict[str, AttributeValue]

SerializedAttributeValue = TypedDict(
"SerializedAttributeValue",
{
"type": Literal["string", "boolean", "double", "integer"],
"value": AttributeValue,
},
)

Log = TypedDict(
"Log",
{
"severity_text": str,
"severity_number": int,
"body": str,
"attributes": dict[str, str | bool | float | int],
"attributes": Attributes,
"time_unix_nano": int,
"trace_id": Optional[str],
},
)

MetricType = Literal["counter", "gauge", "distribution"]

MetricAttributeValue = TypedDict(
"MetricAttributeValue",
{
"value": Union[str, bool, float, int],
"type": Literal["string", "boolean", "double", "integer"],
},
)

Metric = TypedDict(
"Metric",
{
Expand All @@ -254,7 +259,7 @@ class SDKInfo(TypedDict):
"type": MetricType,
"value": float,
"unit": Optional[str],
"attributes": dict[str, str | bool | float | int],
"attributes": Attributes,
},
)

Expand Down
150 changes: 26 additions & 124 deletions sentry_sdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -932,137 +932,39 @@ def capture_event(

return return_value

def _capture_log(self, log):
# type: (Optional[Log]) -> None
if not has_logs_enabled(self.options) or log is None:
def _capture_telemetry(self, telemetry, type_, scope):
# type: (Telemetry, str, Scope) -> None
# Capture attributes-based telemetry (logs, metrics, spansV2)
before_send_getter = {
"log": lambda: get_before_send_log(self.options),
"metric": lambda: get_before_send_metric(self.options),
}.get(type_)

if before_send_getter is not None:
before_send = before_send_getter()
if before_send is not None:
telemetry = before_send(telemetry, {})

if telemetry is None:
return

current_scope = sentry_sdk.get_current_scope()
isolation_scope = sentry_sdk.get_isolation_scope()

log["attributes"]["sentry.sdk.name"] = SDK_INFO["name"]
log["attributes"]["sentry.sdk.version"] = SDK_INFO["version"]

server_name = self.options.get("server_name")
if server_name is not None and SPANDATA.SERVER_ADDRESS not in log["attributes"]:
log["attributes"][SPANDATA.SERVER_ADDRESS] = server_name

environment = self.options.get("environment")
if environment is not None and "sentry.environment" not in log["attributes"]:
log["attributes"]["sentry.environment"] = environment

release = self.options.get("release")
if release is not None and "sentry.release" not in log["attributes"]:
log["attributes"]["sentry.release"] = release

trace_context = current_scope.get_trace_context()
trace_id = trace_context.get("trace_id")
span_id = trace_context.get("span_id")

if trace_id is not None and log.get("trace_id") is None:
log["trace_id"] = trace_id

if (
span_id is not None
and "sentry.trace.parent_span_id" not in log["attributes"]
):
log["attributes"]["sentry.trace.parent_span_id"] = span_id

# The user, if present, is always set on the isolation scope.
if isolation_scope._user is not None:
for log_attribute, user_attribute in (
("user.id", "id"),
("user.name", "username"),
("user.email", "email"),
):
if (
user_attribute in isolation_scope._user
and log_attribute not in log["attributes"]
):
log["attributes"][log_attribute] = isolation_scope._user[
user_attribute
]

# If debug is enabled, log the log to the console
debug = self.options.get("debug", False)
if debug:
logger.debug(
f"[Sentry Logs] [{log.get('severity_text')}] {log.get('body')}"
)
scope.apply_to_telemetry(telemetry)

before_send_log = get_before_send_log(self.options)
if before_send_log is not None:
log = before_send_log(log, {})
batcher = {
"log": self.log_batcher,
"metric": self.metrics_batcher,
}.get(type_) # type: Optional[LogBatcher, MetricsBatcher]

if log is None:
return
if batcher:
batcher.add(telemetry)

if self.log_batcher:
self.log_batcher.add(log)
def _capture_log(self, log, scope):
# type: (Optional[Log], Scope) -> None
self._capture_telemetry(log, "log", scope)

def _capture_metric(self, metric):
def _capture_metric(self, metric, scope):
# type: (Optional[Metric]) -> None
if not has_metrics_enabled(self.options) or metric is None:
return

current_scope = sentry_sdk.get_current_scope()
isolation_scope = sentry_sdk.get_isolation_scope()

metric["attributes"]["sentry.sdk.name"] = SDK_INFO["name"]
metric["attributes"]["sentry.sdk.version"] = SDK_INFO["version"]

server_name = self.options.get("server_name")
if (
server_name is not None
and SPANDATA.SERVER_ADDRESS not in metric["attributes"]
):
metric["attributes"][SPANDATA.SERVER_ADDRESS] = server_name

environment = self.options.get("environment")
if environment is not None and "sentry.environment" not in metric["attributes"]:
metric["attributes"]["sentry.environment"] = environment

release = self.options.get("release")
if release is not None and "sentry.release" not in metric["attributes"]:
metric["attributes"]["sentry.release"] = release

trace_context = current_scope.get_trace_context()
trace_id = trace_context.get("trace_id")
span_id = trace_context.get("span_id")

metric["trace_id"] = trace_id or "00000000-0000-0000-0000-000000000000"
if span_id is not None:
metric["span_id"] = span_id

if isolation_scope._user is not None:
for metric_attribute, user_attribute in (
("user.id", "id"),
("user.name", "username"),
("user.email", "email"),
):
if (
user_attribute in isolation_scope._user
and metric_attribute not in metric["attributes"]
):
metric["attributes"][metric_attribute] = isolation_scope._user[
user_attribute
]

debug = self.options.get("debug", False)
if debug:
logger.debug(
f"[Sentry Metrics] [{metric.get('type')}] {metric.get('name')}: {metric.get('value')}"
)

before_send_metric = get_before_send_metric(self.options)
if before_send_metric is not None:
metric = before_send_metric(metric, {})

if metric is None:
return

if self.metrics_batcher:
self.metrics_batcher.add(metric)
self._capture_telemetry(metric, "metric", scope)

def capture_session(
self,
Expand Down
2 changes: 1 addition & 1 deletion sentry_sdk/integrations/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ def _capture_log_from_record(self, client, record):
attrs["logger.name"] = record.name

# noinspection PyProtectedMember
client._capture_log(
sentry_sdk.get_current_scope()._capture_log(
{
"severity_text": otel_severity_text,
"severity_number": otel_severity_number,
Expand Down
2 changes: 1 addition & 1 deletion sentry_sdk/integrations/loguru.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ def loguru_sentry_logs_handler(message):
else:
attrs[f"sentry.message.parameter.{key}"] = safe_repr(value)

client._capture_log(
sentry_sdk.get_current_scope()._capture_log(
{
"severity_text": otel_severity_text,
"severity_number": otel_severity_number,
Expand Down
6 changes: 2 additions & 4 deletions sentry_sdk/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import time
from typing import Any

from sentry_sdk import get_client
import sentry_sdk
from sentry_sdk.utils import safe_repr, capture_internal_exceptions

OTEL_RANGES = [
Expand All @@ -28,8 +28,6 @@ def __missing__(self, key):

def _capture_log(severity_text, severity_number, template, **kwargs):
# type: (str, int, str, **Any) -> None
client = get_client()

body = template
attrs = {} # type: dict[str, str | bool | float | int]
if "attributes" in kwargs:
Expand Down Expand Up @@ -58,7 +56,7 @@ def _capture_log(severity_text, severity_number, template, **kwargs):
}

# noinspection PyProtectedMember
client._capture_log(
sentry_sdk.get_current_scope()._capture_log(
{
"severity_text": severity_text,
"severity_number": severity_number,
Expand Down
4 changes: 1 addition & 3 deletions sentry_sdk/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ def _capture_metric(
attributes=None, # type: Optional[dict[str, Any]]
):
# type: (...) -> None
client = sentry_sdk.get_client()

attrs = {} # type: dict[str, Union[str, bool, float, int]]
if attributes:
for k, v in attributes.items():
Expand All @@ -48,7 +46,7 @@ def _capture_metric(
"attributes": attrs,
} # type: Metric

client._capture_metric(metric)
sentry_sdk.get_current_scope()._capture_metric(metric)


def count(
Expand Down
Loading
Loading