55
66package io .opentelemetry .instrumentation .api .incubator .semconv .rpc ;
77
8+ import static io .opentelemetry .instrumentation .api .incubator .semconv .rpc .RpcCommonAttributesExtractor .RPC_METHOD_ORIGINAL ;
89import static io .opentelemetry .sdk .testing .assertj .OpenTelemetryAssertions .assertThat ;
10+ import static io .opentelemetry .semconv .ErrorAttributes .ERROR_TYPE ;
911import static org .assertj .core .api .Assertions .entry ;
1012
13+ import io .opentelemetry .api .common .AttributeKey ;
1114import io .opentelemetry .api .common .Attributes ;
1215import io .opentelemetry .api .common .AttributesBuilder ;
1316import io .opentelemetry .context .Context ;
1417import io .opentelemetry .instrumentation .api .instrumenter .AttributesExtractor ;
15- import io .opentelemetry .semconv .incubating .RpcIncubatingAttributes ;
18+ import io .opentelemetry .instrumentation .api .internal .SemconvStability ;
19+ import java .util .ArrayList ;
1620import java .util .HashMap ;
21+ import java .util .List ;
1722import java .util .Map ;
23+ import javax .annotation .Nullable ;
1824import org .junit .jupiter .api .Test ;
1925
2026@ SuppressWarnings ("deprecation" ) // using deprecated semconv
2127class RpcAttributesExtractorTest {
2228
23- enum TestGetter implements RpcAttributesGetter <Map <String , String >, Void > {
24- INSTANCE ;
29+ private static class TestGetter implements RpcAttributesGetter <Map <String , String >, Void > {
30+
31+ private final boolean predefined ;
32+
33+ private TestGetter (boolean predefined ) {
34+ this .predefined = predefined ;
35+ }
2536
2637 @ Override
2738 public String getSystem (Map <String , String > request ) {
@@ -38,37 +49,163 @@ public String getService(Map<String, String> request) {
3849 public String getMethod (Map <String , String > request ) {
3950 return request .get ("method" );
4051 }
52+
53+ @ Nullable
54+ @ Override
55+ public String getRpcMethod (Map <String , String > request ) {
56+ String service = getService (request );
57+ String method = getMethod (request );
58+ if (service == null || method == null ) {
59+ return null ;
60+ }
61+ return service + "/" + method ;
62+ }
63+
64+ @ Nullable
65+ @ Override
66+ public String getErrorType (
67+ Map <String , String > request , @ Nullable Void response , @ Nullable Throwable error ) {
68+ return request .get ("errorType" );
69+ }
70+
71+ @ Override
72+ public boolean isPredefined (Map <String , String > stringStringMap ) {
73+ return predefined ;
74+ }
4175 }
4276
4377 @ Test
4478 void server () {
45- testExtractor (RpcServerAttributesExtractor .create (TestGetter .INSTANCE ));
79+ testExtractor (RpcServerAttributesExtractor .create (new TestGetter (false )), "my.Service/Method" );
80+ }
81+
82+ @ Test
83+ void serverPredefined () {
84+ testExtractor (RpcServerAttributesExtractor .create (new TestGetter (true )), null );
4685 }
4786
4887 @ Test
4988 void client () {
50- testExtractor (RpcClientAttributesExtractor .create (TestGetter .INSTANCE ));
89+ testExtractor (RpcClientAttributesExtractor .create (new TestGetter (false )), "my.Service/Method" );
90+ }
91+
92+ @ Test
93+ void clientPredefined () {
94+ testExtractor (RpcClientAttributesExtractor .create (new TestGetter (true )), null );
95+ }
96+
97+ // Stable semconv keys
98+ private static final AttributeKey <String > RPC_SYSTEM_NAME =
99+ AttributeKey .stringKey ("rpc.system.name" );
100+
101+ // Old semconv keys (from RpcIncubatingAttributes)
102+ private static final AttributeKey <String > RPC_SYSTEM =
103+ io .opentelemetry .semconv .incubating .RpcIncubatingAttributes .RPC_SYSTEM ;
104+
105+ private static final AttributeKey <String > RPC_SERVICE =
106+ io .opentelemetry .semconv .incubating .RpcIncubatingAttributes .RPC_SERVICE ;
107+
108+ private static final AttributeKey <String > RPC_METHOD =
109+ io .opentelemetry .semconv .incubating .RpcIncubatingAttributes .RPC_METHOD ;
110+
111+ private static void testExtractor (
112+ AttributesExtractor <Map <String , String >, Void > extractor , @ Nullable String originalMethod ) {
113+ Map <String , String > request = new HashMap <>();
114+ request .put ("service" , "my.Service" );
115+ request .put ("method" , "Method" );
116+
117+ Context context = Context .root ();
118+
119+ AttributesBuilder attributes = Attributes .builder ();
120+ extractor .onStart (attributes , context , request );
121+
122+ // Build expected entries list based on semconv mode
123+ List <Map .Entry <? extends AttributeKey <?>, ?>> expectedEntries = new ArrayList <>();
124+
125+ if (SemconvStability .emitStableRpcSemconv ()) {
126+ expectedEntries .add (entry (RPC_SYSTEM_NAME , "test" ));
127+ if (originalMethod != null ) {
128+ expectedEntries .add (entry (RPC_METHOD_ORIGINAL , originalMethod ));
129+ expectedEntries .add (entry (RPC_METHOD , "_OTHER" ));
130+ } else {
131+ expectedEntries .add (entry (RPC_METHOD , "my.Service/Method" ));
132+ }
133+ }
134+
135+ if (SemconvStability .emitOldRpcSemconv ()) {
136+ expectedEntries .add (entry (RPC_SYSTEM , "test" ));
137+ expectedEntries .add (entry (RPC_SERVICE , "my.Service" ));
138+ if (!SemconvStability .emitStableRpcSemconv ()) {
139+ expectedEntries .add (entry (RPC_METHOD , "Method" ));
140+ }
141+ }
142+
143+ // safe conversion for test assertions
144+ @ SuppressWarnings ({"unchecked" , "rawtypes" })
145+ Map .Entry <? extends AttributeKey <?>, ?>[] expectedArray =
146+ (Map .Entry <? extends AttributeKey <?>, ?>[]) expectedEntries .toArray (new Map .Entry [0 ]);
147+ assertThat (attributes .build ()).containsOnly (expectedArray );
148+
149+ extractor .onEnd (attributes , context , request , null , null );
150+ assertThat (attributes .build ()).containsOnly (expectedArray );
51151 }
52152
53- private static void testExtractor (AttributesExtractor <Map <String , String >, Void > extractor ) {
153+ @ Test
154+ void shouldExtractErrorType_getter () {
54155 Map <String , String > request = new HashMap <>();
55156 request .put ("service" , "my.Service" );
56157 request .put ("method" , "Method" );
158+ request .put ("errorType" , "CANCELLED" );
159+
160+ AttributesExtractor <Map <String , String >, Void > extractor =
161+ RpcServerAttributesExtractor .create (new TestGetter (false ));
57162
58163 Context context = Context .root ();
164+ AttributesBuilder attributes = Attributes .builder ();
165+ extractor .onStart (attributes , context , request );
166+ extractor .onEnd (attributes , context , request , null , null );
167+
168+ if (SemconvStability .emitStableRpcSemconv ()) {
169+ assertThat (attributes .build ()).containsEntry (ERROR_TYPE , "CANCELLED" );
170+ }
171+ }
172+
173+ @ Test
174+ void shouldExtractErrorType_exceptionClassName () {
175+ Map <String , String > request = new HashMap <>();
176+ request .put ("service" , "my.Service" );
177+ request .put ("method" , "Method" );
59178
179+ AttributesExtractor <Map <String , String >, Void > extractor =
180+ RpcServerAttributesExtractor .create (new TestGetter (false ));
181+
182+ Context context = Context .root ();
183+ AttributesBuilder attributes = Attributes .builder ();
184+ extractor .onStart (attributes , context , request );
185+ extractor .onEnd (attributes , context , request , null , new IllegalArgumentException ());
186+
187+ if (SemconvStability .emitStableRpcSemconv ()) {
188+ assertThat (attributes .build ())
189+ .containsEntry (ERROR_TYPE , "java.lang.IllegalArgumentException" );
190+ }
191+ }
192+
193+ @ Test
194+ void shouldNotExtractErrorType_noError () {
195+ Map <String , String > request = new HashMap <>();
196+ request .put ("service" , "my.Service" );
197+ request .put ("method" , "Method" );
198+
199+ AttributesExtractor <Map <String , String >, Void > extractor =
200+ RpcServerAttributesExtractor .create (new TestGetter (false ));
201+
202+ Context context = Context .root ();
60203 AttributesBuilder attributes = Attributes .builder ();
61204 extractor .onStart (attributes , context , request );
62- assertThat (attributes .build ())
63- .containsOnly (
64- entry (RpcIncubatingAttributes .RPC_SYSTEM , "test" ),
65- entry (RpcIncubatingAttributes .RPC_SERVICE , "my.Service" ),
66- entry (RpcIncubatingAttributes .RPC_METHOD , "Method" ));
67205 extractor .onEnd (attributes , context , request , null , null );
68- assertThat (attributes .build ())
69- .containsOnly (
70- entry (RpcIncubatingAttributes .RPC_SYSTEM , "test" ),
71- entry (RpcIncubatingAttributes .RPC_SERVICE , "my.Service" ),
72- entry (RpcIncubatingAttributes .RPC_METHOD , "Method" ));
206+
207+ if (SemconvStability .emitStableRpcSemconv ()) {
208+ assertThat (attributes .build ()).doesNotContainKey (ERROR_TYPE );
209+ }
73210 }
74211}
0 commit comments