1- import json
21import os
32import uuid
43import random
6564 from sentry_sdk .session import Session
6665 from sentry_sdk .spotlight import SpotlightClient
6766 from sentry_sdk .transport import Transport
67+ from sentry_sdk ._log_batcher import LogBatcher
6868
6969 I = TypeVar ("I" , bound = Integration ) # noqa: E741
7070
@@ -178,6 +178,7 @@ def __init__(self, options=None):
178178 self .transport = None # type: Optional[Transport]
179179 self .monitor = None # type: Optional[Monitor]
180180 self .metrics_aggregator = None # type: Optional[MetricsAggregator]
181+ self .log_batcher = None # type: Optional[LogBatcher]
181182
182183 def __getstate__ (self , * args , ** kwargs ):
183184 # type: (*Any, **Any) -> Any
@@ -375,6 +376,12 @@ def _capture_envelope(envelope):
375376 "Metrics not supported on Python 3.6 and lower with gevent."
376377 )
377378
379+ self .log_batcher = None
380+ if experiments .get ("enable_logs" , False ):
381+ from sentry_sdk ._log_batcher import LogBatcher
382+
383+ self .log_batcher = LogBatcher (capture_func = _capture_envelope )
384+
378385 max_request_body_size = ("always" , "never" , "small" , "medium" )
379386 if self .options ["max_request_body_size" ] not in max_request_body_size :
380387 raise ValueError (
@@ -451,6 +458,7 @@ def _capture_envelope(envelope):
451458 if (
452459 self .monitor
453460 or self .metrics_aggregator
461+ or self .log_batcher
454462 or has_profiling_enabled (self .options )
455463 or isinstance (self .transport , BaseHttpTransport )
456464 ):
@@ -868,15 +876,11 @@ def capture_event(
868876
869877 def _capture_experimental_log (self , current_scope , log ):
870878 # type: (Scope, Log) -> None
871- logs_enabled = self .options ["_experiments" ].get ("enable_sentry_logs " , False )
879+ logs_enabled = self .options ["_experiments" ].get ("enable_logs " , False )
872880 if not logs_enabled :
873881 return
874882 isolation_scope = current_scope .get_isolation_scope ()
875883
876- headers = {
877- "sent_at" : format_timestamp (datetime .now (timezone .utc )),
878- } # type: dict[str, object]
879-
880884 environment = self .options .get ("environment" )
881885 if environment is not None and "sentry.environment" not in log ["attributes" ]:
882886 log ["attributes" ]["sentry.environment" ] = environment
@@ -913,46 +917,13 @@ def _capture_experimental_log(self, current_scope, log):
913917 f'[Sentry Logs] { log ["body" ]} ' ,
914918 )
915919
916- envelope = Envelope (headers = headers )
917-
918920 before_emit_log = self .options ["_experiments" ].get ("before_emit_log" )
919921 if before_emit_log is not None :
920922 log = before_emit_log (log , {})
921923 if log is None :
922924 return
923925
924- def format_attribute (key , val ):
925- # type: (str, int | float | str | bool) -> Any
926- if isinstance (val , bool ):
927- return {"key" : key , "value" : {"boolValue" : val }}
928- if isinstance (val , int ):
929- return {"key" : key , "value" : {"intValue" : str (val )}}
930- if isinstance (val , float ):
931- return {"key" : key , "value" : {"doubleValue" : val }}
932- if isinstance (val , str ):
933- return {"key" : key , "value" : {"stringValue" : val }}
934- return {"key" : key , "value" : {"stringValue" : json .dumps (val )}}
935-
936- otel_log = {
937- "severityText" : log ["severity_text" ],
938- "severityNumber" : log ["severity_number" ],
939- "body" : {"stringValue" : log ["body" ]},
940- "timeUnixNano" : str (log ["time_unix_nano" ]),
941- "attributes" : [
942- format_attribute (k , v ) for (k , v ) in log ["attributes" ].items ()
943- ],
944- }
945-
946- if "trace_id" in log :
947- otel_log ["traceId" ] = log ["trace_id" ]
948-
949- envelope .add_log (otel_log ) # TODO: batch these
950-
951- if self .spotlight :
952- self .spotlight .capture_envelope (envelope )
953-
954- if self .transport is not None :
955- self .transport .capture_envelope (envelope )
926+ self .log_batcher .add (log )
956927
957928 def capture_session (
958929 self , session # type: Session
@@ -1006,6 +977,8 @@ def close(
1006977 self .session_flusher .kill ()
1007978 if self .metrics_aggregator is not None :
1008979 self .metrics_aggregator .kill ()
980+ if self .log_batcher is not None :
981+ self .log_batcher .kill ()
1009982 if self .monitor :
1010983 self .monitor .kill ()
1011984 self .transport .kill ()
@@ -1030,6 +1003,8 @@ def flush(
10301003 self .session_flusher .flush ()
10311004 if self .metrics_aggregator is not None :
10321005 self .metrics_aggregator .flush ()
1006+ if self .log_batcher is not None :
1007+ self .log_batcher .flush ()
10331008 self .transport .flush (timeout = timeout , callback = callback )
10341009
10351010 def __enter__ (self ):
0 commit comments