1- import json
21import os
32import uuid
43import random
6463 from sentry_sdk .session import Session
6564 from sentry_sdk .spotlight import SpotlightClient
6665 from sentry_sdk .transport import Transport
66+ from sentry_sdk ._log_batcher import LogBatcher
6767
6868 I = TypeVar ("I" , bound = Integration ) # noqa: E741
6969
@@ -177,6 +177,7 @@ def __init__(self, options=None):
177177 self .transport = None # type: Optional[Transport]
178178 self .monitor = None # type: Optional[Monitor]
179179 self .metrics_aggregator = None # type: Optional[MetricsAggregator]
180+ self .log_batcher = None # type: Optional[LogBatcher]
180181
181182 def __getstate__ (self , * args , ** kwargs ):
182183 # type: (*Any, **Any) -> Any
@@ -374,6 +375,12 @@ def _capture_envelope(envelope):
374375 "Metrics not supported on Python 3.6 and lower with gevent."
375376 )
376377
378+ self .log_batcher = None
379+ if experiments .get ("enable_logs" , False ):
380+ from sentry_sdk ._log_batcher import LogBatcher
381+
382+ self .log_batcher = LogBatcher (capture_func = _capture_envelope )
383+
377384 max_request_body_size = ("always" , "never" , "small" , "medium" )
378385 if self .options ["max_request_body_size" ] not in max_request_body_size :
379386 raise ValueError (
@@ -450,6 +457,7 @@ def _capture_envelope(envelope):
450457 if (
451458 self .monitor
452459 or self .metrics_aggregator
460+ or self .log_batcher
453461 or has_profiling_enabled (self .options )
454462 or isinstance (self .transport , BaseHttpTransport )
455463 ):
@@ -867,15 +875,11 @@ def capture_event(
867875
868876 def _capture_experimental_log (self , current_scope , log ):
869877 # type: (Scope, Log) -> None
870- logs_enabled = self .options ["_experiments" ].get ("enable_sentry_logs " , False )
878+ logs_enabled = self .options ["_experiments" ].get ("enable_logs " , False )
871879 if not logs_enabled :
872880 return
873881 isolation_scope = current_scope .get_isolation_scope ()
874882
875- headers = {
876- "sent_at" : format_timestamp (datetime .now (timezone .utc )),
877- } # type: dict[str, object]
878-
879883 environment = self .options .get ("environment" )
880884 if environment is not None and "sentry.environment" not in log ["attributes" ]:
881885 log ["attributes" ]["sentry.environment" ] = environment
@@ -903,46 +907,14 @@ def _capture_experimental_log(self, current_scope, log):
903907 f'[Sentry Logs] [{ log .get ("severity_text" )} ] { log .get ("body" )} '
904908 )
905909
906- envelope = Envelope (headers = headers )
907-
908- before_emit_log = self .options ["_experiments" ].get ("before_emit_log" )
909- if before_emit_log is not None :
910- log = before_emit_log (log , {})
910+ before_send_log = self .options ["_experiments" ].get ("before_send_log" )
911+ if before_send_log is not None :
912+ log = before_send_log (log , {})
911913 if log is None :
912914 return
913915
914- def format_attribute (key , val ):
915- # type: (str, int | float | str | bool) -> Any
916- if isinstance (val , bool ):
917- return {"key" : key , "value" : {"boolValue" : val }}
918- if isinstance (val , int ):
919- return {"key" : key , "value" : {"intValue" : str (val )}}
920- if isinstance (val , float ):
921- return {"key" : key , "value" : {"doubleValue" : val }}
922- if isinstance (val , str ):
923- return {"key" : key , "value" : {"stringValue" : val }}
924- return {"key" : key , "value" : {"stringValue" : json .dumps (val )}}
925-
926- otel_log = {
927- "severityText" : log ["severity_text" ],
928- "severityNumber" : log ["severity_number" ],
929- "body" : {"stringValue" : log ["body" ]},
930- "timeUnixNano" : str (log ["time_unix_nano" ]),
931- "attributes" : [
932- format_attribute (k , v ) for (k , v ) in log ["attributes" ].items ()
933- ],
934- }
935-
936- if "trace_id" in log :
937- otel_log ["traceId" ] = log ["trace_id" ]
938-
939- envelope .add_log (otel_log ) # TODO: batch these
940-
941- if self .spotlight :
942- self .spotlight .capture_envelope (envelope )
943-
944- if self .transport is not None :
945- self .transport .capture_envelope (envelope )
916+ if self .log_batcher :
917+ self .log_batcher .add (log )
946918
947919 def capture_session (
948920 self , session # type: Session
@@ -996,6 +968,8 @@ def close(
996968 self .session_flusher .kill ()
997969 if self .metrics_aggregator is not None :
998970 self .metrics_aggregator .kill ()
971+ if self .log_batcher is not None :
972+ self .log_batcher .kill ()
999973 if self .monitor :
1000974 self .monitor .kill ()
1001975 self .transport .kill ()
@@ -1020,6 +994,8 @@ def flush(
1020994 self .session_flusher .flush ()
1021995 if self .metrics_aggregator is not None :
1022996 self .metrics_aggregator .flush ()
997+ if self .log_batcher is not None :
998+ self .log_batcher .flush ()
1023999 self .transport .flush (timeout = timeout , callback = callback )
10241000
10251001 def __enter__ (self ):
0 commit comments