Skip to content

Commit

Permalink
Implement global error handler for OpenTelemetry java
Browse files Browse the repository at this point in the history
  • Loading branch information
MitchellDumovic committed Jul 15, 2020
1 parent 2ec91e1 commit 2e7243d
Show file tree
Hide file tree
Showing 16 changed files with 216 additions and 15 deletions.
25 changes: 23 additions & 2 deletions api/src/main/java/io/opentelemetry/OpenTelemetry.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
import io.opentelemetry.correlationcontext.CorrelationContextManager;
import io.opentelemetry.correlationcontext.DefaultCorrelationContextManager;
import io.opentelemetry.correlationcontext.spi.CorrelationContextManagerFactory;
import io.opentelemetry.errorhandler.DefaultErrorHandler;
import io.opentelemetry.errorhandler.ErrorHandler;
import io.opentelemetry.errorhandler.OpenTelemetryException;
import io.opentelemetry.errorhandler.spi.ErrorHandlerFactory;
import io.opentelemetry.internal.Obfuscated;
import io.opentelemetry.internal.Utils;
import io.opentelemetry.metrics.DefaultMeterProvider;
Expand All @@ -37,14 +41,15 @@
import javax.annotation.concurrent.ThreadSafe;

/**
* This class provides a static global accessor for telemetry objects {@link Tracer}, {@link Meter}
* and {@link CorrelationContextManager}.
* This class provides a static global accessor for telemetry objects {@link Tracer}, {@link Meter},
* {@link CorrelationContextManager}, and {@link ErrorHandler}.
*
* <p>The telemetry objects are lazy-loaded singletons resolved via {@link ServiceLoader} mechanism.
*
* @see TracerProvider
* @see MeterProviderFactory
* @see CorrelationContextManagerFactory
* @see ErrorHandlerFactory
*/
@ThreadSafe
public final class OpenTelemetry {
Expand All @@ -55,6 +60,7 @@ public final class OpenTelemetry {
private final TracerProvider tracerProvider;
private final MeterProvider meterProvider;
private final CorrelationContextManager contextManager;
private final ErrorHandler errorHandler;

private volatile ContextPropagators propagators =
DefaultContextPropagators.builder().addHttpTextFormat(new HttpTraceContext()).build();
Expand Down Expand Up @@ -171,6 +177,16 @@ public static ContextPropagators getPropagators() {
return getInstance().propagators;
}

/**
* Handles any Opentelemetry errors using the custom specified error handler, or the default error
* handler if one has not been set.
*
* @param e The error to be handled.
*/
public static void handleError(OpenTelemetryException e) {
getInstance().errorHandler.handle(e);
}

/**
* Sets the {@link ContextPropagators} object, which can be used to access the set of registered
* propagators for each supported format.
Expand Down Expand Up @@ -216,6 +232,11 @@ private OpenTelemetry() {
contextManagerProvider != null
? contextManagerProvider.create()
: DefaultCorrelationContextManager.getInstance();
ErrorHandlerFactory errorHandlerFactory = loadSpi(ErrorHandlerFactory.class);
errorHandler =
errorHandlerFactory != null
? errorHandlerFactory.create()
: DefaultErrorHandler.getInstance();
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* 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 io.opentelemetry.errorhandler;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.concurrent.ThreadSafe;

/**
* Default implementation of {@link ErrorHandler}. By default, logs the full string of the error
* with a level of "Warning".
*
* @since 0.1.0
*/
@ThreadSafe
public final class DefaultErrorHandler implements ErrorHandler {
private static final Logger logger = Logger.getLogger(DefaultErrorHandler.class.getName());
private static final ErrorHandler instance = new DefaultErrorHandler();

/**
* Returns a {@code ErrorHandler} singleton that is the default implementation for {@link
* ErrorHandler}.
*
* @return a {@code ErrorHandler} singleton that is the default implementation for {@link
* ErrorHandler}.
*/
public static ErrorHandler getInstance() {
return instance;
}

@Override
public void handle(OpenTelemetryException e) {
logger.log(Level.WARNING, e.getMessage(), e);
}

private DefaultErrorHandler() {}
}
11 changes: 11 additions & 0 deletions api/src/main/java/io/opentelemetry/errorhandler/ErrorHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.opentelemetry.errorhandler;

/** Handler handles irremediable events. */
public interface ErrorHandler {
/**
* Handle handles any error or exception deemed irremediable by an OpenTelemetry component.
*
* @param e The exception to be handled
*/
void handle(OpenTelemetryException e);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.opentelemetry.errorhandler;

/**
* An {@code OpenTelemetryException} is an error or exception which is deemed irremediable by an
* OpenTelemetry component. {@code OpenTelemetryExceptions} should be handled by the registered
* OpenTelemetry {@link ErrorHandler} delegate. {@code OpenTelemetryExceptions} may be extended to
* indicate that the exception comes from a specific OpenTelemetry component.
*/
public class OpenTelemetryException extends RuntimeException {
private static final long serialVersionUID = 0L;

public OpenTelemetryException(String message) {
super(message);
}

public OpenTelemetryException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* 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 io.opentelemetry.errorhandler.spi;

import io.opentelemetry.errorhandler.ErrorHandler;
import javax.annotation.concurrent.ThreadSafe;

/**
* ErrorHandlerFactory is a service provider for a {@link ErrorHandler}. Fully qualified class name
* of the implementation should be registered in {@code
* META-INF/services/io.opentelemetry.errorhandler.spi.ErrorHandlerFactory}. <br>
* <br>
* A specific implementation can be selected by a system property {@code
* io.opentelemetry.errorhandler.spi.ErrorHandlerFactory} with value of fully qualified class name.
*
* @see io.opentelemetry.OpenTelemetry
*/
@ThreadSafe
public interface ErrorHandlerFactory {

/**
* Creates a new ErrorHandler.
*
* @return a new ErrorHandler.
* @since 0.1.0
*/
ErrorHandler create();
}
36 changes: 36 additions & 0 deletions api/src/test/java/io/opentelemetry/OpenTelemetryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
import io.opentelemetry.correlationcontext.CorrelationContextManager;
import io.opentelemetry.correlationcontext.DefaultCorrelationContextManager;
import io.opentelemetry.correlationcontext.spi.CorrelationContextManagerFactory;
import io.opentelemetry.errorhandler.ErrorHandler;
import io.opentelemetry.errorhandler.OpenTelemetryException;
import io.opentelemetry.errorhandler.spi.ErrorHandlerFactory;
import io.opentelemetry.metrics.BatchRecorder;
import io.opentelemetry.metrics.DefaultMeterProvider;
import io.opentelemetry.metrics.DoubleCounter;
Expand Down Expand Up @@ -223,6 +226,27 @@ public void testCorrelationContextManagerNotFound() {
OpenTelemetry.getCorrelationContextManager();
}

@Test
public void testErrorHandlerSystemProperty() throws IOException {
File serviceFile = createService(ErrorHandlerFactory.class, ThrowErrorHandler.class);
System.setProperty(ErrorHandlerFactory.class.getName(), ThrowErrorHandler.class.getName());
OpenTelemetryException e = new OpenTelemetryException("test exception");
try {
OpenTelemetry.handleError(e);
} catch (Throwable t) {
assertThat(t).isEqualTo(e);
} finally {
serviceFile.delete();
}
}

@Test
public void testHandlerNotFound() {
System.setProperty(ErrorHandlerFactory.class.getName(), "io.does.not.exists");
thrown.expect(IllegalStateException.class);
OpenTelemetry.handleError(new OpenTelemetryException("test exception"));
}

@Test
public void testPropagatorsSet() {
ContextPropagators propagators = DefaultContextPropagators.builder().build();
Expand Down Expand Up @@ -449,4 +473,16 @@ public Scope withContext(CorrelationContext distContext) {
return null;
}
}

public static class ThrowErrorHandler implements ErrorHandlerFactory, ErrorHandler {
@Override
public ErrorHandler create() {
return new ThrowErrorHandler();
}

@Override
public void handle(OpenTelemetryException e) {
throw e;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package io.opentelemetry.exporters.inmemory;

import io.opentelemetry.OpenTelemetry;
import io.opentelemetry.errorhandler.OpenTelemetryException;
import io.opentelemetry.sdk.trace.data.SpanData;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.util.ArrayList;
Expand Down Expand Up @@ -88,6 +90,8 @@ public void reset() {
public ResultCode export(Collection<SpanData> spans) {
synchronized (this) {
if (isStopped) {
OpenTelemetry.handleError(
new OpenTelemetryException("Export called on stopped in-memory exporter"));
return ResultCode.FAILURE;
}
finishedSpanItems.addAll(spans);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.opentelemetry.OpenTelemetry;
import io.opentelemetry.errorhandler.OpenTelemetryException;
import io.opentelemetry.exporters.jaeger.proto.api_v2.Collector;
import io.opentelemetry.exporters.jaeger.proto.api_v2.CollectorServiceGrpc;
import io.opentelemetry.exporters.jaeger.proto.api_v2.Model;
Expand Down Expand Up @@ -128,6 +130,7 @@ public ResultCode export(Collection<SpanData> spans) {
stub.postSpans(request);
return ResultCode.SUCCESS;
} catch (Throwable e) {
OpenTelemetry.handleError(new OpenTelemetryException("Error exporting spans.", e));
return ResultCode.FAILURE;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package io.opentelemetry.exporters.logging;

import io.opentelemetry.OpenTelemetry;
import io.opentelemetry.errorhandler.OpenTelemetryException;
import io.opentelemetry.sdk.metrics.data.MetricData;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
import java.util.Collection;
Expand Down Expand Up @@ -47,6 +49,8 @@ public ResultCode flush() {
try {
handler.flush();
} catch (Throwable t) {
OpenTelemetry.handleError(
new OpenTelemetryException("Error flushing in logging metric exporter", t));
resultCode = ResultCode.FAILURE;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package io.opentelemetry.exporters.logging;

import io.opentelemetry.OpenTelemetry;
import io.opentelemetry.errorhandler.OpenTelemetryException;
import io.opentelemetry.sdk.trace.data.SpanData;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.util.Collection;
Expand Down Expand Up @@ -47,6 +49,7 @@ public ResultCode flush() {
try {
handler.flush();
} catch (Throwable t) {
OpenTelemetry.handleError(new OpenTelemetryException("Error flushing exported spans.", t));
resultCode = ResultCode.FAILURE;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package io.opentelemetry.exporters.otlp;

import io.grpc.ManagedChannel;
import io.opentelemetry.OpenTelemetry;
import io.opentelemetry.errorhandler.OpenTelemetryException;
import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest;
import io.opentelemetry.proto.collector.metrics.v1.MetricsServiceGrpc;
import io.opentelemetry.sdk.common.export.ConfigBuilder;
Expand Down Expand Up @@ -94,7 +96,8 @@ public ResultCode export(Collection<MetricData> metrics) {
// noinspection ResultOfMethodCallIgnored
stub.export(exportMetricsServiceRequest);
return ResultCode.SUCCESS;
} catch (Throwable e) {
} catch (Throwable t) {
OpenTelemetry.handleError(new OpenTelemetryException("Error exporting metrics", t));
return ResultCode.FAILURE;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;
import io.opentelemetry.OpenTelemetry;
import io.opentelemetry.errorhandler.OpenTelemetryException;
import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest;
import io.opentelemetry.proto.collector.trace.v1.TraceServiceGrpc;
import io.opentelemetry.sdk.common.export.ConfigBuilder;
Expand Down Expand Up @@ -111,6 +113,7 @@ public ResultCode export(Collection<SpanData> spans) {
stub.export(exportTraceServiceRequest);
return ResultCode.SUCCESS;
} catch (Throwable e) {
OpenTelemetry.handleError(new OpenTelemetryException("Error exporting spans.", e));
return ResultCode.FAILURE;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@

import static java.util.concurrent.TimeUnit.NANOSECONDS;

import io.opentelemetry.OpenTelemetry;
import io.opentelemetry.common.AttributeValue;
import io.opentelemetry.common.ReadableAttributes;
import io.opentelemetry.common.ReadableKeyValuePairs.KeyValueConsumer;
import io.opentelemetry.errorhandler.OpenTelemetryException;
import io.opentelemetry.sdk.common.export.ConfigBuilder;
import io.opentelemetry.sdk.resources.ResourceConstants;
import io.opentelemetry.sdk.trace.data.SpanData;
Expand Down Expand Up @@ -246,6 +248,7 @@ public ResultCode export(final Collection<SpanData> spanDataList) {
try {
sender.sendSpans(encodedSpans).execute();
} catch (IOException e) {
OpenTelemetry.handleError(new OpenTelemetryException("Error exporting spans.", e));
return ResultCode.FAILURE;
}
return ResultCode.SUCCESS;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.google.common.annotations.VisibleForTesting;
import io.opentelemetry.OpenTelemetry;
import io.opentelemetry.common.Labels;
import io.opentelemetry.errorhandler.OpenTelemetryException;
import io.opentelemetry.internal.Utils;
import io.opentelemetry.metrics.LongCounter;
import io.opentelemetry.metrics.LongCounter.BoundLongCounter;
Expand Down Expand Up @@ -287,7 +288,8 @@ public void run() {
try {
spanExporter.export(spans);
} catch (Throwable t) {
logger.log(Level.WARNING, "Exception thrown by the export.", t);
OpenTelemetry.handleError(
new OpenTelemetryException("Exception thrown by the export.", t));
}
}
});
Expand Down
Loading

0 comments on commit 2e7243d

Please sign in to comment.