@@ -2,7 +2,7 @@ use super::{BatchLogProcessor, LogProcessor, SdkLogger, SimpleLogProcessor};
22use crate :: error:: { OTelSdkError , OTelSdkResult } ;
33use crate :: logs:: LogExporter ;
44use crate :: Resource ;
5- use opentelemetry:: { otel_debug, otel_info, InstrumentationScope } ;
5+ use opentelemetry:: { otel_debug, otel_info, otel_warn , InstrumentationScope } ;
66use std:: time:: Duration ;
77use std:: {
88 borrow:: Cow ,
@@ -12,16 +12,32 @@ use std::{
1212 } ,
1313} ;
1414
15- // a no nop logger provider used as placeholder when the provider is shutdown
15+ // a no op logger provider used as placeholder when the provider is shutdown
1616// TODO - replace it with LazyLock once it is stable
17- static NOOP_LOGGER_PROVIDER : OnceLock < SdkLoggerProvider > = OnceLock :: new ( ) ;
17+ static SHUTDOWN_LOGGER_PROVIDER : OnceLock < SdkLoggerProvider > = OnceLock :: new ( ) ;
1818
1919#[ inline]
20- fn noop_logger_provider ( ) -> & ' static SdkLoggerProvider {
21- NOOP_LOGGER_PROVIDER . get_or_init ( || SdkLoggerProvider {
20+ fn shutdown_logger_provider ( ) -> & ' static SdkLoggerProvider {
21+ SHUTDOWN_LOGGER_PROVIDER . get_or_init ( || SdkLoggerProvider {
2222 inner : Arc :: new ( LoggerProviderInner {
2323 processors : Vec :: new ( ) ,
2424 is_shutdown : AtomicBool :: new ( true ) ,
25+ is_disabled : false ,
26+ } ) ,
27+ } )
28+ }
29+ // a no op logger provider used as placeholder when sdk is disabled with
30+ // help of environment variable `OTEL_SDK_DISABLED`
31+ // TODO - replace it with LazyLock once it is stable
32+ static DISABLED_LOGGER_PROVIDER : OnceLock < SdkLoggerProvider > = OnceLock :: new ( ) ;
33+
34+ #[ inline]
35+ fn disabled_logger_provider ( ) -> & ' static SdkLoggerProvider {
36+ DISABLED_LOGGER_PROVIDER . get_or_init ( || SdkLoggerProvider {
37+ inner : Arc :: new ( LoggerProviderInner {
38+ processors : Vec :: new ( ) ,
39+ is_shutdown : AtomicBool :: new ( false ) ,
40+ is_disabled : true ,
2541 } ) ,
2642 } )
2743}
@@ -53,13 +69,23 @@ impl opentelemetry::logs::LoggerProvider for SdkLoggerProvider {
5369 }
5470
5571 fn logger_with_scope ( & self , scope : InstrumentationScope ) -> Self :: Logger {
56- // If the provider is shutdown, new logger will refer a no-op logger provider.
72+ // If the provider is shutdown, new logger will refer a shutdown no-op logger provider.
5773 if self . inner . is_shutdown . load ( Ordering :: Relaxed ) {
5874 otel_debug ! (
5975 name: "LoggerProvider.NoOpLoggerReturned" ,
6076 logger_name = scope. name( ) ,
77+ reason = "already_shutdown"
78+ ) ;
79+ return SdkLogger :: new ( scope, shutdown_logger_provider ( ) . clone ( ) ) ;
80+ }
81+ // If the provider is disabled, new logger will refer a disabled no-op logger provider.
82+ if self . inner . is_disabled {
83+ otel_debug ! (
84+ name: "LoggerProvider.NoOpLoggerReturned" ,
85+ logger_name = scope. name( ) ,
86+ reason = "disabled_via_env_variable"
6187 ) ;
62- return SdkLogger :: new ( scope, noop_logger_provider ( ) . clone ( ) ) ;
88+ return SdkLogger :: new ( scope, disabled_logger_provider ( ) . clone ( ) ) ;
6389 }
6490 if scope. name ( ) . is_empty ( ) {
6591 otel_info ! ( name: "LoggerNameEmpty" , message = "Logger name is empty; consider providing a meaningful name. Logger will function normally and the provided name will be used as-is." ) ;
@@ -135,6 +161,7 @@ impl SdkLoggerProvider {
135161struct LoggerProviderInner {
136162 processors : Vec < Box < dyn LogProcessor > > ,
137163 is_shutdown : AtomicBool ,
164+ is_disabled : bool ,
138165}
139166
140167impl LoggerProviderInner {
@@ -295,10 +322,18 @@ impl LoggerProviderBuilder {
295322 processor. set_resource ( & resource) ;
296323 }
297324
325+ let is_disabled =
326+ std:: env:: var ( "OTEL_SDK_DISABLED" ) . is_ok_and ( |var| var. to_lowercase ( ) == "true" ) ;
327+
328+ if is_disabled {
329+ otel_warn ! ( name: "LoggerProvider.Disabled" , message = "SDK is disabled through environment variable" ) ;
330+ }
331+
298332 let logger_provider = SdkLoggerProvider {
299333 inner : Arc :: new ( LoggerProviderInner {
300334 processors,
301335 is_shutdown : AtomicBool :: new ( false ) ,
336+ is_disabled,
302337 } ) ,
303338 } ;
304339
@@ -793,6 +828,7 @@ mod tests {
793828 flush_called. clone( ) ,
794829 ) ) ] ,
795830 is_shutdown : AtomicBool :: new ( false ) ,
831+ is_disabled : false ,
796832 } ) ;
797833
798834 {
@@ -821,6 +857,32 @@ mod tests {
821857 assert ! ( !* flush_called. lock( ) . unwrap( ) ) ;
822858 }
823859
860+ #[ test]
861+ #[ ignore = "modifies OTEL_SDK_DISABLED env var which can affect other test" ]
862+ fn otel_sdk_disabled_env ( ) {
863+ temp_env:: with_var ( "OTEL_SDK_DISABLED" , Some ( "true" ) , || {
864+ let exporter = InMemoryLogExporter :: default ( ) ;
865+ let logger_provider = SdkLoggerProvider :: builder ( )
866+ . with_simple_exporter ( exporter. clone ( ) )
867+ . build ( ) ;
868+ let logger = logger_provider. logger ( "noop" ) ;
869+ let mut record = logger. create_log_record ( ) ;
870+ record. set_body ( "Testing sdk disabled logger" . into ( ) ) ;
871+ logger. emit ( record) ;
872+ let mut record = logger. create_log_record ( ) ;
873+ record. set_body ( "Testing sdk disabled logger" . into ( ) ) ;
874+ logger. emit ( record) ;
875+ let mut record = logger. create_log_record ( ) ;
876+ record. set_body ( "Testing sdk disabled logger" . into ( ) ) ;
877+ logger. emit ( record) ;
878+ let emitted_logs = exporter. get_emitted_logs ( ) . unwrap ( ) ;
879+ assert_eq ! ( emitted_logs. len( ) , 0 ) ;
880+
881+ assert ! ( logger. provider( ) . shutdown( ) . is_ok( ) ) ;
882+ assert ! ( logger. provider( ) . shutdown( ) . is_err( ) ) ;
883+ } ) ;
884+ }
885+
824886 #[ test]
825887 fn drop_after_shutdown_test_with_multiple_providers ( ) {
826888 let shutdown_called = Arc :: new ( Mutex :: new ( 0 ) ) ; // Count the number of times shutdown is called
@@ -833,6 +895,7 @@ mod tests {
833895 flush_called. clone( ) ,
834896 ) ) ] ,
835897 is_shutdown : AtomicBool :: new ( false ) ,
898+ is_disabled : false ,
836899 } ) ;
837900
838901 // Create a scope to test behavior when providers are dropped
0 commit comments