This repository has been archived by the owner on Jan 12, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Send API Exception Metrics to SignalFx (#72)
* Optionally send API error metrics to SignalFx
- Loading branch information
Showing
3 changed files
with
185 additions
and
1 deletion.
There are no files selected for viewing
110 changes: 110 additions & 0 deletions
110
src/main/java/com/nike/cerberus/error/SfxAwareApiExceptionHandlerUtils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
/* | ||
* Copyright (c) 2017 Nike, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
*/ | ||
|
||
package com.nike.cerberus.error; | ||
|
||
import com.codahale.metrics.Counter; | ||
import com.codahale.metrics.MetricRegistry; | ||
import com.nike.backstopper.apierror.ApiError; | ||
import com.nike.backstopper.handler.ApiExceptionHandlerUtils; | ||
import com.nike.backstopper.handler.RequestInfoForLogging; | ||
import com.nike.internal.util.Pair; | ||
import com.signalfx.codahale.metrics.MetricBuilder; | ||
import com.signalfx.codahale.reporter.MetricMetadata; | ||
import com.signalfx.codahale.reporter.SignalFxReporter; | ||
|
||
import java.util.Collection; | ||
import java.util.List; | ||
|
||
/** | ||
* A SignalFx-aware ApiExceptionHandlerUtils that increments an api_errors {@link Counter} | ||
* metric with the following dimensions (based on the error info that gets logged): | ||
* response_code, contributing_errors, and exception_class. | ||
*/ | ||
public class SfxAwareApiExceptionHandlerUtils extends ApiExceptionHandlerUtils { | ||
|
||
/** | ||
* The name of the API errors metric sent to SignalFx. | ||
*/ | ||
public static final String API_ERRORS_METRIC_NAME = "api_errors"; | ||
/** | ||
* The name/key of the HTTP response code dimension applied to the API errors metric. | ||
*/ | ||
public static final String RESPONSE_CODE_DIM_KEY = "response_code"; | ||
/** | ||
* The name/key of the contributing errors dimension applied to the API errors metric. | ||
*/ | ||
public static final String CONTRIBUTING_ERRORS_DIM_KEY = "contributing_errors"; | ||
/** | ||
* The name/key of the exception class dimension applied to the API errors metric. | ||
*/ | ||
public static final String EXCEPTION_CLASS_DIM_KEY = "exception_class"; | ||
|
||
protected final MetricRegistry metricRegistry; | ||
protected final MetricMetadata sfxMetricMetadata; | ||
|
||
/** | ||
* Creates a new instance. | ||
* | ||
* @param metricRegistry The {@link MetricRegistry} that is used to create metrics for reporting to SignalFx. | ||
* Cannot be null. | ||
* @param sfxMetricMetadata The SignalFx reporter's {@link MetricMetadata} for building dimensioned metrics - | ||
* this can be retrieved by calling {@link SignalFxReporter#getMetricMetadata()} on your SignalFx reporter. | ||
* Cannot be null. | ||
*/ | ||
public SfxAwareApiExceptionHandlerUtils(MetricRegistry metricRegistry, | ||
MetricMetadata sfxMetricMetadata) { | ||
if (metricRegistry == null) | ||
throw new IllegalArgumentException("metricRegistry cannot be null"); | ||
|
||
if (sfxMetricMetadata == null) | ||
throw new IllegalArgumentException("sfxMetricMetadata cannot be null"); | ||
|
||
this.metricRegistry = metricRegistry; | ||
this.sfxMetricMetadata = sfxMetricMetadata; | ||
} | ||
|
||
@Override | ||
public String buildErrorMessageForLogs(StringBuilder sb, RequestInfoForLogging request, | ||
Collection<ApiError> contributingErrors, Integer httpStatusCode, | ||
Throwable cause, | ||
List<Pair<String, String>> extraDetailsForLogging) { | ||
try { | ||
// Do the normal logging thing. | ||
return super.buildErrorMessageForLogs( | ||
sb, request, contributingErrors, httpStatusCode, cause, extraDetailsForLogging | ||
); | ||
} | ||
finally { | ||
// Update SignalFx metrics around API Errors. | ||
String contributingErrorsString = contributingErrors == null | ||
? "[NONE]" | ||
: concatenateErrorCollection(contributingErrors); | ||
|
||
Counter apiErrorsCounterMetric = sfxMetricMetadata | ||
.forBuilder(MetricBuilder.COUNTERS) | ||
.withMetricName(API_ERRORS_METRIC_NAME) | ||
.withDimension(RESPONSE_CODE_DIM_KEY, String.valueOf(httpStatusCode)) | ||
.withDimension(CONTRIBUTING_ERRORS_DIM_KEY, contributingErrorsString) | ||
.withDimension(EXCEPTION_CLASS_DIM_KEY, cause.getClass().getName()) | ||
.createOrGet(metricRegistry); | ||
|
||
apiErrorsCounterMetric.inc(); | ||
} | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
72 changes: 72 additions & 0 deletions
72
...ain/java/com/nike/cerberus/server/config/guice/CerberusBackstopperRiposteGuiceModule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/* | ||
* Copyright (c) 2017 Nike, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
*/ | ||
|
||
package com.nike.cerberus.server.config.guice; | ||
|
||
import com.codahale.metrics.MetricRegistry; | ||
import com.google.inject.AbstractModule; | ||
import com.google.inject.Provides; | ||
import com.nike.backstopper.handler.ApiExceptionHandlerUtils; | ||
import com.nike.cerberus.error.SfxAwareApiExceptionHandlerUtils; | ||
import com.nike.riposte.metrics.codahale.CodahaleMetricsCollector; | ||
import com.nike.riposte.metrics.codahale.contrib.SignalFxReporterFactory; | ||
import com.signalfx.codahale.reporter.MetricMetadata; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import javax.annotation.Nullable; | ||
import javax.inject.Singleton; | ||
|
||
public class CerberusBackstopperRiposteGuiceModule extends AbstractModule { | ||
private final Logger logger = LoggerFactory.getLogger(this.getClass()); | ||
|
||
@Override | ||
protected void configure() { | ||
} | ||
|
||
/** | ||
* @param metricsCollector The {@link CodahaleMetricsCollector} being used for the app. Can be null - if null is | ||
* passed in then {@link ApiExceptionHandlerUtils} will be returned. | ||
* @param sfxReporterFactory The {@link SignalFxReporterFactory} being used for the app. Can be null - if null is | ||
* passed in then {@link ApiExceptionHandlerUtils} will be returned. | ||
* @return A {@link SfxAwareApiExceptionHandlerUtils} if the given args are not null, or {@link | ||
* ApiExceptionHandlerUtils} if either arg is null. | ||
*/ | ||
@Provides | ||
@Singleton | ||
public ApiExceptionHandlerUtils sfxAwareApiExceptionHandlerUtils(@Nullable CodahaleMetricsCollector metricsCollector, | ||
@Nullable SignalFxReporterFactory sfxReporterFactory) { | ||
|
||
MetricRegistry metricRegistry = metricsCollector == null ? null : metricsCollector.getMetricRegistry(); | ||
MetricMetadata sfxMetricMetadata = sfxReporterFactory == null || metricRegistry == null | ||
? null | ||
: sfxReporterFactory.getReporter(metricRegistry).getMetricMetadata(); | ||
|
||
if (metricRegistry == null || sfxMetricMetadata == null) { | ||
logger.warn("Unable to do SignalFx metric gathering around API Errors - the CodahaleMetricsCollector " | ||
+ "and/or SignalFxReporterFactory were null. Defaulting to ApiExceptionHandlerUtils. " | ||
+ "metrics_collector_is_null={}, sfx_reporter_factory_is_null={}", | ||
metricsCollector == null, | ||
sfxReporterFactory == null); | ||
return new ApiExceptionHandlerUtils(); | ||
} | ||
|
||
// We have all the bits we need to do metrics reporting, so return a SfxAwareApiExceptionHandlerUtils | ||
// that will do it. | ||
return new SfxAwareApiExceptionHandlerUtils(metricRegistry, sfxMetricMetadata); | ||
} | ||
} |