Skip to content

Commit aaa4036

Browse files
committed
feat(sdk-platform-java): Add CompositeTracer and CompositeTracerFactory.
1 parent 965761a commit aaa4036

File tree

14 files changed

+865
-17
lines changed

14 files changed

+865
-17
lines changed

sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientContext.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -271,14 +271,15 @@ public static ClientContext create(StubSettings settings) throws IOException {
271271
if (watchdogProvider != null && watchdogProvider.shouldAutoClose()) {
272272
backgroundResources.add(watchdog);
273273
}
274-
ApiTracerContext apiTracerContext =
275-
ApiTracerContext.newBuilder()
276-
.setServerAddress(endpointContext.resolvedServerAddress())
277-
.setServerPort(endpointContext.resolvedServerPort())
278-
.setLibraryMetadata(settings.getLibraryMetadata())
279-
.build();
274+
280275
ApiTracerFactory apiTracerFactory = settings.getTracerFactory();
281-
if (apiTracerFactory instanceof SpanTracerFactory) {
276+
if (apiTracerFactory.needsContext()) {
277+
ApiTracerContext apiTracerContext =
278+
ApiTracerContext.newBuilder()
279+
.setServerAddress(endpointContext.resolvedServerAddress())
280+
.setServerPort(endpointContext.resolvedServerPort())
281+
.setLibraryMetadata(settings.getLibraryMetadata())
282+
.build();
282283
apiTracerFactory = apiTracerFactory.withContext(apiTracerContext);
283284
}
284285

sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/ApiTracerFactory.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,8 @@ default ApiTracer newTracer(ApiTracer parent, ApiTracerContext tracerContext) {
7373
return newTracer(parent, spanName, tracerContext.operationType());
7474
}
7575

76-
/**
77-
* @return the {@link ApiTracerContext} for this factory
78-
*/
79-
default ApiTracerContext getApiTracerContext() {
80-
return ApiTracerContext.empty();
76+
default boolean needsContext() {
77+
return false;
8178
}
8279

8380
/**
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions are
6+
* met:
7+
*
8+
* * Redistributions of source code must retain the above copyright
9+
* notice, this list of conditions and the following disclaimer.
10+
* * Redistributions in binary form must reproduce the above
11+
* copyright notice, this list of conditions and the following disclaimer
12+
* in the documentation and/or other materials provided with the
13+
* distribution.
14+
* * Neither the name of Google LLC nor the names of its
15+
* contributors may be used to endorse or promote products derived from
16+
* this software without specific prior written permission.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
*/
30+
package com.google.api.gax.tracing;
31+
32+
import com.google.api.core.InternalApi;
33+
import com.google.common.collect.ImmutableList;
34+
import java.util.ArrayList;
35+
import java.util.List;
36+
import java.util.Map;
37+
38+
/**
39+
* A composite implementation of {@link ApiTracer} that delegates all tracing events to a list of
40+
* underlying tracers.
41+
*
42+
* <p>For internal use only.
43+
*/
44+
@InternalApi
45+
class CompositeTracer extends BaseApiTracer {
46+
private final List<ApiTracer> children;
47+
48+
public CompositeTracer(List<ApiTracer> children) {
49+
this.children = ImmutableList.copyOf(children);
50+
}
51+
52+
@Override
53+
public Scope inScope() {
54+
final List<Scope> childScopes = new ArrayList<>(children.size());
55+
56+
for (ApiTracer child : children) {
57+
childScopes.add(child.inScope());
58+
}
59+
60+
return () -> {
61+
for (Scope childScope : childScopes) {
62+
childScope.close();
63+
}
64+
};
65+
}
66+
67+
@Override
68+
public void operationSucceeded() {
69+
for (ApiTracer child : children) {
70+
child.operationSucceeded();
71+
}
72+
}
73+
74+
@Override
75+
public void operationCancelled() {
76+
for (ApiTracer child : children) {
77+
child.operationCancelled();
78+
}
79+
}
80+
81+
@Override
82+
public void operationFailed(Throwable error) {
83+
for (ApiTracer child : children) {
84+
child.operationFailed(error);
85+
}
86+
}
87+
88+
@Override
89+
public void connectionSelected(String id) {
90+
for (ApiTracer child : children) {
91+
child.connectionSelected(id);
92+
}
93+
}
94+
95+
@Override
96+
@Deprecated
97+
public void attemptStarted(int attemptNumber) {
98+
for (ApiTracer child : children) {
99+
child.attemptStarted(attemptNumber);
100+
}
101+
}
102+
103+
@Override
104+
public void attemptStarted(Object request, int attemptNumber) {
105+
for (ApiTracer child : children) {
106+
child.attemptStarted(request, attemptNumber);
107+
}
108+
}
109+
110+
@Override
111+
public void attemptSucceeded() {
112+
for (ApiTracer child : children) {
113+
child.attemptSucceeded();
114+
}
115+
}
116+
117+
@Override
118+
public void attemptCancelled() {
119+
for (ApiTracer child : children) {
120+
child.attemptCancelled();
121+
}
122+
}
123+
124+
@Override
125+
public void attemptFailed(Throwable error, org.threeten.bp.Duration delay) {
126+
for (ApiTracer child : children) {
127+
child.attemptFailed(error, delay);
128+
}
129+
}
130+
131+
@Override
132+
public void attemptFailedDuration(Throwable error, java.time.Duration delay) {
133+
for (ApiTracer child : children) {
134+
child.attemptFailedDuration(error, delay);
135+
}
136+
}
137+
138+
@Override
139+
public void attemptFailedRetriesExhausted(Throwable error) {
140+
for (ApiTracer child : children) {
141+
child.attemptFailedRetriesExhausted(error);
142+
}
143+
}
144+
145+
@Override
146+
public void attemptPermanentFailure(Throwable error) {
147+
for (ApiTracer child : children) {
148+
child.attemptPermanentFailure(error);
149+
}
150+
}
151+
152+
@Override
153+
public void lroStartFailed(Throwable error) {
154+
for (ApiTracer child : children) {
155+
child.lroStartFailed(error);
156+
}
157+
}
158+
159+
@Override
160+
public void lroStartSucceeded() {
161+
for (ApiTracer child : children) {
162+
child.lroStartSucceeded();
163+
}
164+
}
165+
166+
@Override
167+
public void responseReceived() {
168+
for (ApiTracer child : children) {
169+
child.responseReceived();
170+
}
171+
}
172+
173+
@Override
174+
public void responseHeadersReceived(Map<String, Object> headers) {
175+
for (ApiTracer child : children) {
176+
child.responseHeadersReceived(headers);
177+
}
178+
}
179+
180+
@Override
181+
public void requestSent() {
182+
for (ApiTracer child : children) {
183+
child.requestSent();
184+
}
185+
}
186+
187+
@Override
188+
public void batchRequestSent(long elementCount, long requestSize) {
189+
for (ApiTracer child : children) {
190+
child.batchRequestSent(elementCount, requestSize);
191+
}
192+
}
193+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions are
6+
* met:
7+
*
8+
* * Redistributions of source code must retain the above copyright
9+
* notice, this list of conditions and the following disclaimer.
10+
* * Redistributions in binary form must reproduce the above
11+
* copyright notice, this list of conditions and the following disclaimer
12+
* in the documentation and/or other materials provided with the
13+
* distribution.
14+
* * Neither the name of Google LLC nor the names of its
15+
* contributors may be used to endorse or promote products derived from
16+
* this software without specific prior written permission.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
*/
30+
package com.google.api.gax.tracing;
31+
32+
import com.google.common.collect.ImmutableList;
33+
import java.util.ArrayList;
34+
import java.util.List;
35+
36+
/**
37+
* A composite implementation of {@link ApiTracerFactory} that bundles multiple tracing factories
38+
* and produces a {@link CompositeTracer} out of them.
39+
*
40+
*/
41+
public class CompositeTracerFactory extends BaseApiTracerFactory {
42+
private final List<ApiTracerFactory> apiTracerFactories;
43+
44+
public CompositeTracerFactory(List<ApiTracerFactory> apiTracerFactories) {
45+
this.apiTracerFactories = ImmutableList.copyOf(apiTracerFactories);
46+
}
47+
48+
@Override
49+
public ApiTracer newTracer(ApiTracer parent, SpanName spanName, OperationType operationType) {
50+
List<ApiTracer> children = new ArrayList<>(apiTracerFactories.size());
51+
52+
for (ApiTracerFactory factory : apiTracerFactories) {
53+
children.add(factory.newTracer(parent, spanName, operationType));
54+
}
55+
return new CompositeTracer(children);
56+
}
57+
58+
@Override
59+
public ApiTracer newTracer(ApiTracer parent, ApiTracerContext tracerContext) {
60+
List<ApiTracer> children = new ArrayList<>(apiTracerFactories.size());
61+
62+
for (ApiTracerFactory factory : apiTracerFactories) {
63+
children.add(factory.newTracer(parent, tracerContext));
64+
}
65+
return new CompositeTracer(children);
66+
}
67+
68+
@Override
69+
public boolean needsContext() {
70+
for (ApiTracerFactory factory : apiTracerFactories) {
71+
if (factory.needsContext()) {
72+
return true;
73+
}
74+
}
75+
return false;
76+
}
77+
78+
@Override
79+
public ApiTracerFactory withContext(ApiTracerContext context) {
80+
List<ApiTracerFactory> contextualizedChildren = new ArrayList<>(apiTracerFactories.size());
81+
82+
for (ApiTracerFactory factory : apiTracerFactories) {
83+
contextualizedChildren.add(factory.withContext(context));
84+
}
85+
return new CompositeTracerFactory(contextualizedChildren);
86+
}
87+
}

sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/GoldenSignalsMetricsTracerFactory.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ public ApiTracer newTracer(ApiTracer parent, ApiTracerContext methodLevelTracerC
7474
return new GoldenSignalsMetricsTracer(metricsRecorder, mergedTracerContext);
7575
}
7676

77+
@Override
78+
public boolean needsContext() {
79+
return clientLevelTracerContext == null || clientLevelTracerContext.equals(ApiTracerContext.empty());
80+
}
81+
7782
@Override
7883
public ApiTracerFactory withContext(ApiTracerContext context) {
7984
if (context == null) {

sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/LoggingTracerFactory.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
import com.google.api.core.BetaApi;
3434
import com.google.api.core.InternalApi;
35+
import com.google.common.annotations.VisibleForTesting;
3536

3637
/** A {@link ApiTracerFactory} that creates instances of {@link LoggingTracer}. */
3738
@BetaApi
@@ -57,11 +58,16 @@ public ApiTracer newTracer(ApiTracer parent, ApiTracerContext context) {
5758
return new LoggingTracer(apiTracerContext.merge(context));
5859
}
5960

60-
@Override
61-
public ApiTracerContext getApiTracerContext() {
61+
@VisibleForTesting
62+
ApiTracerContext getApiTracerContext() {
6263
return apiTracerContext;
6364
}
6465

66+
@Override
67+
public boolean needsContext() {
68+
return apiTracerContext == null || apiTracerContext.equals(ApiTracerContext.empty());
69+
}
70+
6571
@Override
6672
public ApiTracerFactory withContext(ApiTracerContext context) {
6773
return new LoggingTracerFactory(apiTracerContext.merge(context));

sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/SpanTracerFactory.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ public ApiTracer newTracer(ApiTracer parent, ApiTracerContext apiTracerContext)
9494
}
9595

9696
@Override
97-
public ApiTracerContext getApiTracerContext() {
98-
return apiTracerContext;
97+
public boolean needsContext() {
98+
return apiTracerContext == null || apiTracerContext.equals(ApiTracerContext.empty());
9999
}
100100

101101
/**

sdk-platform-java/gax-java/gax/src/test/java/com/google/api/gax/rpc/ClientContextTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1298,6 +1298,7 @@ void testCreate_withTracerFactoryReturningNullWithContext() throws IOException {
12981298
FixedCredentialsProvider.create(Mockito.mock(Credentials.class)));
12991299

13001300
ApiTracerFactory apiTracerFactory = Mockito.mock(SpanTracerFactory.class);
1301+
Mockito.doReturn(true).when(apiTracerFactory).needsContext();
13011302
Mockito.doReturn(apiTracerFactory).when(apiTracerFactory).withContext(Mockito.any());
13021303

13031304
FakeStubSettings settings = Mockito.spy(builder.build());

0 commit comments

Comments
 (0)