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.internal; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static io.opencensus.tags.unsafe.ContextUtils.TAG_CONTEXT_KEY; 21 import static java.util.concurrent.TimeUnit.MILLISECONDS; 22 import static org.junit.Assert.assertEquals; 23 import static org.junit.Assert.assertFalse; 24 import static org.junit.Assert.assertNotNull; 25 import static org.junit.Assert.assertNotSame; 26 import static org.junit.Assert.assertNull; 27 import static org.junit.Assert.assertSame; 28 import static org.junit.Assert.assertTrue; 29 import static org.junit.Assert.fail; 30 import static org.mockito.Matchers.any; 31 import static org.mockito.Matchers.anyString; 32 import static org.mockito.Matchers.eq; 33 import static org.mockito.Matchers.isNull; 34 import static org.mockito.Matchers.same; 35 import static org.mockito.Mockito.inOrder; 36 import static org.mockito.Mockito.never; 37 import static org.mockito.Mockito.reset; 38 import static org.mockito.Mockito.spy; 39 import static org.mockito.Mockito.times; 40 import static org.mockito.Mockito.verify; 41 import static org.mockito.Mockito.verifyNoMoreInteractions; 42 import static org.mockito.Mockito.verifyZeroInteractions; 43 import static org.mockito.Mockito.when; 44 45 import io.grpc.Attributes; 46 import io.grpc.CallOptions; 47 import io.grpc.Channel; 48 import io.grpc.ClientCall; 49 import io.grpc.ClientInterceptor; 50 import io.grpc.ClientInterceptors; 51 import io.grpc.ClientStreamTracer; 52 import io.grpc.Context; 53 import io.grpc.Metadata; 54 import io.grpc.MethodDescriptor; 55 import io.grpc.ServerCall; 56 import io.grpc.ServerCallHandler; 57 import io.grpc.ServerServiceDefinition; 58 import io.grpc.ServerStreamTracer; 59 import io.grpc.Status; 60 import io.grpc.internal.testing.StatsTestUtils; 61 import io.grpc.internal.testing.StatsTestUtils.FakeStatsRecorder; 62 import io.grpc.internal.testing.StatsTestUtils.FakeTagContextBinarySerializer; 63 import io.grpc.internal.testing.StatsTestUtils.FakeTagger; 64 import io.grpc.internal.testing.StatsTestUtils.MockableSpan; 65 import io.grpc.testing.GrpcServerRule; 66 import io.opencensus.contrib.grpc.metrics.RpcMeasureConstants; 67 import io.opencensus.tags.TagContext; 68 import io.opencensus.tags.TagValue; 69 import io.opencensus.trace.BlankSpan; 70 import io.opencensus.trace.EndSpanOptions; 71 import io.opencensus.trace.MessageEvent; 72 import io.opencensus.trace.MessageEvent.Type; 73 import io.opencensus.trace.Span; 74 import io.opencensus.trace.SpanBuilder; 75 import io.opencensus.trace.SpanContext; 76 import io.opencensus.trace.Tracer; 77 import io.opencensus.trace.propagation.BinaryFormat; 78 import io.opencensus.trace.propagation.SpanContextParseException; 79 import io.opencensus.trace.unsafe.ContextUtils; 80 import java.io.InputStream; 81 import java.util.HashSet; 82 import java.util.List; 83 import java.util.Random; 84 import java.util.Set; 85 import java.util.concurrent.atomic.AtomicReference; 86 import org.junit.After; 87 import org.junit.Before; 88 import org.junit.Rule; 89 import org.junit.Test; 90 import org.junit.runner.RunWith; 91 import org.junit.runners.JUnit4; 92 import org.mockito.ArgumentCaptor; 93 import org.mockito.Captor; 94 import org.mockito.InOrder; 95 import org.mockito.Mock; 96 import org.mockito.MockitoAnnotations; 97 98 /** 99 * Test for {@link CensusStatsModule} and {@link CensusTracingModule}. 100 */ 101 @RunWith(JUnit4.class) 102 public class CensusModulesTest { 103 private static final CallOptions.Key<String> CUSTOM_OPTION = 104 CallOptions.Key.createWithDefault("option1", "default"); 105 private static final CallOptions CALL_OPTIONS = 106 CallOptions.DEFAULT.withOption(CUSTOM_OPTION, "customvalue"); 107 108 private static class StringInputStream extends InputStream { 109 final String string; 110 111 StringInputStream(String string) { 112 this.string = string; 113 } 114 115 @Override 116 public int read() { 117 // InProcessTransport doesn't actually read bytes from the InputStream. The InputStream is 118 // passed to the InProcess server and consumed by MARSHALLER.parse(). 119 throw new UnsupportedOperationException("Should not be called"); 120 } 121 } 122 123 private static final MethodDescriptor.Marshaller<String> MARSHALLER = 124 new MethodDescriptor.Marshaller<String>() { 125 @Override 126 public InputStream stream(String value) { 127 return new StringInputStream(value); 128 } 129 130 @Override 131 public String parse(InputStream stream) { 132 return ((StringInputStream) stream).string; 133 } 134 }; 135 136 private final MethodDescriptor<String, String> method = 137 MethodDescriptor.<String, String>newBuilder() 138 .setType(MethodDescriptor.MethodType.UNKNOWN) 139 .setRequestMarshaller(MARSHALLER) 140 .setResponseMarshaller(MARSHALLER) 141 .setFullMethodName("package1.service2/method3") 142 .build(); 143 private final MethodDescriptor<String, String> sampledMethod = 144 method.toBuilder().setSampledToLocalTracing(true).build(); 145 146 private final FakeClock fakeClock = new FakeClock(); 147 private final FakeTagger tagger = new FakeTagger(); 148 private final FakeTagContextBinarySerializer tagCtxSerializer = 149 new FakeTagContextBinarySerializer(); 150 private final FakeStatsRecorder statsRecorder = new FakeStatsRecorder(); 151 private final Random random = new Random(1234); 152 private final Span fakeClientParentSpan = MockableSpan.generateRandomSpan(random); 153 private final Span spyClientSpan = spy(MockableSpan.generateRandomSpan(random)); 154 private final SpanContext fakeClientSpanContext = spyClientSpan.getContext(); 155 private final Span spyServerSpan = spy(MockableSpan.generateRandomSpan(random)); 156 private final byte[] binarySpanContext = new byte[]{3, 1, 5}; 157 private final SpanBuilder spyClientSpanBuilder = spy(new MockableSpan.Builder()); 158 private final SpanBuilder spyServerSpanBuilder = spy(new MockableSpan.Builder()); 159 160 @Rule 161 public final GrpcServerRule grpcServerRule = new GrpcServerRule().directExecutor(); 162 163 @Mock 164 private Tracer tracer; 165 @Mock 166 private BinaryFormat mockTracingPropagationHandler; 167 @Mock 168 private ClientCall.Listener<String> mockClientCallListener; 169 @Mock 170 private ServerCall.Listener<String> mockServerCallListener; 171 @Captor 172 private ArgumentCaptor<Status> statusCaptor; 173 @Captor 174 private ArgumentCaptor<MessageEvent> messageEventCaptor; 175 176 private CensusStatsModule censusStats; 177 private CensusTracingModule censusTracing; 178 179 @Before 180 @SuppressWarnings("unchecked") 181 public void setUp() throws Exception { 182 MockitoAnnotations.initMocks(this); 183 when(spyClientSpanBuilder.startSpan()).thenReturn(spyClientSpan); 184 when(tracer.spanBuilderWithExplicitParent(anyString(), any(Span.class))) 185 .thenReturn(spyClientSpanBuilder); 186 when(spyServerSpanBuilder.startSpan()).thenReturn(spyServerSpan); 187 when(tracer.spanBuilderWithRemoteParent(anyString(), any(SpanContext.class))) 188 .thenReturn(spyServerSpanBuilder); 189 when(mockTracingPropagationHandler.toByteArray(any(SpanContext.class))) 190 .thenReturn(binarySpanContext); 191 when(mockTracingPropagationHandler.fromByteArray(any(byte[].class))) 192 .thenReturn(fakeClientSpanContext); 193 censusStats = 194 new CensusStatsModule( 195 tagger, tagCtxSerializer, statsRecorder, fakeClock.getStopwatchSupplier(), true); 196 censusTracing = new CensusTracingModule(tracer, mockTracingPropagationHandler); 197 } 198 199 @After 200 public void wrapUp() { 201 assertNull(statsRecorder.pollRecord()); 202 } 203 204 @Test 205 public void clientInterceptorNoCustomTag() { 206 testClientInterceptors(false); 207 } 208 209 @Test 210 public void clientInterceptorCustomTag() { 211 testClientInterceptors(true); 212 } 213 214 // Test that Census ClientInterceptors uses the TagContext and Span out of the current Context 215 // to create the ClientCallTracer, and that it intercepts ClientCall.Listener.onClose() to call 216 // ClientCallTracer.callEnded(). 217 private void testClientInterceptors(boolean nonDefaultContext) { 218 grpcServerRule.getServiceRegistry().addService( 219 ServerServiceDefinition.builder("package1.service2").addMethod( 220 method, new ServerCallHandler<String, String>() { 221 @Override 222 public ServerCall.Listener<String> startCall( 223 ServerCall<String, String> call, Metadata headers) { 224 call.sendHeaders(new Metadata()); 225 call.sendMessage("Hello"); 226 call.close( 227 Status.PERMISSION_DENIED.withDescription("No you don't"), new Metadata()); 228 return mockServerCallListener; 229 } 230 }).build()); 231 232 final AtomicReference<CallOptions> capturedCallOptions = new AtomicReference<CallOptions>(); 233 ClientInterceptor callOptionsCaptureInterceptor = new ClientInterceptor() { 234 @Override 235 public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall( 236 MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) { 237 capturedCallOptions.set(callOptions); 238 return next.newCall(method, callOptions); 239 } 240 }; 241 Channel interceptedChannel = 242 ClientInterceptors.intercept( 243 grpcServerRule.getChannel(), callOptionsCaptureInterceptor, 244 censusStats.getClientInterceptor(true, true), censusTracing.getClientInterceptor()); 245 ClientCall<String, String> call; 246 if (nonDefaultContext) { 247 Context ctx = 248 Context.ROOT.withValues( 249 TAG_CONTEXT_KEY, 250 tagger.emptyBuilder().put( 251 StatsTestUtils.EXTRA_TAG, TagValue.create("extra value")).build(), 252 ContextUtils.CONTEXT_SPAN_KEY, 253 fakeClientParentSpan); 254 Context origCtx = ctx.attach(); 255 try { 256 call = interceptedChannel.newCall(method, CALL_OPTIONS); 257 } finally { 258 ctx.detach(origCtx); 259 } 260 } else { 261 assertEquals(TAG_CONTEXT_KEY.get(Context.ROOT), TAG_CONTEXT_KEY.get()); 262 assertNull(ContextUtils.CONTEXT_SPAN_KEY.get()); 263 call = interceptedChannel.newCall(method, CALL_OPTIONS); 264 } 265 266 // The interceptor adds tracer factory to CallOptions 267 assertEquals("customvalue", capturedCallOptions.get().getOption(CUSTOM_OPTION)); 268 assertEquals(2, capturedCallOptions.get().getStreamTracerFactories().size()); 269 assertTrue( 270 capturedCallOptions.get().getStreamTracerFactories().get(0) 271 instanceof CensusTracingModule.ClientCallTracer); 272 assertTrue( 273 capturedCallOptions.get().getStreamTracerFactories().get(1) 274 instanceof CensusStatsModule.ClientCallTracer); 275 276 // Make the call 277 Metadata headers = new Metadata(); 278 call.start(mockClientCallListener, headers); 279 280 StatsTestUtils.MetricsRecord record = statsRecorder.pollRecord(); 281 assertNotNull(record); 282 TagValue methodTag = record.tags.get(RpcMeasureConstants.RPC_METHOD); 283 assertEquals(method.getFullMethodName(), methodTag.asString()); 284 if (nonDefaultContext) { 285 TagValue extraTag = record.tags.get(StatsTestUtils.EXTRA_TAG); 286 assertEquals("extra value", extraTag.asString()); 287 assertEquals(2, record.tags.size()); 288 } else { 289 assertNull(record.tags.get(StatsTestUtils.EXTRA_TAG)); 290 assertEquals(1, record.tags.size()); 291 } 292 293 if (nonDefaultContext) { 294 verify(tracer).spanBuilderWithExplicitParent( 295 eq("Sent.package1.service2.method3"), same(fakeClientParentSpan)); 296 verify(spyClientSpanBuilder).setRecordEvents(eq(true)); 297 } else { 298 verify(tracer).spanBuilderWithExplicitParent( 299 eq("Sent.package1.service2.method3"), isNull(Span.class)); 300 verify(spyClientSpanBuilder).setRecordEvents(eq(true)); 301 } 302 verify(spyClientSpan, never()).end(any(EndSpanOptions.class)); 303 304 // End the call 305 call.halfClose(); 306 call.request(1); 307 308 verify(mockClientCallListener).onClose(statusCaptor.capture(), any(Metadata.class)); 309 Status status = statusCaptor.getValue(); 310 assertEquals(Status.Code.PERMISSION_DENIED, status.getCode()); 311 assertEquals("No you don't", status.getDescription()); 312 313 // The intercepting listener calls callEnded() on ClientCallTracer, which records to Census. 314 record = statsRecorder.pollRecord(); 315 assertNotNull(record); 316 methodTag = record.tags.get(RpcMeasureConstants.RPC_METHOD); 317 assertEquals(method.getFullMethodName(), methodTag.asString()); 318 TagValue statusTag = record.tags.get(RpcMeasureConstants.RPC_STATUS); 319 assertEquals(Status.Code.PERMISSION_DENIED.toString(), statusTag.asString()); 320 if (nonDefaultContext) { 321 TagValue extraTag = record.tags.get(StatsTestUtils.EXTRA_TAG); 322 assertEquals("extra value", extraTag.asString()); 323 } else { 324 assertNull(record.tags.get(StatsTestUtils.EXTRA_TAG)); 325 } 326 verify(spyClientSpan).end( 327 EndSpanOptions.builder() 328 .setStatus( 329 io.opencensus.trace.Status.PERMISSION_DENIED 330 .withDescription("No you don't")) 331 .setSampleToLocalSpanStore(false) 332 .build()); 333 verify(spyClientSpan, never()).end(); 334 } 335 336 @Test 337 public void clientBasicStatsDefaultContext_startsAndFinishes() { 338 subtestClientBasicStatsDefaultContext(true, true); 339 } 340 341 @Test 342 public void clientBasicStatsDefaultContext_startsOnly() { 343 subtestClientBasicStatsDefaultContext(true, false); 344 } 345 346 @Test 347 public void clientBasicStatsDefaultContext_finishesOnly() { 348 subtestClientBasicStatsDefaultContext(false, true); 349 } 350 351 @Test 352 public void clientBasicStatsDefaultContext_neither() { 353 subtestClientBasicStatsDefaultContext(false, true); 354 } 355 356 private void subtestClientBasicStatsDefaultContext(boolean recordStarts, boolean recordFinishes) { 357 CensusStatsModule.ClientCallTracer callTracer = 358 censusStats.newClientCallTracer( 359 tagger.empty(), method.getFullMethodName(), recordStarts, recordFinishes); 360 Metadata headers = new Metadata(); 361 ClientStreamTracer tracer = callTracer.newClientStreamTracer(CallOptions.DEFAULT, headers); 362 363 if (recordStarts) { 364 StatsTestUtils.MetricsRecord record = statsRecorder.pollRecord(); 365 assertNotNull(record); 366 assertNoServerContent(record); 367 assertEquals(1, record.tags.size()); 368 TagValue methodTag = record.tags.get(RpcMeasureConstants.RPC_METHOD); 369 assertEquals(method.getFullMethodName(), methodTag.asString()); 370 assertEquals(1, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_STARTED_COUNT)); 371 } else { 372 assertNull(statsRecorder.pollRecord()); 373 } 374 375 fakeClock.forwardTime(30, MILLISECONDS); 376 tracer.outboundHeaders(); 377 378 fakeClock.forwardTime(100, MILLISECONDS); 379 tracer.outboundMessage(0); 380 tracer.outboundWireSize(1028); 381 tracer.outboundUncompressedSize(1128); 382 383 fakeClock.forwardTime(16, MILLISECONDS); 384 tracer.inboundMessage(0); 385 tracer.inboundWireSize(33); 386 tracer.inboundUncompressedSize(67); 387 tracer.outboundMessage(1); 388 tracer.outboundWireSize(99); 389 tracer.outboundUncompressedSize(865); 390 391 fakeClock.forwardTime(24, MILLISECONDS); 392 tracer.inboundMessage(1); 393 tracer.inboundWireSize(154); 394 tracer.inboundUncompressedSize(552); 395 tracer.streamClosed(Status.OK); 396 callTracer.callEnded(Status.OK); 397 398 if (recordFinishes) { 399 StatsTestUtils.MetricsRecord record = statsRecorder.pollRecord(); 400 assertNotNull(record); 401 assertNoServerContent(record); 402 TagValue methodTag = record.tags.get(RpcMeasureConstants.RPC_METHOD); 403 assertEquals(method.getFullMethodName(), methodTag.asString()); 404 TagValue statusTag = record.tags.get(RpcMeasureConstants.RPC_STATUS); 405 assertEquals(Status.Code.OK.toString(), statusTag.asString()); 406 assertEquals(1, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_FINISHED_COUNT)); 407 assertNull(record.getMetric(RpcMeasureConstants.RPC_CLIENT_ERROR_COUNT)); 408 assertEquals(2, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_REQUEST_COUNT)); 409 assertEquals( 410 1028 + 99, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_REQUEST_BYTES)); 411 assertEquals( 412 1128 + 865, 413 record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_UNCOMPRESSED_REQUEST_BYTES)); 414 assertEquals(2, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_RESPONSE_COUNT)); 415 assertEquals( 416 33 + 154, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_RESPONSE_BYTES)); 417 assertEquals(67 + 552, 418 record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_UNCOMPRESSED_RESPONSE_BYTES)); 419 assertEquals(30 + 100 + 16 + 24, 420 record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_ROUNDTRIP_LATENCY)); 421 } else { 422 assertNull(statsRecorder.pollRecord()); 423 } 424 } 425 426 @Test 427 public void clientBasicTracingDefaultSpan() { 428 CensusTracingModule.ClientCallTracer callTracer = 429 censusTracing.newClientCallTracer(null, method); 430 Metadata headers = new Metadata(); 431 ClientStreamTracer clientStreamTracer = 432 callTracer.newClientStreamTracer(CallOptions.DEFAULT, headers); 433 verify(tracer).spanBuilderWithExplicitParent( 434 eq("Sent.package1.service2.method3"), isNull(Span.class)); 435 verify(spyClientSpan, never()).end(any(EndSpanOptions.class)); 436 437 clientStreamTracer.outboundMessage(0); 438 clientStreamTracer.outboundMessageSent(0, 882, -1); 439 clientStreamTracer.inboundMessage(0); 440 clientStreamTracer.outboundMessage(1); 441 clientStreamTracer.outboundMessageSent(1, -1, 27); 442 clientStreamTracer.inboundMessageRead(0, 255, 90); 443 444 clientStreamTracer.streamClosed(Status.OK); 445 callTracer.callEnded(Status.OK); 446 447 InOrder inOrder = inOrder(spyClientSpan); 448 inOrder.verify(spyClientSpan, times(3)).addMessageEvent(messageEventCaptor.capture()); 449 List<MessageEvent> events = messageEventCaptor.getAllValues(); 450 assertEquals( 451 MessageEvent.builder(Type.SENT, 0).setCompressedMessageSize(882).build(), events.get(0)); 452 assertEquals( 453 MessageEvent.builder(Type.SENT, 1).setUncompressedMessageSize(27).build(), events.get(1)); 454 assertEquals( 455 MessageEvent.builder(Type.RECEIVED, 0) 456 .setCompressedMessageSize(255) 457 .setUncompressedMessageSize(90) 458 .build(), 459 events.get(2)); 460 inOrder.verify(spyClientSpan).end( 461 EndSpanOptions.builder() 462 .setStatus(io.opencensus.trace.Status.OK) 463 .setSampleToLocalSpanStore(false) 464 .build()); 465 verifyNoMoreInteractions(spyClientSpan); 466 verifyNoMoreInteractions(tracer); 467 } 468 469 @Test 470 public void clientTracingSampledToLocalSpanStore() { 471 CensusTracingModule.ClientCallTracer callTracer = 472 censusTracing.newClientCallTracer(null, sampledMethod); 473 callTracer.callEnded(Status.OK); 474 475 verify(spyClientSpan).end( 476 EndSpanOptions.builder() 477 .setStatus(io.opencensus.trace.Status.OK) 478 .setSampleToLocalSpanStore(true) 479 .build()); 480 } 481 482 @Test 483 public void clientStreamNeverCreatedStillRecordStats() { 484 CensusStatsModule.ClientCallTracer callTracer = 485 censusStats.newClientCallTracer( 486 tagger.empty(), method.getFullMethodName(), true, true); 487 488 fakeClock.forwardTime(3000, MILLISECONDS); 489 callTracer.callEnded(Status.DEADLINE_EXCEEDED.withDescription("3 seconds")); 490 491 // Upstart record 492 StatsTestUtils.MetricsRecord record = statsRecorder.pollRecord(); 493 assertNotNull(record); 494 assertNoServerContent(record); 495 assertEquals(1, record.tags.size()); 496 TagValue methodTag = record.tags.get(RpcMeasureConstants.RPC_METHOD); 497 assertEquals(method.getFullMethodName(), methodTag.asString()); 498 assertEquals(1, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_STARTED_COUNT)); 499 500 // Completion record 501 record = statsRecorder.pollRecord(); 502 assertNotNull(record); 503 assertNoServerContent(record); 504 methodTag = record.tags.get(RpcMeasureConstants.RPC_METHOD); 505 assertEquals(method.getFullMethodName(), methodTag.asString()); 506 TagValue statusTag = record.tags.get(RpcMeasureConstants.RPC_STATUS); 507 assertEquals(Status.Code.DEADLINE_EXCEEDED.toString(), statusTag.asString()); 508 assertEquals(1, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_FINISHED_COUNT)); 509 assertEquals(1, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_ERROR_COUNT)); 510 assertEquals(0, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_REQUEST_COUNT)); 511 assertEquals(0, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_REQUEST_BYTES)); 512 assertEquals(0, 513 record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_UNCOMPRESSED_REQUEST_BYTES)); 514 assertEquals(0, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_RESPONSE_COUNT)); 515 assertEquals(0, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_RESPONSE_BYTES)); 516 assertEquals(0, 517 record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_UNCOMPRESSED_RESPONSE_BYTES)); 518 assertEquals( 519 3000, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_CLIENT_ROUNDTRIP_LATENCY)); 520 assertNull(record.getMetric(RpcMeasureConstants.RPC_CLIENT_SERVER_ELAPSED_TIME)); 521 } 522 523 @Test 524 public void clientStreamNeverCreatedStillRecordTracing() { 525 CensusTracingModule.ClientCallTracer callTracer = 526 censusTracing.newClientCallTracer(fakeClientParentSpan, method); 527 verify(tracer).spanBuilderWithExplicitParent( 528 eq("Sent.package1.service2.method3"), same(fakeClientParentSpan)); 529 verify(spyClientSpanBuilder).setRecordEvents(eq(true)); 530 531 callTracer.callEnded(Status.DEADLINE_EXCEEDED.withDescription("3 seconds")); 532 verify(spyClientSpan).end( 533 EndSpanOptions.builder() 534 .setStatus( 535 io.opencensus.trace.Status.DEADLINE_EXCEEDED 536 .withDescription("3 seconds")) 537 .setSampleToLocalSpanStore(false) 538 .build()); 539 verifyNoMoreInteractions(spyClientSpan); 540 } 541 542 @Test 543 public void statsHeadersPropagateTags_record() { 544 subtestStatsHeadersPropagateTags(true, true); 545 } 546 547 @Test 548 public void statsHeadersPropagateTags_notRecord() { 549 subtestStatsHeadersPropagateTags(true, false); 550 } 551 552 @Test 553 public void statsHeadersNotPropagateTags_record() { 554 subtestStatsHeadersPropagateTags(false, true); 555 } 556 557 @Test 558 public void statsHeadersNotPropagateTags_notRecord() { 559 subtestStatsHeadersPropagateTags(false, false); 560 } 561 562 private void subtestStatsHeadersPropagateTags(boolean propagate, boolean recordStats) { 563 // EXTRA_TAG is propagated by the FakeStatsContextFactory. Note that not all tags are 564 // propagated. The StatsContextFactory decides which tags are to propagated. gRPC facilitates 565 // the propagation by putting them in the headers. 566 TagContext clientCtx = tagger.emptyBuilder().put( 567 StatsTestUtils.EXTRA_TAG, TagValue.create("extra-tag-value-897")).build(); 568 CensusStatsModule census = 569 new CensusStatsModule( 570 tagger, 571 tagCtxSerializer, 572 statsRecorder, 573 fakeClock.getStopwatchSupplier(), 574 propagate); 575 Metadata headers = new Metadata(); 576 CensusStatsModule.ClientCallTracer callTracer = 577 census.newClientCallTracer(clientCtx, method.getFullMethodName(), recordStats, recordStats); 578 // This propagates clientCtx to headers if propagates==true 579 callTracer.newClientStreamTracer(CallOptions.DEFAULT, headers); 580 if (recordStats) { 581 // Client upstart record 582 StatsTestUtils.MetricsRecord clientRecord = statsRecorder.pollRecord(); 583 assertNotNull(clientRecord); 584 assertNoServerContent(clientRecord); 585 assertEquals(2, clientRecord.tags.size()); 586 TagValue clientMethodTag = clientRecord.tags.get(RpcMeasureConstants.RPC_METHOD); 587 assertEquals(method.getFullMethodName(), clientMethodTag.asString()); 588 TagValue clientPropagatedTag = clientRecord.tags.get(StatsTestUtils.EXTRA_TAG); 589 assertEquals("extra-tag-value-897", clientPropagatedTag.asString()); 590 } 591 592 if (propagate) { 593 assertTrue(headers.containsKey(census.statsHeader)); 594 } else { 595 assertFalse(headers.containsKey(census.statsHeader)); 596 return; 597 } 598 599 ServerStreamTracer serverTracer = 600 census.getServerTracerFactory(recordStats, recordStats).newServerStreamTracer( 601 method.getFullMethodName(), headers); 602 // Server tracer deserializes clientCtx from the headers, so that it records stats with the 603 // propagated tags. 604 Context serverContext = serverTracer.filterContext(Context.ROOT); 605 // It also put clientCtx in the Context seen by the call handler 606 assertEquals( 607 tagger.toBuilder(clientCtx).put( 608 RpcMeasureConstants.RPC_METHOD, 609 TagValue.create(method.getFullMethodName())).build(), 610 TAG_CONTEXT_KEY.get(serverContext)); 611 612 // Verifies that the server tracer records the status with the propagated tag 613 serverTracer.streamClosed(Status.OK); 614 615 if (recordStats) { 616 // Server upstart record 617 StatsTestUtils.MetricsRecord serverRecord = statsRecorder.pollRecord(); 618 assertNotNull(serverRecord); 619 assertNoClientContent(serverRecord); 620 assertEquals(2, serverRecord.tags.size()); 621 TagValue serverMethodTag = serverRecord.tags.get(RpcMeasureConstants.RPC_METHOD); 622 assertEquals(method.getFullMethodName(), serverMethodTag.asString()); 623 TagValue serverPropagatedTag = serverRecord.tags.get(StatsTestUtils.EXTRA_TAG); 624 assertEquals("extra-tag-value-897", serverPropagatedTag.asString()); 625 626 // Server completion record 627 serverRecord = statsRecorder.pollRecord(); 628 assertNotNull(serverRecord); 629 assertNoClientContent(serverRecord); 630 serverMethodTag = serverRecord.tags.get(RpcMeasureConstants.RPC_METHOD); 631 assertEquals(method.getFullMethodName(), serverMethodTag.asString()); 632 TagValue serverStatusTag = serverRecord.tags.get(RpcMeasureConstants.RPC_STATUS); 633 assertEquals(Status.Code.OK.toString(), serverStatusTag.asString()); 634 assertNull(serverRecord.getMetric(RpcMeasureConstants.RPC_SERVER_ERROR_COUNT)); 635 serverPropagatedTag = serverRecord.tags.get(StatsTestUtils.EXTRA_TAG); 636 assertEquals("extra-tag-value-897", serverPropagatedTag.asString()); 637 } 638 639 // Verifies that the client tracer factory uses clientCtx, which includes the custom tags, to 640 // record stats. 641 callTracer.callEnded(Status.OK); 642 643 if (recordStats) { 644 // Client completion record 645 StatsTestUtils.MetricsRecord clientRecord = statsRecorder.pollRecord(); 646 assertNotNull(clientRecord); 647 assertNoServerContent(clientRecord); 648 TagValue clientMethodTag = clientRecord.tags.get(RpcMeasureConstants.RPC_METHOD); 649 assertEquals(method.getFullMethodName(), clientMethodTag.asString()); 650 TagValue clientStatusTag = clientRecord.tags.get(RpcMeasureConstants.RPC_STATUS); 651 assertEquals(Status.Code.OK.toString(), clientStatusTag.asString()); 652 assertNull(clientRecord.getMetric(RpcMeasureConstants.RPC_CLIENT_ERROR_COUNT)); 653 TagValue clientPropagatedTag = clientRecord.tags.get(StatsTestUtils.EXTRA_TAG); 654 assertEquals("extra-tag-value-897", clientPropagatedTag.asString()); 655 } 656 657 if (!recordStats) { 658 assertNull(statsRecorder.pollRecord()); 659 } 660 } 661 662 @Test 663 public void statsHeadersNotPropagateDefaultContext() { 664 CensusStatsModule.ClientCallTracer callTracer = 665 censusStats.newClientCallTracer(tagger.empty(), method.getFullMethodName(), false, false); 666 Metadata headers = new Metadata(); 667 callTracer.newClientStreamTracer(CallOptions.DEFAULT, headers); 668 assertFalse(headers.containsKey(censusStats.statsHeader)); 669 } 670 671 @Test 672 public void statsHeaderMalformed() { 673 // Construct a malformed header and make sure parsing it will throw 674 byte[] statsHeaderValue = new byte[]{1}; 675 Metadata.Key<byte[]> arbitraryStatsHeader = 676 Metadata.Key.of("grpc-tags-bin", Metadata.BINARY_BYTE_MARSHALLER); 677 try { 678 tagCtxSerializer.fromByteArray(statsHeaderValue); 679 fail("Should have thrown"); 680 } catch (Exception e) { 681 // Expected 682 } 683 684 // But the header key will return a default context for it 685 Metadata headers = new Metadata(); 686 assertNull(headers.get(censusStats.statsHeader)); 687 headers.put(arbitraryStatsHeader, statsHeaderValue); 688 assertSame(tagger.empty(), headers.get(censusStats.statsHeader)); 689 } 690 691 @Test 692 public void traceHeadersPropagateSpanContext() throws Exception { 693 CensusTracingModule.ClientCallTracer callTracer = 694 censusTracing.newClientCallTracer(fakeClientParentSpan, method); 695 Metadata headers = new Metadata(); 696 callTracer.newClientStreamTracer(CallOptions.DEFAULT, headers); 697 698 verify(mockTracingPropagationHandler).toByteArray(same(fakeClientSpanContext)); 699 verifyNoMoreInteractions(mockTracingPropagationHandler); 700 verify(tracer).spanBuilderWithExplicitParent( 701 eq("Sent.package1.service2.method3"), same(fakeClientParentSpan)); 702 verify(spyClientSpanBuilder).setRecordEvents(eq(true)); 703 verifyNoMoreInteractions(tracer); 704 assertTrue(headers.containsKey(censusTracing.tracingHeader)); 705 706 ServerStreamTracer serverTracer = 707 censusTracing.getServerTracerFactory().newServerStreamTracer( 708 method.getFullMethodName(), headers); 709 verify(mockTracingPropagationHandler).fromByteArray(same(binarySpanContext)); 710 verify(tracer).spanBuilderWithRemoteParent( 711 eq("Recv.package1.service2.method3"), same(spyClientSpan.getContext())); 712 verify(spyServerSpanBuilder).setRecordEvents(eq(true)); 713 714 Context filteredContext = serverTracer.filterContext(Context.ROOT); 715 assertSame(spyServerSpan, ContextUtils.CONTEXT_SPAN_KEY.get(filteredContext)); 716 } 717 718 @Test 719 public void traceHeaders_propagateSpanContext() throws Exception { 720 CensusTracingModule.ClientCallTracer callTracer = 721 censusTracing.newClientCallTracer(fakeClientParentSpan, method); 722 Metadata headers = new Metadata(); 723 724 callTracer.newClientStreamTracer(CallOptions.DEFAULT, headers); 725 726 assertThat(headers.keys()).isNotEmpty(); 727 } 728 729 @Test 730 public void traceHeaders_missingCensusImpl_notPropagateSpanContext() 731 throws Exception { 732 reset(spyClientSpanBuilder); 733 when(spyClientSpanBuilder.startSpan()).thenReturn(BlankSpan.INSTANCE); 734 Metadata headers = new Metadata(); 735 736 CensusTracingModule.ClientCallTracer callTracer = 737 censusTracing.newClientCallTracer(BlankSpan.INSTANCE, method); 738 callTracer.newClientStreamTracer(CallOptions.DEFAULT, headers); 739 740 assertThat(headers.keys()).isEmpty(); 741 } 742 743 @Test 744 public void traceHeaders_clientMissingCensusImpl_preservingHeaders() throws Exception { 745 reset(spyClientSpanBuilder); 746 when(spyClientSpanBuilder.startSpan()).thenReturn(BlankSpan.INSTANCE); 747 Metadata headers = new Metadata(); 748 headers.put( 749 Metadata.Key.of("never-used-key-bin", Metadata.BINARY_BYTE_MARSHALLER), 750 new byte[] {}); 751 Set<String> originalHeaderKeys = new HashSet<String>(headers.keys()); 752 753 CensusTracingModule.ClientCallTracer callTracer = 754 censusTracing.newClientCallTracer(BlankSpan.INSTANCE, method); 755 callTracer.newClientStreamTracer(CallOptions.DEFAULT, headers); 756 757 assertThat(headers.keys()).containsExactlyElementsIn(originalHeaderKeys); 758 } 759 760 @Test 761 public void traceHeaderMalformed() throws Exception { 762 // As comparison, normal header parsing 763 Metadata headers = new Metadata(); 764 headers.put(censusTracing.tracingHeader, fakeClientSpanContext); 765 // mockTracingPropagationHandler was stubbed to always return fakeServerParentSpanContext 766 assertSame(spyClientSpan.getContext(), headers.get(censusTracing.tracingHeader)); 767 768 // Make BinaryPropagationHandler always throw when parsing the header 769 when(mockTracingPropagationHandler.fromByteArray(any(byte[].class))) 770 .thenThrow(new SpanContextParseException("Malformed header")); 771 772 headers = new Metadata(); 773 assertNull(headers.get(censusTracing.tracingHeader)); 774 headers.put(censusTracing.tracingHeader, fakeClientSpanContext); 775 assertSame(SpanContext.INVALID, headers.get(censusTracing.tracingHeader)); 776 assertNotSame(spyClientSpan.getContext(), SpanContext.INVALID); 777 778 // A null Span is used as the parent in this case 779 censusTracing.getServerTracerFactory().newServerStreamTracer( 780 method.getFullMethodName(), headers); 781 verify(tracer).spanBuilderWithRemoteParent( 782 eq("Recv.package1.service2.method3"), isNull(SpanContext.class)); 783 verify(spyServerSpanBuilder).setRecordEvents(eq(true)); 784 } 785 786 @Test 787 public void serverBasicStatsNoHeaders_startsAndFinishes() { 788 subtestServerBasicStatsNoHeaders(true, true); 789 } 790 791 @Test 792 public void serverBasicStatsNoHeaders_startsOnly() { 793 subtestServerBasicStatsNoHeaders(true, false); 794 } 795 796 @Test 797 public void serverBasicStatsNoHeaders_finishesOnly() { 798 subtestServerBasicStatsNoHeaders(false, true); 799 } 800 801 @Test 802 public void serverBasicStatsNoHeaders_neither() { 803 subtestServerBasicStatsNoHeaders(false, false); 804 } 805 806 private void subtestServerBasicStatsNoHeaders(boolean recordStarts, boolean recordFinishes) { 807 ServerStreamTracer.Factory tracerFactory = 808 censusStats.getServerTracerFactory(recordStarts, recordFinishes); 809 ServerStreamTracer tracer = 810 tracerFactory.newServerStreamTracer(method.getFullMethodName(), new Metadata()); 811 812 if (recordStarts) { 813 StatsTestUtils.MetricsRecord record = statsRecorder.pollRecord(); 814 assertNotNull(record); 815 assertNoClientContent(record); 816 assertEquals(1, record.tags.size()); 817 TagValue methodTag = record.tags.get(RpcMeasureConstants.RPC_METHOD); 818 assertEquals(method.getFullMethodName(), methodTag.asString()); 819 assertEquals(1, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_SERVER_STARTED_COUNT)); 820 } else { 821 assertNull(statsRecorder.pollRecord()); 822 } 823 824 Context filteredContext = tracer.filterContext(Context.ROOT); 825 TagContext statsCtx = TAG_CONTEXT_KEY.get(filteredContext); 826 assertEquals( 827 tagger 828 .emptyBuilder() 829 .put( 830 RpcMeasureConstants.RPC_METHOD, 831 TagValue.create(method.getFullMethodName())) 832 .build(), 833 statsCtx); 834 835 tracer.inboundMessage(0); 836 tracer.inboundWireSize(34); 837 tracer.inboundUncompressedSize(67); 838 839 fakeClock.forwardTime(100, MILLISECONDS); 840 tracer.outboundMessage(0); 841 tracer.outboundWireSize(1028); 842 tracer.outboundUncompressedSize(1128); 843 844 fakeClock.forwardTime(16, MILLISECONDS); 845 tracer.inboundMessage(1); 846 tracer.inboundWireSize(154); 847 tracer.inboundUncompressedSize(552); 848 tracer.outboundMessage(1); 849 tracer.outboundWireSize(99); 850 tracer.outboundUncompressedSize(865); 851 852 fakeClock.forwardTime(24, MILLISECONDS); 853 854 tracer.streamClosed(Status.CANCELLED); 855 856 if (recordFinishes) { 857 StatsTestUtils.MetricsRecord record = statsRecorder.pollRecord(); 858 assertNotNull(record); 859 assertNoClientContent(record); 860 TagValue methodTag = record.tags.get(RpcMeasureConstants.RPC_METHOD); 861 assertEquals(method.getFullMethodName(), methodTag.asString()); 862 TagValue statusTag = record.tags.get(RpcMeasureConstants.RPC_STATUS); 863 assertEquals(Status.Code.CANCELLED.toString(), statusTag.asString()); 864 assertEquals(1, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_SERVER_FINISHED_COUNT)); 865 assertEquals(1, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_SERVER_ERROR_COUNT)); 866 assertEquals(2, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_SERVER_RESPONSE_COUNT)); 867 assertEquals( 868 1028 + 99, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_SERVER_RESPONSE_BYTES)); 869 assertEquals( 870 1128 + 865, 871 record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_SERVER_UNCOMPRESSED_RESPONSE_BYTES)); 872 assertEquals(2, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_SERVER_REQUEST_COUNT)); 873 assertEquals( 874 34 + 154, record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_SERVER_REQUEST_BYTES)); 875 assertEquals(67 + 552, 876 record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_SERVER_UNCOMPRESSED_REQUEST_BYTES)); 877 assertEquals(100 + 16 + 24, 878 record.getMetricAsLongOrFail(RpcMeasureConstants.RPC_SERVER_SERVER_LATENCY)); 879 } else { 880 assertNull(statsRecorder.pollRecord()); 881 } 882 } 883 884 @Test 885 public void serverBasicTracingNoHeaders() { 886 ServerStreamTracer.Factory tracerFactory = censusTracing.getServerTracerFactory(); 887 ServerStreamTracer serverStreamTracer = 888 tracerFactory.newServerStreamTracer(method.getFullMethodName(), new Metadata()); 889 verifyZeroInteractions(mockTracingPropagationHandler); 890 verify(tracer).spanBuilderWithRemoteParent( 891 eq("Recv.package1.service2.method3"), isNull(SpanContext.class)); 892 verify(spyServerSpanBuilder).setRecordEvents(eq(true)); 893 894 Context filteredContext = serverStreamTracer.filterContext(Context.ROOT); 895 assertSame(spyServerSpan, ContextUtils.CONTEXT_SPAN_KEY.get(filteredContext)); 896 897 serverStreamTracer.serverCallStarted( 898 new ServerCallInfoImpl<String, String>(method, Attributes.EMPTY, null)); 899 900 verify(spyServerSpan, never()).end(any(EndSpanOptions.class)); 901 902 serverStreamTracer.outboundMessage(0); 903 serverStreamTracer.outboundMessageSent(0, 882, -1); 904 serverStreamTracer.inboundMessage(0); 905 serverStreamTracer.outboundMessage(1); 906 serverStreamTracer.outboundMessageSent(1, -1, 27); 907 serverStreamTracer.inboundMessageRead(0, 255, 90); 908 909 serverStreamTracer.streamClosed(Status.CANCELLED); 910 911 InOrder inOrder = inOrder(spyServerSpan); 912 inOrder.verify(spyServerSpan, times(3)).addMessageEvent(messageEventCaptor.capture()); 913 List<MessageEvent> events = messageEventCaptor.getAllValues(); 914 assertEquals( 915 MessageEvent.builder(Type.SENT, 0).setCompressedMessageSize(882).build(), events.get(0)); 916 assertEquals( 917 MessageEvent.builder(Type.SENT, 1).setUncompressedMessageSize(27).build(), events.get(1)); 918 assertEquals( 919 MessageEvent.builder(Type.RECEIVED, 0) 920 .setCompressedMessageSize(255) 921 .setUncompressedMessageSize(90) 922 .build(), 923 events.get(2)); 924 inOrder.verify(spyServerSpan).end( 925 EndSpanOptions.builder() 926 .setStatus(io.opencensus.trace.Status.CANCELLED) 927 .setSampleToLocalSpanStore(false) 928 .build()); 929 verifyNoMoreInteractions(spyServerSpan); 930 } 931 932 @Test 933 public void serverTracingSampledToLocalSpanStore() { 934 ServerStreamTracer.Factory tracerFactory = censusTracing.getServerTracerFactory(); 935 ServerStreamTracer serverStreamTracer = 936 tracerFactory.newServerStreamTracer(sampledMethod.getFullMethodName(), new Metadata()); 937 938 serverStreamTracer.filterContext(Context.ROOT); 939 940 serverStreamTracer.serverCallStarted( 941 new ServerCallInfoImpl<String, String>(sampledMethod, Attributes.EMPTY, null)); 942 943 serverStreamTracer.streamClosed(Status.CANCELLED); 944 945 verify(spyServerSpan).end( 946 EndSpanOptions.builder() 947 .setStatus(io.opencensus.trace.Status.CANCELLED) 948 .setSampleToLocalSpanStore(true) 949 .build()); 950 } 951 952 @Test 953 public void serverTracingNotSampledToLocalSpanStore_whenServerCallNotCreated() { 954 ServerStreamTracer.Factory tracerFactory = censusTracing.getServerTracerFactory(); 955 ServerStreamTracer serverStreamTracer = 956 tracerFactory.newServerStreamTracer(sampledMethod.getFullMethodName(), new Metadata()); 957 958 serverStreamTracer.streamClosed(Status.CANCELLED); 959 960 verify(spyServerSpan).end( 961 EndSpanOptions.builder() 962 .setStatus(io.opencensus.trace.Status.CANCELLED) 963 .setSampleToLocalSpanStore(false) 964 .build()); 965 } 966 967 @Test 968 public void convertToTracingStatus() { 969 // Without description 970 for (Status.Code grpcCode : Status.Code.values()) { 971 Status grpcStatus = Status.fromCode(grpcCode); 972 io.opencensus.trace.Status tracingStatus = 973 CensusTracingModule.convertStatus(grpcStatus); 974 assertEquals(grpcCode.toString(), tracingStatus.getCanonicalCode().toString()); 975 assertNull(tracingStatus.getDescription()); 976 } 977 978 // With description 979 for (Status.Code grpcCode : Status.Code.values()) { 980 Status grpcStatus = Status.fromCode(grpcCode).withDescription("This is my description"); 981 io.opencensus.trace.Status tracingStatus = 982 CensusTracingModule.convertStatus(grpcStatus); 983 assertEquals(grpcCode.toString(), tracingStatus.getCanonicalCode().toString()); 984 assertEquals(grpcStatus.getDescription(), tracingStatus.getDescription()); 985 } 986 } 987 988 989 @Test 990 public void generateTraceSpanName() { 991 assertEquals( 992 "Sent.io.grpc.Foo", CensusTracingModule.generateTraceSpanName(false, "io.grpc/Foo")); 993 assertEquals( 994 "Recv.io.grpc.Bar", CensusTracingModule.generateTraceSpanName(true, "io.grpc/Bar")); 995 } 996 997 private static void assertNoServerContent(StatsTestUtils.MetricsRecord record) { 998 assertNull(record.getMetric(RpcMeasureConstants.RPC_SERVER_ERROR_COUNT)); 999 assertNull(record.getMetric(RpcMeasureConstants.RPC_SERVER_REQUEST_COUNT)); 1000 assertNull(record.getMetric(RpcMeasureConstants.RPC_SERVER_RESPONSE_COUNT)); 1001 assertNull(record.getMetric(RpcMeasureConstants.RPC_SERVER_REQUEST_BYTES)); 1002 assertNull(record.getMetric(RpcMeasureConstants.RPC_SERVER_RESPONSE_BYTES)); 1003 assertNull(record.getMetric(RpcMeasureConstants.RPC_SERVER_SERVER_ELAPSED_TIME)); 1004 assertNull(record.getMetric(RpcMeasureConstants.RPC_SERVER_SERVER_LATENCY)); 1005 assertNull(record.getMetric(RpcMeasureConstants.RPC_SERVER_UNCOMPRESSED_REQUEST_BYTES)); 1006 assertNull(record.getMetric(RpcMeasureConstants.RPC_SERVER_UNCOMPRESSED_RESPONSE_BYTES)); 1007 } 1008 1009 private static void assertNoClientContent(StatsTestUtils.MetricsRecord record) { 1010 assertNull(record.getMetric(RpcMeasureConstants.RPC_CLIENT_ERROR_COUNT)); 1011 assertNull(record.getMetric(RpcMeasureConstants.RPC_CLIENT_REQUEST_COUNT)); 1012 assertNull(record.getMetric(RpcMeasureConstants.RPC_CLIENT_RESPONSE_COUNT)); 1013 assertNull(record.getMetric(RpcMeasureConstants.RPC_CLIENT_REQUEST_BYTES)); 1014 assertNull(record.getMetric(RpcMeasureConstants.RPC_CLIENT_RESPONSE_BYTES)); 1015 assertNull(record.getMetric(RpcMeasureConstants.RPC_CLIENT_ROUNDTRIP_LATENCY)); 1016 assertNull(record.getMetric(RpcMeasureConstants.RPC_CLIENT_SERVER_ELAPSED_TIME)); 1017 assertNull(record.getMetric(RpcMeasureConstants.RPC_CLIENT_UNCOMPRESSED_REQUEST_BYTES)); 1018 assertNull(record.getMetric(RpcMeasureConstants.RPC_CLIENT_UNCOMPRESSED_RESPONSE_BYTES)); 1019 } 1020 } 1021