11use std:: any:: TypeId ;
22use std:: borrow:: Cow ;
3+ #[ cfg( feature = "logs" ) ]
4+ use std:: collections:: BTreeMap ;
35use std:: fmt;
46use std:: panic:: RefUnwindSafe ;
57use std:: sync:: { Arc , RwLock } ;
@@ -21,6 +23,8 @@ use crate::types::{Dsn, Uuid};
2123use crate :: SessionMode ;
2224use crate :: { ClientOptions , Envelope , Hub , Integration , Scope , Transport } ;
2325#[ cfg( feature = "logs" ) ]
26+ use sentry_types:: protocol:: v7:: Context ;
27+ #[ cfg( feature = "logs" ) ]
2428use sentry_types:: protocol:: v7:: { Log , LogAttribute } ;
2529
2630impl < T : Into < ClientOptions > > From < T > for Client {
@@ -55,6 +59,8 @@ pub struct Client {
5559 session_flusher : RwLock < Option < SessionFlusher > > ,
5660 #[ cfg( feature = "logs" ) ]
5761 logs_batcher : RwLock < Option < LogsBatcher > > ,
62+ #[ cfg( feature = "logs" ) ]
63+ default_log_attributes : Option < BTreeMap < String , LogAttribute > > ,
5864 integrations : Vec < ( TypeId , Arc < dyn Integration > ) > ,
5965 pub ( crate ) sdk_info : ClientSdkInfo ,
6066}
@@ -92,6 +98,8 @@ impl Clone for Client {
9298 session_flusher,
9399 #[ cfg( feature = "logs" ) ]
94100 logs_batcher,
101+ #[ cfg( feature = "logs" ) ]
102+ default_log_attributes : self . default_log_attributes . clone ( ) ,
95103 integrations : self . integrations . clone ( ) ,
96104 sdk_info : self . sdk_info . clone ( ) ,
97105 }
@@ -168,16 +176,75 @@ impl Client {
168176 None
169177 } ) ;
170178
171- Client {
179+ #[ allow( unused_mut) ]
180+ let mut client = Client {
172181 options,
173182 transport,
174183 #[ cfg( feature = "release-health" ) ]
175184 session_flusher,
176185 #[ cfg( feature = "logs" ) ]
177186 logs_batcher,
187+ #[ cfg( feature = "logs" ) ]
188+ default_log_attributes : None ,
178189 integrations,
179190 sdk_info,
191+ } ;
192+
193+ #[ cfg( feature = "logs" ) ]
194+ client. cache_default_log_attributes ( ) ;
195+
196+ client
197+ }
198+
199+ #[ cfg( feature = "logs" ) ]
200+ fn cache_default_log_attributes ( & mut self ) {
201+ let mut attributes = BTreeMap :: new ( ) ;
202+
203+ if let Some ( environment) = self . options . environment . as_ref ( ) {
204+ attributes. insert ( "sentry.environment" . to_owned ( ) , environment. clone ( ) . into ( ) ) ;
205+ }
206+
207+ if let Some ( release) = self . options . release . as_ref ( ) {
208+ attributes. insert ( "sentry.release" . to_owned ( ) , release. clone ( ) . into ( ) ) ;
180209 }
210+
211+ attributes. insert (
212+ "sentry.sdk.name" . to_owned ( ) ,
213+ self . sdk_info . name . to_owned ( ) . into ( ) ,
214+ ) ;
215+
216+ attributes. insert (
217+ "sentry.sdk.version" . to_owned ( ) ,
218+ self . sdk_info . version . to_owned ( ) . into ( ) ,
219+ ) ;
220+
221+ // Process a fake event through integrations, so that `ContextIntegration` (if available)
222+ // provides the OS Context.
223+ // This is needed as that integration adds the OS Context to events using an event
224+ // processor, which logs don't go through.
225+ // We cannot get the `ContextIntegration` directly, as its type lives in `sentry-contexts`,
226+ // which `sentry-core` doesn't depend on.
227+ let mut fake_event = Event :: default ( ) ;
228+ for ( _, integration) in self . integrations . iter ( ) {
229+ if let Some ( res) = integration. process_event ( fake_event. clone ( ) , & self . options ) {
230+ fake_event = res;
231+ }
232+ }
233+
234+ if let Some ( Context :: Os ( os) ) = fake_event. contexts . get ( "os" ) {
235+ if let Some ( name) = os. name . as_ref ( ) {
236+ attributes. insert ( "os.name" . to_owned ( ) , name. to_owned ( ) . into ( ) ) ;
237+ }
238+ if let Some ( version) = os. version . as_ref ( ) {
239+ attributes. insert ( "os.version" . to_owned ( ) , version. to_owned ( ) . into ( ) ) ;
240+ }
241+ }
242+
243+ if let Some ( server) = & self . options . server_name {
244+ attributes. insert ( "server.address" . to_owned ( ) , server. clone ( ) . into ( ) ) ;
245+ }
246+
247+ self . default_log_attributes = Some ( attributes) ;
181248 }
182249
183250 pub ( crate ) fn get_integration < I > ( & self ) -> Option < & I >
@@ -413,60 +480,18 @@ impl Client {
413480 fn prepare_log ( & self , mut log : Log , scope : & Scope ) -> Option < Log > {
414481 scope. apply_to_log ( & mut log) ;
415482
416- self . set_log_default_attributes ( & mut log) ;
483+ if let Some ( default_attributes) = self . default_log_attributes . as_ref ( ) {
484+ for ( key, val) in default_attributes. iter ( ) {
485+ log. attributes . entry ( key. to_owned ( ) ) . or_insert ( val. clone ( ) ) ;
486+ }
487+ }
417488
418489 if let Some ( ref func) = self . options . before_send_log {
419490 log = func ( log) ?;
420491 }
421492
422493 Some ( log)
423494 }
424-
425- #[ cfg( feature = "logs" ) ]
426- fn set_log_default_attributes ( & self , log : & mut Log ) {
427- if !log. attributes . contains_key ( "sentry.environment" ) {
428- if let Some ( environment) = self . options . environment . as_ref ( ) {
429- log. attributes . insert (
430- "sentry.environment" . to_owned ( ) ,
431- LogAttribute ( environment. clone ( ) . into ( ) ) ,
432- ) ;
433- }
434- }
435-
436- if !log. attributes . contains_key ( "sentry.release" ) {
437- if let Some ( release) = self . options . release . as_ref ( ) {
438- log. attributes . insert (
439- "sentry.release" . to_owned ( ) ,
440- LogAttribute ( release. clone ( ) . into ( ) ) ,
441- ) ;
442- }
443- }
444-
445- if !log. attributes . contains_key ( "sentry.sdk.name" ) {
446- log. attributes . insert (
447- "sentry.sdk.name" . to_owned ( ) ,
448- LogAttribute ( self . sdk_info . name . to_owned ( ) . into ( ) ) ,
449- ) ;
450- }
451-
452- if !log. attributes . contains_key ( "sentry.sdk.version" ) {
453- log. attributes . insert (
454- "sentry.sdk.version" . to_owned ( ) ,
455- LogAttribute ( self . sdk_info . version . to_owned ( ) . into ( ) ) ,
456- ) ;
457- }
458-
459- // TODO: set OS (and Rust?) context
460-
461- if !log. attributes . contains_key ( "server.address" ) {
462- if let Some ( server) = & self . options . server_name {
463- log. attributes . insert (
464- "server.address" . to_owned ( ) ,
465- LogAttribute ( server. clone ( ) . into ( ) ) ,
466- ) ;
467- }
468- }
469- }
470495}
471496
472497// Make this unwind safe. It's not out of the box because of the
0 commit comments