@@ -99,6 +99,8 @@ use opentelemetry::{
9999 logs:: { AnyValue , LogRecord , Logger , LoggerProvider , Severity } ,
100100 Key ,
101101} ;
102+ #[ cfg( feature = "experimental_metadata_attributes" ) ]
103+ use opentelemetry_semantic_conventions:: trace:: { CODE_FILEPATH , CODE_LINENO , CODE_NAMESPACE } ;
102104use std:: borrow:: Cow ;
103105
104106pub struct OpenTelemetryLogBridge < P , L >
@@ -130,6 +132,28 @@ where
130132 log_record. set_severity_number ( severity_of_level ( record. level ( ) ) ) ;
131133 log_record. set_severity_text ( record. level ( ) . as_str ( ) ) ;
132134 log_record. set_body ( AnyValue :: from ( record. args ( ) . to_string ( ) ) ) ;
135+
136+ #[ cfg( feature = "experimental_metadata_attributes" ) ]
137+ {
138+ if let Some ( filepath) = record. file ( ) {
139+ log_record. add_attribute (
140+ Key :: new ( CODE_FILEPATH ) ,
141+ AnyValue :: from ( filepath. to_string ( ) ) ,
142+ ) ;
143+ }
144+
145+ if let Some ( line_no) = record. line ( ) {
146+ log_record. add_attribute ( Key :: new ( CODE_LINENO ) , AnyValue :: from ( line_no) ) ;
147+ }
148+
149+ if let Some ( module) = record. module_path ( ) {
150+ log_record. add_attribute (
151+ Key :: new ( CODE_NAMESPACE ) ,
152+ AnyValue :: from ( module. to_string ( ) ) ,
153+ ) ;
154+ }
155+ }
156+
133157 log_record. add_attributes ( log_attributes ( record. key_values ( ) ) ) ;
134158 log_record. set_target ( record. metadata ( ) . target ( ) . to_string ( ) ) ;
135159
@@ -1127,6 +1151,54 @@ mod tests {
11271151 }
11281152 }
11291153
1154+ #[ cfg( feature = "experimental_metadata_attributes" ) ]
1155+ #[ test]
1156+ fn logbridge_code_attributes ( ) {
1157+ use opentelemetry_semantic_conventions:: trace:: {
1158+ CODE_FILEPATH , CODE_LINENO , CODE_NAMESPACE ,
1159+ } ;
1160+
1161+ let exporter = InMemoryLogsExporter :: default ( ) ;
1162+
1163+ let logger_provider = LoggerProvider :: builder ( )
1164+ . with_simple_exporter ( exporter. clone ( ) )
1165+ . build ( ) ;
1166+
1167+ let otel_log_appender = OpenTelemetryLogBridge :: new ( & logger_provider) ;
1168+
1169+ otel_log_appender. log (
1170+ & log:: RecordBuilder :: new ( )
1171+ . level ( log:: Level :: Warn )
1172+ . args ( format_args ! ( "WARN" ) )
1173+ . file ( Some ( "src/main.rs" ) )
1174+ . module_path ( Some ( "service" ) )
1175+ . line ( Some ( 101 ) )
1176+ . build ( ) ,
1177+ ) ;
1178+
1179+ let logs = exporter. get_emitted_logs ( ) . unwrap ( ) ;
1180+
1181+ let get = |needle : & str | -> Option < AnyValue > {
1182+ logs[ 0 ] . record . attributes_iter ( ) . find_map ( |( k, v) | {
1183+ if k. as_str ( ) == needle {
1184+ Some ( v. clone ( ) )
1185+ } else {
1186+ None
1187+ }
1188+ } )
1189+ } ;
1190+
1191+ assert_eq ! (
1192+ Some ( AnyValue :: String ( StringValue :: from( "src/main.rs" ) ) ) ,
1193+ get( CODE_FILEPATH )
1194+ ) ;
1195+ assert_eq ! (
1196+ Some ( AnyValue :: String ( StringValue :: from( "service" ) ) ) ,
1197+ get( CODE_NAMESPACE )
1198+ ) ;
1199+ assert_eq ! ( Some ( AnyValue :: Int ( 101 ) ) , get( CODE_LINENO ) ) ;
1200+ }
1201+
11301202 #[ test]
11311203 fn test_flush ( ) {
11321204 let exporter = InMemoryLogsExporter :: default ( ) ;
0 commit comments