/** * This method allows you to chain multiple {@link MetricDimensionConfigurator}s together into a single * instance. This instance's {@code setupMetricWithDimensions(...)} will be executed before the passed-in * argument's {@code setupMetricWithDimensions(...)}. See {@link ChainedMetricDimensionConfigurator} for more * details. Note that a new object will be created and returned - this instance and the passed-in instance * are not modified. * * @param chainMe The {@link MetricDimensionConfigurator} that should be chained after this one. * @return A new {@link ChainedMetricDimensionConfigurator} that will chain this instance with the given * argument. */ default MetricDimensionConfigurator<T> chainedWith(MetricDimensionConfigurator<T> chainMe) { return new ChainedMetricDimensionConfigurator<>(this, chainMe); }
@Override public MetricMetadata.BuilderTagger<T> setupMetricWithDimensions(MetricMetadata.BuilderTagger<T> rawBuilder, RequestInfo<?> requestInfo, ResponseInfo<?> responseInfo, HttpProcessingState httpState, int responseHttpStatusCode, int responseHttpStatusCodeXXValue, long elapsedTimeMillis, Endpoint<?> endpoint, String endpointClass, String method, String matchingPathTemplate) { // Execute the first configurator. MetricMetadata.BuilderTagger<T> withFirstConfiguration = firstConfigurator.setupMetricWithDimensions( rawBuilder, requestInfo, responseInfo, httpState, responseHttpStatusCode, responseHttpStatusCodeXXValue, elapsedTimeMillis, endpoint, endpointClass, method, matchingPathTemplate ); // Execute the second configurator with the result of the first. return secondConfigurator.setupMetricWithDimensions( withFirstConfiguration, requestInfo, responseInfo, httpState, responseHttpStatusCode, responseHttpStatusCodeXXValue, elapsedTimeMillis, endpoint, endpointClass, method, matchingPathTemplate ); }
/** * Create a new instance for the given {@link SignalFxReporter} reporting with the given frequency. The default * metric dimension configurator will be used to set up the endpoint timer dimensions: * {@link #DEFAULT_REQUEST_LATENCY_TIMER_DIMENSION_CONFIGURATOR}. * * <p>IMPORTANT NOTE: The given {@code reportingFrequency} must match whatever you used when starting the given * {@link SignalFxReporter} (see {@link SignalFxReporter#start(long, TimeUnit)}), or else the data reported to * SignalFx may not make sense. * * @param signalFxReporter The {@link SignalFxReporter} to use for metric metadata. Cannot be null. * @param reportingFrequency The frequency that the given {@code signalFxReporter} reports its data to SignalFx. * Cannot be null, and the individual values inside the pair cannot be null. * @param metricRegistry The {@link MetricRegistry} being used for the application that the endpoint timers should * be registered under. Cannot be null. */ public SignalFxEndpointMetricsHandler(SignalFxReporter signalFxReporter, Pair<Long, TimeUnit> reportingFrequency, MetricRegistry metricRegistry) { this(extractMetricMetadataFromSignalFxReporter(signalFxReporter), metricRegistry, generateDefaultTimerMetricBuilder(reportingFrequency), null); }
/** * Creates a new instance with the given {@link MetricRegistry}, and using the given {@link SignalFxReporterFactory} * to retrieve the {@link SignalFxReporter#getMetricMetadata()} and reporting frequency. * * <p>Note that {@link Timer}s and {@link Histogram}s created from this class will have {@link * SlidingTimeWindowReservoir} reservoirs that match the given {@link SignalFxReporterFactory#getInterval()} and * {@link SignalFxReporterFactory#getTimeUnit()}. * * @param sfxReporterFactory The {@link SignalFxReporterFactory} to use to get the {@link * SignalFxReporter#getMetricMetadata()} and reporting frequency. */ public SignalFxAwareCodahaleMetricsCollector(MetricRegistry metricRegistry, SignalFxReporterFactory sfxReporterFactory) { this(metricRegistry, sfxReporterFactory.getReporter(metricRegistry).getMetricMetadata(), new RollingWindowTimerBuilder(sfxReporterFactory.getInterval(), sfxReporterFactory.getTimeUnit()), new RollingWindowHistogramBuilder(sfxReporterFactory.getInterval(), sfxReporterFactory.getTimeUnit()) ); }
@Test public void setupEndpointsMetrics_throws_IllegalArgumentException_if_serverConfig_is_null() { // given EndpointMetricsHandlerDefaultImpl newImpl = new EndpointMetricsHandlerDefaultImpl(); // when Throwable ex = catchThrowable(() -> newImpl.setupEndpointsMetrics(null, metricRegistryMock)); // then assertThat(ex) .isInstanceOf(IllegalArgumentException.class) .hasMessage("ServerConfig cannot be null"); }
@Test public void RollingWindowHistogramBuilder_newMetric_creates_a_new_histogram_with_each_call() { // given RollingWindowHistogramBuilder rwhb = new RollingWindowHistogramBuilder(42, TimeUnit.DAYS); // when Histogram firstCallHistogram = rwhb.newMetric(); Histogram secondCallHistogram = rwhb.newMetric(); // then assertThat(firstCallHistogram).isNotSameAs(secondCallHistogram); }
@Test public void RollingWindowTimerBuilder_newMetric_creates_a_new_timer_with_each_call() { // given RollingWindowTimerBuilder rwtb = new RollingWindowTimerBuilder(42, TimeUnit.DAYS); // when Timer firstCallTimer = rwtb.newMetric(); Timer secondCallTimer = rwtb.newMetric(); // then assertThat(firstCallTimer).isNotSameAs(secondCallTimer); }
@Test public void three_arg_constructor_fails_with_IllegalArgumentException_if_reportingFrequency_is_null() { // given SignalFxReporter reporterMock = mock(SignalFxReporter.class); wireUpReporterForConstructor(reporterMock); // when Throwable ex = catchThrowable( () -> new SignalFxEndpointMetricsHandler(reporterMock, null, metricRegistryMock) ); // then assertThat(ex).isInstanceOf(IllegalArgumentException.class) .hasMessage("reportingFrequency cannot be null"); }
@Override public void setupEndpointsMetrics(ServerConfig config, MetricRegistry metricRegistry) { if (config == null) throw new IllegalArgumentException("ServerConfig cannot be null"); if (metricRegistry == null) throw new IllegalArgumentException("MetricRegistry cannot be null"); setupEndpointSpecificMetrics(config, metricRegistry); setupEndpointAggregateMetrics(metricRegistry); }
protected void setupEndpointAggregateMetrics(MetricRegistry metricRegistry) { // codahale this.requests = createAndRegisterRequestTimer(name(prefix, "requests"), metricRegistry); this.responses = new Meter[]{ metricRegistry.meter(name(prefix, "1xx-responses")), // 1xx metricRegistry.meter(name(prefix, "2xx-responses")), // 2xx metricRegistry.meter(name(prefix, "3xx-responses")), // 3xx metricRegistry.meter(name(prefix, "4xx-responses")), // 4xx metricRegistry.meter(name(prefix, "5xx-responses")) // 5xx }; this.getRequests = createAndRegisterRequestTimer(name(prefix, "get-requests"), metricRegistry); this.postRequests = createAndRegisterRequestTimer(name(prefix, "post-requests"), metricRegistry); this.putRequests = createAndRegisterRequestTimer(name(prefix, "put-requests"), metricRegistry); this.deleteRequests = createAndRegisterRequestTimer(name(prefix, "delete-requests"), metricRegistry); this.otherRequests = createAndRegisterRequestTimer(name(prefix, "other-requests"), metricRegistry); }
protected static MetricBuilder<Timer> generateDefaultTimerMetricBuilder(Pair<Long, TimeUnit> reportingFrequency) { if (reportingFrequency == null) throw new IllegalArgumentException("reportingFrequency cannot be null"); if (reportingFrequency.getLeft() == null) throw new IllegalArgumentException("reportingFrequency amount cannot be null"); if (reportingFrequency.getRight() == null) throw new IllegalArgumentException("reportingFrequency TimeUnit cannot be null"); return new RollingWindowTimerBuilder(reportingFrequency.getLeft(), reportingFrequency.getRight()); }
@Test public void two_arg_constructor_fails_with_IllegalArgumentException_if_metricRegistry_is_null() { // given SignalFxReporterFactory reporterFactoryMock = mock(SignalFxReporterFactory.class); wireUpReporterFactoryMockForConstructor(reporterFactoryMock, metricRegistryMock); // when Throwable ex = catchThrowable(() -> new SignalFxEndpointMetricsHandler(reporterFactoryMock, null)); // then assertThat(ex).isInstanceOf(IllegalArgumentException.class) .hasMessage("MetricRegistry cannot be null"); }
@Test public void default_constructor_sets_up_instance_as_expected() { // when EndpointMetricsHandlerDefaultImpl newImpl = new EndpointMetricsHandlerDefaultImpl(); // then assertThat(newImpl.requestTimerGenerator) .isSameAs(EndpointMetricsHandlerDefaultImpl.DEFAULT_REQUEST_TIMER_GENERATOR); }
@Test public void two_arg_constructor_fails_with_IllegalArgumentException_if_factory_is_null() { // when Throwable ex = catchThrowable(() -> new SignalFxEndpointMetricsHandler(null, metricRegistryMock)); // then assertThat(ex).isInstanceOf(IllegalArgumentException.class) .hasMessage("SignalFxReporterFactory cannot be null"); }
protected String getTimerAndMeterMapKeyForEndpoint(Endpoint<?> endpoint) { String methodsString = getMatchingHttpMethodsAsCombinedString(endpoint); //TODO: this might be odd for multi-path endpoints return methodsString + "-" + endpoint.requestMatcher().matchingPathTemplates(); }
@Test public void setupEndpointsMetrics_does_nothing() { // given ServerConfig serverConfigMock = mock(ServerConfig.class); // when handler.setupEndpointsMetrics(serverConfigMock, metricRegistryMock); // then verifyZeroInteractions( serverConfigMock, metricMetadataMock, metricRegistryMock, requestTimerBuilderMock, dimensionConfiguratorMock ); }
@Test public void setupEndpointsMetrics_throws_IllegalArgumentException_if_metricRegistry_is_null() { // given EndpointMetricsHandlerDefaultImpl newImpl = new EndpointMetricsHandlerDefaultImpl(); // when Throwable ex = catchThrowable(() -> newImpl.setupEndpointsMetrics(serverConfig, null)); // then assertThat(ex) .isInstanceOf(IllegalArgumentException.class) .hasMessage("MetricRegistry cannot be null"); }
@Test public void three_arg_constructor_fails_with_IllegalArgumentException_if_metricRegistry_is_null() { // given SignalFxReporter reporterMock = mock(SignalFxReporter.class); wireUpReporterForConstructor(reporterMock); // when Throwable ex = catchThrowable( () -> new SignalFxEndpointMetricsHandler(reporterMock, Pair.of(42L, TimeUnit.DAYS), null) ); // then assertThat(ex).isInstanceOf(IllegalArgumentException.class) .hasMessage("metricRegistry cannot be null"); }
@Test public void kitchen_sink_constructor_defaults_requestTimerDimensionConfigurator_if_passed_null() { // when SignalFxEndpointMetricsHandler instance = new SignalFxEndpointMetricsHandler( metricMetadataMock, metricRegistryMock, requestTimerBuilderMock, null ); // then assertThat(instance.requestTimerDimensionConfigurator) .isSameAs(DEFAULT_REQUEST_LATENCY_TIMER_DIMENSION_CONFIGURATOR); }
@Test public void kitchen_sink_constructor_sets_fields_as_expected() { // when SignalFxEndpointMetricsHandler instance = new SignalFxEndpointMetricsHandler( metricMetadataMock, metricRegistryMock, requestTimerBuilderMock, dimensionConfiguratorMock ); // then assertThat(instance.metricMetadata).isSameAs(metricMetadataMock); assertThat(instance.metricRegistry).isSameAs(metricRegistryMock); assertThat(instance.requestTimerBuilder).isSameAs(requestTimerBuilderMock); assertThat(instance.requestTimerDimensionConfigurator).isSameAs(dimensionConfiguratorMock); }