@@ -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,18 @@ 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( ) ,
6177 ) ;
62- return SdkLogger :: new ( scope, noop_logger_provider ( ) . clone ( ) ) ;
78+ return SdkLogger :: new ( scope, shutdown_logger_provider ( ) . clone ( ) ) ;
79+ }
80+ // If the provider is disabled, new logger will refer a disabled no-op logger provider.
81+ if self . inner . is_disabled {
82+ otel_warn ! ( name: "LoggerProvider.NoOpLoggerReturned" , message = "Returned NoOpLogger. SDK is disabled" ) ;
83+ return SdkLogger :: new ( scope, disabled_logger_provider ( ) . clone ( ) ) ;
6384 }
6485 if scope. name ( ) . is_empty ( ) {
6586 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 +156,7 @@ impl SdkLoggerProvider {
135156struct LoggerProviderInner {
136157 processors : Vec < Box < dyn LogProcessor > > ,
137158 is_shutdown : AtomicBool ,
159+ is_disabled : bool ,
138160}
139161
140162impl LoggerProviderInner {
@@ -267,10 +289,18 @@ impl LoggerProviderBuilder {
267289 processor. set_resource ( & resource) ;
268290 }
269291
292+ let is_disabled =
293+ std:: env:: var ( "OTEL_SDK_DISABLED" ) . is_ok_and ( |var| var. to_lowercase ( ) == "true" ) ;
294+
295+ if is_disabled {
296+ otel_warn ! ( name: "LoggerProvider.Disabled" , message = "SDK is disabled through environment variable" ) ;
297+ }
298+
270299 let logger_provider = SdkLoggerProvider {
271300 inner : Arc :: new ( LoggerProviderInner {
272301 processors,
273302 is_shutdown : AtomicBool :: new ( false ) ,
303+ is_disabled,
274304 } ) ,
275305 } ;
276306
@@ -749,6 +779,7 @@ mod tests {
749779 flush_called. clone( ) ,
750780 ) ) ] ,
751781 is_shutdown : AtomicBool :: new ( false ) ,
782+ is_disabled : false ,
752783 } ) ;
753784
754785 {
@@ -777,6 +808,32 @@ mod tests {
777808 assert ! ( !* flush_called. lock( ) . unwrap( ) ) ;
778809 }
779810
811+ #[ test]
812+ #[ ignore = "modifies OTEL_SDK_DISABLED env var which can affect other test" ]
813+ fn otel_sdk_disabled_env ( ) {
814+ temp_env:: with_var ( "OTEL_SDK_DISABLED" , Some ( "true" ) , || {
815+ let exporter = InMemoryLogExporter :: default ( ) ;
816+ let logger_provider = SdkLoggerProvider :: builder ( )
817+ . with_simple_exporter ( exporter. clone ( ) )
818+ . build ( ) ;
819+ let logger = logger_provider. logger ( "noop" ) ;
820+ let mut record = logger. create_log_record ( ) ;
821+ record. set_body ( "Testing sdk disabled logger" . into ( ) ) ;
822+ logger. emit ( record) ;
823+ let mut record = logger. create_log_record ( ) ;
824+ record. set_body ( "Testing sdk disabled logger" . into ( ) ) ;
825+ logger. emit ( record) ;
826+ let mut record = logger. create_log_record ( ) ;
827+ record. set_body ( "Testing sdk disabled logger" . into ( ) ) ;
828+ logger. emit ( record) ;
829+ let emitted_logs = exporter. get_emitted_logs ( ) . unwrap ( ) ;
830+ assert_eq ! ( emitted_logs. len( ) , 0 ) ;
831+
832+ assert ! ( logger. provider( ) . shutdown( ) . is_ok( ) ) ;
833+ assert ! ( logger. provider( ) . shutdown( ) . is_err( ) ) ;
834+ } ) ;
835+ }
836+
780837 #[ test]
781838 fn drop_after_shutdown_test_with_multiple_providers ( ) {
782839 let shutdown_called = Arc :: new ( Mutex :: new ( 0 ) ) ; // Count the number of times shutdown is called
@@ -789,6 +846,7 @@ mod tests {
789846 flush_called. clone( ) ,
790847 ) ) ] ,
791848 is_shutdown : AtomicBool :: new ( false ) ,
849+ is_disabled : false ,
792850 } ) ;
793851
794852 // Create a scope to test behavior when providers are dropped
0 commit comments