1
- import json
2
1
import os
3
2
import uuid
4
3
import random
64
63
from sentry_sdk .session import Session
65
64
from sentry_sdk .spotlight import SpotlightClient
66
65
from sentry_sdk .transport import Transport
66
+ from sentry_sdk ._log_batcher import LogBatcher
67
67
68
68
I = TypeVar ("I" , bound = Integration ) # noqa: E741
69
69
@@ -177,6 +177,7 @@ def __init__(self, options=None):
177
177
self .transport = None # type: Optional[Transport]
178
178
self .monitor = None # type: Optional[Monitor]
179
179
self .metrics_aggregator = None # type: Optional[MetricsAggregator]
180
+ self .log_batcher = None # type: Optional[LogBatcher]
180
181
181
182
def __getstate__ (self , * args , ** kwargs ):
182
183
# type: (*Any, **Any) -> Any
@@ -374,6 +375,12 @@ def _capture_envelope(envelope):
374
375
"Metrics not supported on Python 3.6 and lower with gevent."
375
376
)
376
377
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
+
377
384
max_request_body_size = ("always" , "never" , "small" , "medium" )
378
385
if self .options ["max_request_body_size" ] not in max_request_body_size :
379
386
raise ValueError (
@@ -450,6 +457,7 @@ def _capture_envelope(envelope):
450
457
if (
451
458
self .monitor
452
459
or self .metrics_aggregator
460
+ or self .log_batcher
453
461
or has_profiling_enabled (self .options )
454
462
or isinstance (self .transport , BaseHttpTransport )
455
463
):
@@ -867,15 +875,11 @@ def capture_event(
867
875
868
876
def _capture_experimental_log (self , current_scope , log ):
869
877
# 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 )
871
879
if not logs_enabled :
872
880
return
873
881
isolation_scope = current_scope .get_isolation_scope ()
874
882
875
- headers = {
876
- "sent_at" : format_timestamp (datetime .now (timezone .utc )),
877
- } # type: dict[str, object]
878
-
879
883
environment = self .options .get ("environment" )
880
884
if environment is not None and "sentry.environment" not in log ["attributes" ]:
881
885
log ["attributes" ]["sentry.environment" ] = environment
@@ -903,46 +907,14 @@ def _capture_experimental_log(self, current_scope, log):
903
907
f'[Sentry Logs] [{ log .get ("severity_text" )} ] { log .get ("body" )} '
904
908
)
905
909
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 , {})
911
913
if log is None :
912
914
return
913
915
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 )
946
918
947
919
def capture_session (
948
920
self , session # type: Session
@@ -996,6 +968,8 @@ def close(
996
968
self .session_flusher .kill ()
997
969
if self .metrics_aggregator is not None :
998
970
self .metrics_aggregator .kill ()
971
+ if self .log_batcher is not None :
972
+ self .log_batcher .kill ()
999
973
if self .monitor :
1000
974
self .monitor .kill ()
1001
975
self .transport .kill ()
@@ -1020,6 +994,8 @@ def flush(
1020
994
self .session_flusher .flush ()
1021
995
if self .metrics_aggregator is not None :
1022
996
self .metrics_aggregator .flush ()
997
+ if self .log_batcher is not None :
998
+ self .log_batcher .flush ()
1023
999
self .transport .flush (timeout = timeout , callback = callback )
1024
1000
1025
1001
def __enter__ (self ):
0 commit comments