@@ -422,6 +422,25 @@ impl TracerProviderBuilder {
422422 TracerProviderBuilder { resource, ..self }
423423 }
424424
425+ /// Transforms the current [Resource] in this builder using a function.
426+ ///
427+ /// The transformed [Resource] represents the entity producing telemetry and
428+ /// is associated with all [Tracer]s the [SdkTracerProvider] will create.
429+ ///
430+ /// By default, if this option is not used, the default [Resource] will be used.
431+ ///
432+ /// *Note*: Calls to this method are not additive, the returned resource will
433+ /// completely replace the existing [Resource].
434+ ///
435+ /// [Tracer]: opentelemetry::trace::Tracer
436+ pub fn transform_resource < F > ( self , f : F ) -> Self
437+ where
438+ F : FnOnce ( Option < & Resource > ) -> Resource ,
439+ {
440+ let resource = Some ( f ( self . resource . as_ref ( ) ) ) ;
441+ TracerProviderBuilder { resource, ..self }
442+ }
443+
425444 /// Create a new provider from this configuration.
426445 pub fn build ( self ) -> SdkTracerProvider {
427446 let mut config = self . config ;
@@ -882,4 +901,138 @@ mod tests {
882901 // Verify that shutdown was only called once, even after drop
883902 assert_eq ! ( shutdown_count. load( Ordering :: SeqCst ) , 1 ) ;
884903 }
904+
905+ #[ test]
906+ fn transform_resource_with_none ( ) {
907+ // Test transform_resource when no resource is set initially
908+ let provider = super :: SdkTracerProvider :: builder ( )
909+ . transform_resource ( |existing| {
910+ assert ! ( existing. is_none( ) ) ;
911+ Resource :: new ( vec ! [ KeyValue :: new( "transformed" , "value" ) ] )
912+ } )
913+ . build ( ) ;
914+
915+ assert_eq ! (
916+ provider. config( ) . resource. get( & Key :: new( "transformed" ) ) ,
917+ Some ( Value :: from( "value" ) )
918+ ) ;
919+ }
920+
921+ #[ test]
922+ fn transform_resource_with_existing ( ) {
923+ // Test transform_resource when a resource already exists
924+ let provider = super :: SdkTracerProvider :: builder ( )
925+ . with_resource ( Resource :: new ( vec ! [ KeyValue :: new( "original" , "value1" ) ] ) )
926+ . transform_resource ( |existing| {
927+ assert ! ( existing. is_some( ) ) ;
928+ let existing_resource = existing. unwrap ( ) ;
929+ assert_eq ! (
930+ existing_resource. get( & Key :: new( "original" ) ) ,
931+ Some ( Value :: from( "value1" ) )
932+ ) ;
933+
934+ // Create a new resource that merges with the existing one
935+ existing_resource
936+ . merge ( & Resource :: new ( vec ! [ KeyValue :: new( "transformed" , "value2" ) ] ) )
937+ } )
938+ . build ( ) ;
939+
940+ // Both original and transformed attributes should be present
941+ assert_eq ! (
942+ provider. config( ) . resource. get( & Key :: new( "original" ) ) ,
943+ Some ( Value :: from( "value1" ) )
944+ ) ;
945+ assert_eq ! (
946+ provider. config( ) . resource. get( & Key :: new( "transformed" ) ) ,
947+ Some ( Value :: from( "value2" ) )
948+ ) ;
949+ }
950+
951+ #[ test]
952+ fn transform_resource_replace_existing ( ) {
953+ // Test transform_resource that completely replaces the existing resource
954+ let provider = super :: SdkTracerProvider :: builder ( )
955+ . with_resource ( Resource :: new ( vec ! [ KeyValue :: new( "original" , "value1" ) ] ) )
956+ . transform_resource ( |_existing| {
957+ // Ignore existing and create a completely new resource
958+ Resource :: new ( vec ! [ KeyValue :: new( "replaced" , "new_value" ) ] )
959+ } )
960+ . build ( ) ;
961+
962+ // Only the new resource should be present
963+ assert_eq ! ( provider. config( ) . resource. get( & Key :: new( "original" ) ) , None ) ;
964+ assert_eq ! (
965+ provider. config( ) . resource. get( & Key :: new( "replaced" ) ) ,
966+ Some ( Value :: from( "new_value" ) )
967+ ) ;
968+ }
969+
970+ #[ test]
971+ fn transform_resource_multiple_calls ( ) {
972+ // Test multiple calls to transform_resource
973+ let provider = super :: SdkTracerProvider :: builder ( )
974+ . with_resource ( Resource :: new ( vec ! [ KeyValue :: new( "initial" , "value" ) ] ) )
975+ . transform_resource ( |existing| {
976+ existing. unwrap ( ) . merge ( & Resource :: new ( vec ! [ KeyValue :: new(
977+ "first_transform" ,
978+ "value1" ,
979+ ) ] ) )
980+ } )
981+ . transform_resource ( |existing| {
982+ existing. unwrap ( ) . merge ( & Resource :: new ( vec ! [ KeyValue :: new(
983+ "second_transform" ,
984+ "value2" ,
985+ ) ] ) )
986+ } )
987+ . build ( ) ;
988+
989+ // All attributes should be present
990+ assert_eq ! (
991+ provider. config( ) . resource. get( & Key :: new( "initial" ) ) ,
992+ Some ( Value :: from( "value" ) )
993+ ) ;
994+ assert_eq ! (
995+ provider. config( ) . resource. get( & Key :: new( "first_transform" ) ) ,
996+ Some ( Value :: from( "value1" ) )
997+ ) ;
998+ assert_eq ! (
999+ provider
1000+ . config( )
1001+ . resource
1002+ . get( & Key :: new( "second_transform" ) ) ,
1003+ Some ( Value :: from( "value2" ) )
1004+ ) ;
1005+ }
1006+
1007+ #[ test]
1008+ fn transform_resource_with_schema_url ( ) {
1009+ // Test transform_resource preserving schema URL
1010+ let provider = super :: SdkTracerProvider :: builder ( )
1011+ . with_resource (
1012+ Resource :: builder_empty ( )
1013+ . with_schema_url (
1014+ vec ! [ KeyValue :: new( "original" , "value" ) ] ,
1015+ "http://example.com/schema" ,
1016+ )
1017+ . build ( ) ,
1018+ )
1019+ . transform_resource ( |existing| {
1020+ let existing_resource = existing. unwrap ( ) ;
1021+ existing_resource. merge ( & Resource :: new ( vec ! [ KeyValue :: new( "added" , "new_value" ) ] ) )
1022+ } )
1023+ . build ( ) ;
1024+
1025+ assert_eq ! (
1026+ provider. config( ) . resource. get( & Key :: new( "original" ) ) ,
1027+ Some ( Value :: from( "value" ) )
1028+ ) ;
1029+ assert_eq ! (
1030+ provider. config( ) . resource. get( & Key :: new( "added" ) ) ,
1031+ Some ( Value :: from( "new_value" ) )
1032+ ) ;
1033+ assert_eq ! (
1034+ provider. config( ) . resource. schema_url( ) ,
1035+ Some ( "http://example.com/schema" )
1036+ ) ;
1037+ }
8851038}
0 commit comments