• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

grpc / grpc-java / #20494

30 Sep 2026 08:29AM UTC coverage: 89.351% (+0.05%) from 89.306%
#20494

push

github

web-flow
core: Implement [A121](https://github.com/grpc/proposal/pull/556) (#12893)

39251 of 43929 relevant lines covered (89.35%)

0.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.79
/../api/src/main/java/io/grpc/ClientStreamTracer.java
1
/*
2
 * Copyright 2017 The gRPC Authors
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package io.grpc;
18

19
import static com.google.common.base.Preconditions.checkNotNull;
20

21
import com.google.common.base.MoreObjects;
22

23
/**
24
 * {@link StreamTracer} for the client-side.
25
 *
26
 * <p>This class is thread-safe.
27
 */
28
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/2861")
29
public abstract class ClientStreamTracer extends StreamTracer {
1 ✔
30
  /**
31
   * Indicates how long the call was delayed, in nanoseconds, due to waiting for name resolution
32
   * result. If the call option is not set, the call did not experience name resolution delay.
33
   */
34
  public static final CallOptions.Key<Long> NAME_RESOLUTION_DELAYED =
1 ✔
35
      CallOptions.Key.create("io.grpc.ClientStreamTracer.NAME_RESOLUTION_DELAYED");
1 ✔
36

37
  /**
38
   * The stream is being created on a ready transport.
39
   *
40
   * @param headers the mutable initial metadata. Modifications to it will be sent to the socket but
41
   *     not be seen by client interceptors and the application.
42
   *
43
   * @since 1.40.0
44
   */
45
  public void streamCreated(@Grpc.TransportAttr Attributes transportAttrs, Metadata headers) {
46
  }
1 ✔
47

48
  /**
49
   * Name resolution is completed and the connection starts getting established. This method is only
50
   * invoked on the streams that encounter such delay.
51
   *
52
   * </p>gRPC buffers the client call if the remote address and configurations, e.g. timeouts and
53
   * retry policy, are not ready. Asynchronously gRPC internally does the name resolution to get
54
   * this information. The streams that are processed immediately on ready transports by the time
55
   * the RPC comes do not go through the pending process, thus this callback will not be invoked.
56
   */
57
  public void createPendingStream() {
58
  }
1 ✔
59

60
  /**
61
   * Called when an attempt-level delay (such as waiting for a load balancing pick or connection
62
   * establishment) starts, or when the {@code delayType} of the ongoing delay changes, in which
63
   * case {@link #recordDelayEnd} is called for the previous delay first.
64
   *
65
   * <p>Implementations should start a timer and open a child tracing span (named strictly
66
   * {@code "Delay"}) carrying the canonical {@code grpc.delay_type} attribute.
67
   *
68
   * @param delayType canonical low-cardinality label categorizing the delay (e.g., "connecting")
69
   * @param delayReason high-cardinality diagnostic string describing granular runtime conditions
70
   * @since 1.86.0
71
   */
72
  public void recordDelayStart(String delayType, String delayReason) {
73
  }
1 ✔
74

75
  /**
76
   * Called when an attempt-level delay reason changes while the delay type remains constant (for
77
   * example, when a load balancing policy updates its connection status detail).
78
   *
79
   * <p>Implementations should record a structured event (such as {@code "Delay triggered"}) on
80
   * the active delay span without recreating the span or resetting cumulative timers.
81
   *
82
   * @param delayType canonical low-cardinality label of the ongoing delay
83
   * @param delayReason updated high-cardinality diagnostic string describing new conditions
84
   * @since 1.86.0
85
   */
86
  public void recordDelayReasonChanged(String delayType, String delayReason) {
87
  }
1 ✔
88

89
  /**
90
   * Called when an attempt-level delay ends upon successful pick or stream creation, or when the
91
   * attempt is cancelled or reaches its deadline while still waiting.
92
   *
93
   * <p>Implementations should close the active child tracing span and record the elapsed duration
94
   * to the {@code grpc.client.attempt.delay.duration} histogram labeled with {@code delayType}.
95
   *
96
   * @param delayType canonical low-cardinality label of the delay being ended
97
   * @since 1.86.0
98
   */
99
  public void recordDelayEnd(String delayType) {
100
  }
1 ✔
101

102
  /**
103
   * Headers has been sent to the socket.
104
   */
105
  public void outboundHeaders() {
106
  }
1 ✔
107

108
  /**
109
   * Headers has been received from the server.
110
   */
111
  public void inboundHeaders() {
112
  }
1 ✔
113

114
  /**
115
   * Headers has been received from the server. This method does not pass ownership to {@code
116
   * headers}, so implementations must not access the metadata after returning. Modifications to the
117
   * metadata within this method will be seen by interceptors and the application.
118
   *
119
   * @param headers the received header metadata
120
   */
121
  public void inboundHeaders(Metadata headers) {
122
    inboundHeaders();
1 ✔
123
  }
1 ✔
124

125
  /**
126
   * Trailing metadata has been received from the server. This method does not pass ownership to
127
   * {@code trailers}, so implementations must not access the metadata after returning.
128
   * Modifications to the metadata within this method will be seen by interceptors and the
129
   * application.
130
   *
131
   * @param trailers the received trailing metadata
132
   * @since 1.17.0
133
   */
134
  public void inboundTrailers(Metadata trailers) {
135
  }
1 ✔
136

137
  /**
138
   * Information providing context to the call became available.
139
   */
140
  @Internal
141
  public void addOptionalLabel(String key, String value) {
142
  }
1 ✔
143

144
  /**
145
   * Factory class for {@link ClientStreamTracer}.
146
   */
147
  public abstract static class Factory {
1 ✔
148
    /**
149
     * Creates a {@link ClientStreamTracer} for a new client stream.  This is called inside the
150
     * transport when it's creating the stream.
151
     *
152
     * @param info information about the stream
153
     * @param headers the mutable headers of the stream. It can be safely mutated within this
154
     *        method.  Changes made to it will be sent by the stream.  It should not be saved
155
     *        because it is not safe for read or write after the method returns.
156
     *
157
     * @since 1.20.0
158
     */
159
    public ClientStreamTracer newClientStreamTracer(StreamInfo info, Metadata headers) {
160
      throw new UnsupportedOperationException("Not implemented");
×
161
    }
162

163
    /**
164
     * Called when a call-level delay (such as waiting for name resolution) starts before any
165
     * individual RPC attempt is created, or when the {@code delayType} of the ongoing delay
166
     * changes, in which case {@link #recordDelayEnd} is called for the previous delay first.
167
     *
168
     * <p>Implementations should start a timer and open a child tracing span (named strictly
169
     * {@code "Delay"}) carrying the canonical {@code grpc.delay_type} attribute.
170
     *
171
     * @param delayType canonical low-cardinality label categorizing the delay (e.g., "resolving")
172
     * @param delayReason high-cardinality diagnostic string describing granular runtime conditions
173
     * @since 1.86.0
174
     */
175
    public void recordDelayStart(String delayType, String delayReason) {
176
    }
1 ✔
177

178
    /**
179
     * Called when a call-level delay reason changes while the delay type remains constant.
180
     *
181
     * <p>Implementations should record a structured event (such as {@code "Delay triggered"}) on
182
     * the active call delay span without recreating the span or resetting timers.
183
     *
184
     * @param delayType canonical low-cardinality label of the ongoing delay
185
     * @param delayReason updated high-cardinality diagnostic string describing new conditions
186
     * @since 1.86.0
187
     */
188
    public void recordDelayReasonChanged(String delayType, String delayReason) {
189
    }
1 ✔
190

191
    /**
192
     * Called when a call-level delay ends upon successful name resolution, or when the RPC is
193
     * cancelled or reaches its deadline before resolution completes.
194
     *
195
     * <p>Implementations should close the active call delay span and record the elapsed duration
196
     * to the {@code grpc.client.call.delay.duration} histogram labeled with {@code delayType}.
197
     *
198
     * @param delayType canonical low-cardinality label of the delay being ended
199
     * @since 1.86.0
200
     */
201
    public void recordDelayEnd(String delayType) {
202
    }
1 ✔
203
  }
204

205
  /**
206
   * Information about a stream.
207
   *
208
   * <p>Note this class doesn't override {@code equals()} and {@code hashCode}, as is the case for
209
   * {@link CallOptions}.
210
   *
211
   * @since 1.20.0
212
   */
213
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2861")
214
  public static final class StreamInfo {
215
    private final CallOptions callOptions;
216
    private final int previousAttempts;
217
    private final boolean isTransparentRetry;
218
    private final boolean isHedging;
219

220
    StreamInfo(
221
        CallOptions callOptions, int previousAttempts, boolean isTransparentRetry,
222
        boolean isHedging) {
1 ✔
223
      this.callOptions = checkNotNull(callOptions, "callOptions");
1 ✔
224
      this.previousAttempts = previousAttempts;
1 ✔
225
      this.isTransparentRetry = isTransparentRetry;
1 ✔
226
      this.isHedging = isHedging;
1 ✔
227
    }
1 ✔
228

229
    /**
230
     * Returns the effective CallOptions of the call.
231
     */
232
    public CallOptions getCallOptions() {
233
      return callOptions;
1 ✔
234
    }
235

236
    /**
237
     * Returns the number of preceding attempts for the RPC.
238
     *
239
     * @since 1.40.0
240
     */
241
    public int getPreviousAttempts() {
242
      return previousAttempts;
1 ✔
243
    }
244

245
    /**
246
     * Whether the stream is a transparent retry.
247
     *
248
     * @since 1.40.0
249
     */
250
    public boolean isTransparentRetry() {
251
      return isTransparentRetry;
1 ✔
252
    }
253

254
    /**
255
     * Whether the stream is hedging.
256
     *
257
     * @since 1.74.0
258
     */
259
    public boolean isHedging() {
260
      return isHedging;
1 ✔
261
    }
262

263
    /**
264
     * Converts this StreamInfo into a new Builder.
265
     *
266
     * @since 1.21.0
267
     */
268
    public Builder toBuilder() {
269
      return new Builder()
1 ✔
270
          .setCallOptions(callOptions)
1 ✔
271
          .setPreviousAttempts(previousAttempts)
1 ✔
272
          .setIsTransparentRetry(isTransparentRetry)
1 ✔
273
          .setIsHedging(isHedging);
1 ✔
274

275
    }
276

277
    /**
278
     * Creates an empty Builder.
279
     *
280
     * @since 1.21.0
281
     */
282
    public static Builder newBuilder() {
283
      return new Builder();
1 ✔
284
    }
285

286
    @Override
287
    public String toString() {
288
      return MoreObjects.toStringHelper(this)
×
289
          .add("callOptions", callOptions)
×
290
          .add("previousAttempts", previousAttempts)
×
291
          .add("isTransparentRetry", isTransparentRetry)
×
292
          .add("isHedging", isHedging)
×
293
          .toString();
×
294
    }
295

296
    /**
297
     * Builds {@link StreamInfo} objects.
298
     *
299
     * @since 1.21.0
300
     */
301
    public static final class Builder {
302
      private CallOptions callOptions = CallOptions.DEFAULT;
1 ✔
303
      private int previousAttempts;
304
      private boolean isTransparentRetry;
305
      private boolean isHedging;
306

307
      Builder() {
1 ✔
308
      }
1 ✔
309

310
      /**
311
       * Sets the effective CallOptions of the call.  This field is optional.
312
       */
313
      public Builder setCallOptions(CallOptions callOptions) {
314
        this.callOptions = checkNotNull(callOptions, "callOptions cannot be null");
1 ✔
315
        return this;
1 ✔
316
      }
317

318
      /**
319
       * Set the number of preceding attempts of the RPC.
320
       *
321
       * @since 1.40.0
322
       */
323
      public Builder setPreviousAttempts(int previousAttempts) {
324
        this.previousAttempts = previousAttempts;
1 ✔
325
        return this;
1 ✔
326
      }
327

328
      /**
329
       * Sets whether the stream is a transparent retry.
330
       *
331
       * @since 1.40.0
332
       */
333
      public Builder setIsTransparentRetry(boolean isTransparentRetry) {
334
        this.isTransparentRetry = isTransparentRetry;
1 ✔
335
        return this;
1 ✔
336
      }
337

338
      /**
339
       * Sets whether the stream is hedging.
340
       *
341
       * @since 1.74.0
342
       */
343
      public Builder setIsHedging(boolean isHedging) {
344
        this.isHedging = isHedging;
1 ✔
345
        return this;
1 ✔
346
      }
347

348
      /**
349
       * Builds a new StreamInfo.
350
       */
351
      public StreamInfo build() {
352
        return new StreamInfo(callOptions, previousAttempts, isTransparentRetry, isHedging);
1 ✔
353
      }
354
    }
355
  }
356
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc