Initial Contribution Signed-off-by: Jan Supol <jan.supol@oracle.com>
diff --git a/incubator/open-tracing/pom.xml b/incubator/open-tracing/pom.xml new file mode 100644 index 0000000..f96771e --- /dev/null +++ b/incubator/open-tracing/pom.xml
@@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v. 2.0, which is available at + http://www.eclipse.org/legal/epl-2.0. + + This Source Code may also be made available under the following Secondary + Licenses when the conditions for such availability set forth in the + Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + version 2 with the GNU Classpath Exception, which is available at + https://www.gnu.org/software/classpath/license.html. + + SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.glassfish.jersey.incubator</groupId> + <artifactId>project</artifactId> + <version>2.28-SNAPSHOT</version> + </parent> + + <groupId>org.glassfish.jersey.incubator</groupId> + <artifactId>jersey-open-tracing</artifactId> + <packaging>jar</packaging> + + <name>jersey-open-tracing</name> + + <description> + Jersey support for OpenTracing. + </description> + + <dependencies> + <dependency> + <groupId>io.opentracing</groupId> + <artifactId>opentracing-api</artifactId> + </dependency> + + <dependency> + <groupId>io.opentracing</groupId> + <artifactId>opentracing-util</artifactId> + </dependency> + + <dependency> + <groupId>org.glassfish.jersey.core</groupId> + <artifactId>jersey-common</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.glassfish.jersey.core</groupId> + <artifactId>jersey-server</artifactId> + <version>${project.version}</version> + </dependency> + + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.glassfish.jersey.test-framework.providers</groupId> + <artifactId>jersey-test-framework-provider-grizzly2</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>com.sun.istack</groupId> + <artifactId>maven-istack-commons-plugin</artifactId> + <inherited>true</inherited> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <inherited>true</inherited> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <inherited>true</inherited> + </plugin> + </plugins> + </build> +</project>
diff --git a/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingApplicationEventListener.java b/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingApplicationEventListener.java new file mode 100644 index 0000000..d155760 --- /dev/null +++ b/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingApplicationEventListener.java
@@ -0,0 +1,272 @@ +/* + * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.opentracing; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.ContainerResponseFilter; + +import org.glassfish.jersey.server.ContainerRequest; +import org.glassfish.jersey.server.ContainerResponse; +import org.glassfish.jersey.server.monitoring.ApplicationEvent; +import org.glassfish.jersey.server.monitoring.ApplicationEventListener; +import org.glassfish.jersey.server.monitoring.RequestEvent; +import org.glassfish.jersey.server.monitoring.RequestEventListener; + +import io.opentracing.Span; +import io.opentracing.SpanContext; +import io.opentracing.Tracer; +import io.opentracing.propagation.Format; +import io.opentracing.propagation.TextMapExtractAdapter; +import io.opentracing.tag.Tags; +import io.opentracing.util.GlobalTracer; + +/** + * Application event listener responsible for creating and propagating server-side request {@link io.opentracing.Span}. + * + * @author Adam Lindenthal (adam.lindenthal at oracle.com) + * @since 2.26 + */ +class OpenTracingApplicationEventListener implements ApplicationEventListener { + private final Tracer globalTracer = GlobalTracer.get(); + private final OpenTracingFeature.Verbosity verbosity; + + /** + * Creates event listener instance with given {@link org.glassfish.jersey.opentracing.OpenTracingFeature.Verbosity}. + * + * @param verbosity desired verbosity level + */ + public OpenTracingApplicationEventListener(OpenTracingFeature.Verbosity verbosity) { + this.verbosity = verbosity; + } + + @Override + public void onEvent(ApplicationEvent event) { + // we don't care about the server lifecycle + } + + @Override + public RequestEventListener onRequest(RequestEvent requestEvent) { + if (requestEvent.getType() == RequestEvent.Type.START) { + Span requestSpan = handleRequestStart(requestEvent.getContainerRequest()); + return new OpenTracingRequestEventListener(requestSpan); + } + return null; + } + + private Span handleRequestStart(ContainerRequest request) { + + final Map<String, String> mappedHeaders = request + .getHeaders() + .entrySet() + .stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + (entry) -> OpenTracingUtils.formatList(entry.getValue()))); + + final SpanContext extractedContext = + globalTracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapExtractAdapter(mappedHeaders)); + + Tracer.SpanBuilder spanBuilder = globalTracer + .buildSpan(OpenTracingFeature.DEFAULT_REQUEST_SPAN_NAME) + .withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER) + .withTag(Tags.HTTP_METHOD.getKey(), request.getMethod()) + .withTag(Tags.HTTP_URL.getKey(), request.getRequestUri().toASCIIString()) + .withTag(LocalizationMessages.OPENTRACING_TAG_REQUEST_HEADERS(), + OpenTracingUtils.headersAsString(request.getHeaders())) + .withTag(LocalizationMessages.OPENTRACING_TAG_HAS_REQUEST_ENTITY(), request.hasEntity()); + + if (extractedContext != null) { + spanBuilder = spanBuilder.asChildOf(extractedContext); + } + + final Span span = spanBuilder.startManual(); + request.setProperty(OpenTracingFeature.SPAN_CONTEXT_PROPERTY, span); + span.log(LocalizationMessages.OPENTRACING_LOG_REQUEST_STARTED()); + return span; + } + + class OpenTracingRequestEventListener implements RequestEventListener { + private Span requestSpan; + private Span resourceSpan = null; + + OpenTracingRequestEventListener(final Span requestSpan) { + this.requestSpan = requestSpan; + } + + @Override + public void onEvent(RequestEvent event) { + + switch (event.getType()) { + case MATCHING_START: + logVerbose(LocalizationMessages.OPENTRACING_LOG_MATCHING_STARTED()); + break; + + case LOCATOR_MATCHED: + logVerbose(LocalizationMessages.OPENTRACING_LOG_LOCATOR_MATCHED( + OpenTracingUtils.formatList(event.getUriInfo().getMatchedResourceLocators()))); + break; + + case SUBRESOURCE_LOCATED: + logVerbose(LocalizationMessages.OPENTRACING_LOG_SUBRESOURCE_LOCATED( + OpenTracingUtils.formatList(event.getUriInfo().getLocatorSubResources()))); + break; + + + case REQUEST_MATCHED: + logVerbose(LocalizationMessages.OPENTRACING_LOG_REQUEST_MATCHED(event.getUriInfo() + .getMatchedResourceMethod() + .getInvocable() + .getDefinitionMethod())); + log(LocalizationMessages.OPENTRACING_LOG_REQUEST_FILTERING_STARTED()); + break; + + case REQUEST_FILTERED: + List<ContainerRequestFilter> requestFilters = new ArrayList<>(); + event.getContainerRequestFilters().forEach(requestFilters::add); + + logVerbose(LocalizationMessages.OPENTRACING_LOG_REQUEST_FILTERING_FINISHED(requestFilters.size())); + if (requestFilters.size() > 0) { + log(LocalizationMessages.OPENTRACING_LOG_APPLIED_REQUEST_FILTERS( + OpenTracingUtils.formatProviders(requestFilters))); + } + break; + + case RESOURCE_METHOD_START: + logVerbose(LocalizationMessages.OPENTRACING_LOG_RESOURCE_METHOD_STARTED( + event.getUriInfo().getMatchedResourceMethod().getInvocable().getDefinitionMethod())); + + resourceSpan = globalTracer.buildSpan(OpenTracingFeature.DEFAULT_RESOURCE_SPAN_NAME) + .asChildOf(requestSpan) + .startManual(); + + event.getContainerRequest().setProperty(OpenTracingFeature.SPAN_CONTEXT_PROPERTY, resourceSpan); + break; + + case RESOURCE_METHOD_FINISHED: + log(LocalizationMessages.OPENTRACING_LOG_RESOURCE_METHOD_FINISHED()); + break; + + case RESP_FILTERS_START: + // this is the first event after resource method is guaranteed to have finished, even for asynchronous + // processing; resourceSpan will be finished and the span in the context will be switched back to the + // resource span before any further tracing can occur. + event.getContainerRequest().setProperty(OpenTracingFeature.SPAN_CONTEXT_PROPERTY, requestSpan); + resourceSpan.finish(); + logVerbose(LocalizationMessages.OPENTRACING_LOG_RESPONSE_FILTERING_STARTED()); + break; + + case RESP_FILTERS_FINISHED: + List<ContainerResponseFilter> responseFilters = new ArrayList<>(); + event.getContainerResponseFilters().forEach(responseFilters::add); + logVerbose(LocalizationMessages.OPENTRACING_LOG_RESPONSE_FILTERING_FINISHED(responseFilters.size())); + if (responseFilters.size() > 0) { + log(LocalizationMessages.OPENTRACING_LOG_APPLIED_RESPONSE_FILTERS( + OpenTracingUtils.formatProviders(responseFilters))); + } + break; + + case ON_EXCEPTION: + if (resourceSpan != null) { + resourceSpan.setTag(Tags.ERROR.getKey(), true); + resourceSpan.finish(); + } + requestSpan.setTag(Tags.ERROR.getKey(), true); + logError(event.getException()); + break; + + case EXCEPTION_MAPPER_FOUND: + log(LocalizationMessages.OPENTRACING_LOG_EXCEPTION_MAPPER_FOUND( + event.getExceptionMapper().getClass().getName())); + break; + + case EXCEPTION_MAPPING_FINISHED: + log(LocalizationMessages.OPENTRACING_LOG_EXCEPTION_MAPPING_FINISHED() + + (event.isResponseSuccessfullyMapped() + ? LocalizationMessages.OPENTRACING_LOG_EXCEPTION_MAPPING_SUCCESS() + : LocalizationMessages.OPENTRACING_LOG_EXCEPTION_MAPPING_NOEXCEPTION_OR_FAILED())); + + break; + + + case FINISHED: + if (requestSpan != null) { + ContainerResponse response = event.getContainerResponse(); + if (response != null) { + int status = response.getStatus(); + requestSpan + .setTag(Tags.HTTP_STATUS.getKey(), status) + .setTag(LocalizationMessages.OPENTRACING_TAG_HAS_RESPONSE_ENTITY(), response.hasEntity()) + .setTag(LocalizationMessages.OPENTRACING_TAG_RESPONSE_LENGTH(), response.getLength()); + + if (400 <= status) { + requestSpan.setTag(Tags.ERROR.getKey(), true); + } + } + requestSpan.finish(); + } + break; + } + } + + /** + * Adds a {@link OpenTracingFeature.Verbosity#TRACE}-level log entry into the request span. + * @param s log message + */ + private void logVerbose(String s) { + log(OpenTracingFeature.Verbosity.TRACE, s); + } + + /** + * Adds a {@link OpenTracingFeature.Verbosity#INFO}-level log entry into the request span. + * @param s log message + */ + private void log(String s) { + log(OpenTracingFeature.Verbosity.INFO, s); + } + + /** + * Adds a log entry with given {@link org.glassfish.jersey.opentracing.OpenTracingFeature.Verbosity}-level into the + * request span. + * + * @param level desired verbosity level + * @param s log message + */ + private void log(OpenTracingFeature.Verbosity level, String s) { + if (level.ordinal() <= verbosity.ordinal()) { + requestSpan.log(s); + } + } + + /** + * Adds an error log into the request span. + * @param t exception to be logged. + */ + private void logError(final Throwable t) { + Map<String, Object> errorMap = new HashMap<>(2); + errorMap.put("event", "error"); + errorMap.put("error.object", t); + requestSpan.log(errorMap); + } + } + +}
diff --git a/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingClientRequestFilter.java b/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingClientRequestFilter.java new file mode 100644 index 0000000..7934488 --- /dev/null +++ b/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingClientRequestFilter.java
@@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.opentracing; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.ClientRequestFilter; +import javax.ws.rs.core.MediaType; + +import io.opentracing.Span; +import io.opentracing.SpanContext; +import io.opentracing.Tracer; +import io.opentracing.propagation.Format; +import io.opentracing.propagation.TextMapInjectAdapter; +import io.opentracing.tag.Tags; +import io.opentracing.util.GlobalTracer; + +/** + * Client-side request filter, that creates the client request {@code Span}. + * <p> + * Stores request-related metadata into the {@code Span} as {@code Tags} + * and {@link GlobalTracer#inject(SpanContext, Format, Object) injects} it into http headers. + * + * @author Adam Lindenthal (adam.lindenthal at oracle.com) + * @since 2.26 + */ +class OpenTracingClientRequestFilter implements ClientRequestFilter { + + @Override + public void filter(ClientRequestContext requestContext) throws IOException { + Tracer.SpanBuilder spanBuilder = GlobalTracer.get() + .buildSpan(LocalizationMessages.OPENTRACING_SPAN_PREFIX_CLIENT() + requestContext.getMethod()) + .withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT) + .withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT) + .withTag(Tags.HTTP_URL.getKey(), requestContext.getUri().toASCIIString()) + .withTag(Tags.HTTP_METHOD.getKey(), requestContext.getMethod()) + .withTag(LocalizationMessages.OPENTRACING_TAG_HAS_REQUEST_ENTITY(), requestContext.hasEntity()) + .withTag(LocalizationMessages.OPENTRACING_TAG_ACCEPTABLE_MEDIA_TYPES(), requestContext.getAcceptableMediaTypes() + .stream() + .map(MediaType::toString) + .collect(Collectors.joining(", "))) + .withTag(LocalizationMessages.OPENTRACING_TAG_REQUEST_HEADERS(), + OpenTracingUtils.headersAsString(requestContext.getHeaders())); + + // if pre-stored "span" property is found, propagate the stored context + final Object property = requestContext.getProperty(OpenTracingFeature.SPAN_CONTEXT_PROPERTY); + if (property != null && property instanceof SpanContext) { + spanBuilder = spanBuilder.asChildOf((SpanContext) property); + } + Span span = spanBuilder.startManual(); + + requestContext.setProperty(OpenTracingFeature.SPAN_CONTEXT_PROPERTY, span); + Map<String, String> addedHeaders = new HashMap<>(); + GlobalTracer.get().inject(span.context(), Format.Builtin.HTTP_HEADERS, new TextMapInjectAdapter(addedHeaders)); + addedHeaders.forEach((key, value) -> requestContext.getHeaders().add(key, value)); + } +}
diff --git a/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingClientResponseFilter.java b/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingClientResponseFilter.java new file mode 100644 index 0000000..0dae48f --- /dev/null +++ b/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingClientResponseFilter.java
@@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.opentracing; + +import java.io.IOException; + +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.ClientResponseContext; +import javax.ws.rs.client.ClientResponseFilter; + +import io.opentracing.Span; +import io.opentracing.tag.Tags; + +/** + * Retrieves stored span from the {@link ClientRequestContext} and adds response details to the tracing info. + * + * @author Adam Lindenthal (adam.lindenthal at oracle.com) + * @since 2.26 + */ +public class OpenTracingClientResponseFilter implements ClientResponseFilter { + + @Override + public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException { + final Object spanProperty = requestContext.getProperty(OpenTracingFeature.SPAN_CONTEXT_PROPERTY); + if (spanProperty != null && spanProperty instanceof Span) { + ((Span) spanProperty) + .setTag(Tags.HTTP_STATUS.getKey(), responseContext.getStatus()) + .setTag(LocalizationMessages.OPENTRACING_TAG_HAS_RESPONSE_ENTITY(), responseContext.hasEntity()) + .setTag(LocalizationMessages.OPENTRACING_TAG_RESPONSE_LENGTH(), responseContext.getLength()) + .setTag(LocalizationMessages.OPENTRACING_TAG_RESPONSE_HEADERS(), + OpenTracingUtils.headersAsString(responseContext.getHeaders())) + .finish(); + } + } +}
diff --git a/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingFeature.java b/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingFeature.java new file mode 100644 index 0000000..6afb1f9 --- /dev/null +++ b/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingFeature.java
@@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.opentracing; + +import java.util.logging.Logger; + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.core.Feature; +import javax.ws.rs.core.FeatureContext; + +import org.glassfish.jersey.Beta; + +import io.opentracing.util.GlobalTracer; + +/** + * A feature that enables OpenTracing support on server and client. + * + * @author Adam Lindenthal (adam.lindenthal at oracle.com) + * @since 2.26 + */ +@Beta +public class OpenTracingFeature implements Feature { + private static final Logger LOGGER = Logger.getLogger(OpenTracingFeature.class.getName()); + private final Verbosity verbosity; + + /** + * Creates feature instance with default ({@link Verbosity#INFO} verbosity level. + */ + public OpenTracingFeature() { + verbosity = Verbosity.INFO; + } + + /** + * Creates feature instance with given ({@link Verbosity} level. + * @param verbosity desired level of logging verbosity + */ + public OpenTracingFeature(Verbosity verbosity) { + this.verbosity = verbosity; + } + + /** + * Stored span's {@link ContainerRequestContext} property key. + */ + public static final String SPAN_CONTEXT_PROPERTY = "span"; + + /** + * Default resource span name. + */ + public static final String DEFAULT_RESOURCE_SPAN_NAME = "jersey-resource"; + + /** + * Default child span name. + */ + public static final String DEFAULT_CHILD_SPAN_NAME = "jersey-resource-app"; + + /** + * Default request "root" span name. + */ + public static final String DEFAULT_REQUEST_SPAN_NAME = "jersey-server"; + + @Override + public boolean configure(FeatureContext context) { + if (!GlobalTracer.isRegistered()) { + LOGGER.warning(LocalizationMessages.OPENTRACING_TRACER_NOT_REGISTERED()); + } + + switch (context.getConfiguration().getRuntimeType()) { + case CLIENT: + context.register(OpenTracingClientRequestFilter.class).register(OpenTracingClientResponseFilter.class); + break; + case SERVER: + context.register(new OpenTracingApplicationEventListener(verbosity)); + } + return true; + } + + /** + * OpenTracing Jersey event logging verbosity. + */ + public enum Verbosity { + /** + * Only logs basic Jersey processing related events. + */ + INFO, + + /** + * Logs more fine grained events related to Jersey processing. + */ + TRACE + } + +}
diff --git a/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingUtils.java b/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingUtils.java new file mode 100644 index 0000000..4bc3ad0 --- /dev/null +++ b/incubator/open-tracing/src/main/java/org/glassfish/jersey/opentracing/OpenTracingUtils.java
@@ -0,0 +1,123 @@ +/* + * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.opentracing; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.core.MultivaluedMap; + +import io.opentracing.Span; +import io.opentracing.Tracer; +import io.opentracing.util.GlobalTracer; + +/** + * Utility methods for Jersey OpenTracing integration. + * + * @author Adam Lindenthal (adam.lindenthal at oracle.com) + * @since 2.26 + */ +public class OpenTracingUtils { + + private OpenTracingUtils() { + } + + /** + * Resolve resource-level span. + * <p> + * If open tracing is enabled and {@link GlobalTracer} is registered, resource-level span should be stored in the + * {@link OpenTracingFeature#SPAN_CONTEXT_PROPERTY}. This span is resolved and returned as an {@link Optional}. + * + * @param context {@link ContainerRequestContext} instance, can be obtained via {@code @Context} injection + * @return {@link Optional} of the resolved span, if found; empty optional if not + */ + public static Optional<Span> getRequestSpan(final ContainerRequestContext context) { + if (context != null) { + final Object spanProperty = context.getProperty(OpenTracingFeature.SPAN_CONTEXT_PROPERTY); + if (spanProperty != null && spanProperty instanceof Span) { + return Optional.of((Span) spanProperty); + } + } + return Optional.empty(); + } + + /** + * Create and start ad-hoc custom span with the default name as a child span of the request span (if available). + * + * @param context {@link ContainerRequestContext} instance, can be obtained via {@code @Context} injection + * @return If parent span ("request span") instance is stored in the {@code ContainerRequestContext}, new span is created + * as a child span of the found span. If no parent span found, new "root" span is created. In both cases, the returned span + * is already started. In order to successfully store the tracing, {@link Span#finish()} needs to be invoked explicitly, + * after the traced code finishes. + */ + public static Span getRequestChildSpan(final ContainerRequestContext context) { + return getRequestChildSpan(context, OpenTracingFeature.DEFAULT_CHILD_SPAN_NAME); + } + + /** + * Create and start ad-hoc custom span with a custom name as a child span of the request span (if available). + * + * @param context {@link ContainerRequestContext} instance, can be obtained via {@code @Context} injection + * @param spanName name to be used for the created span + * @return If parent span ("request span") instance is stored in the {@code ContainerRequestContext}, new span is created + * as a child span of the found span. If no parent span found, new "root" span is created. In both cases, the returned span + * is already started. In order to successfully store the tracing, {@link Span#finish()} needs to be invoked explicitly, + * after the traced code finishes. + */ + public static Span getRequestChildSpan(final ContainerRequestContext context, final String spanName) { + Tracer.SpanBuilder spanBuilder = GlobalTracer.get().buildSpan(spanName); + if (context != null) { + final Object spanProperty = context.getProperty(OpenTracingFeature.SPAN_CONTEXT_PROPERTY); + if (spanProperty != null && spanProperty instanceof Span) { + spanBuilder = spanBuilder.asChildOf((Span) spanProperty); + } + } + return spanBuilder.startManual(); + } + + /** + * Convert request/response headers from {@link MultivaluedMap} into printable form. + * + * @param headers multi-valued map of request or response headers + * @return {@code String} representation, e.g. "[header1=foo]; [header2=bar, baz]" + */ + static String headersAsString(final MultivaluedMap<String, ?> headers) { + return headers.entrySet() + .stream() + .map((entry) -> "[" + + entry.getKey() + "=" + + entry.getValue() + .stream() + .map(Object::toString) + .collect(Collectors.joining(", ")) + + "]") + .collect(Collectors.joining("; ")); + } + + static String formatList(List<?> list) { + return list.stream().map(Object::toString).collect(Collectors.joining(", ")); + } + + static String formatProviders(Iterable<?> providers) { + return StreamSupport.stream(providers.spliterator(), false) + .map((provider) -> provider.getClass().getName()) + .collect(Collectors.joining(", ")); + } +}
diff --git a/incubator/open-tracing/src/main/resources/org/glassfish/jersey/opentracing/localization.properties b/incubator/open-tracing/src/main/resources/org/glassfish/jersey/opentracing/localization.properties new file mode 100644 index 0000000..67f8508 --- /dev/null +++ b/incubator/open-tracing/src/main/resources/org/glassfish/jersey/opentracing/localization.properties
@@ -0,0 +1,44 @@ +# +# Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved. +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Public License v. 2.0, which is available at +# http://www.eclipse.org/legal/epl-2.0. +# +# This Source Code may also be made available under the following Secondary +# Licenses when the conditions for such availability set forth in the +# Eclipse Public License v. 2.0 are satisfied: GNU General Public License, +# version 2 with the GNU Classpath Exception, which is available at +# https://www.gnu.org/software/classpath/license.html. +# +# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 +# + +opentracing.tracer.not.registered=OpenTracing feature is enabled, but no Tracer has been registered. Default no-op tracer will \ + be used. +opentracing.log.request.started=Request started. +opentracing.log.matching.started=Resource matching started. +opentracing.log.request.matched=Request matched, method: {0} +opentracing.log.locator.matched=Locator matched. Matched locators: {0} +opentracing.log.subresource.located=Subresource located: {0} +opentracing.log.request.filtering.started=Request filtering started. +opentracing.log.request.filtering.finished=Request filtering finished, {0} filter(s) applied. +opentracing.log.applied.request.filters=Applied request filters: {0}. +opentracing.log.resource.method.started=Resource method {0} started. +opentracing.log.response.filtering.started=Response filtering started. +opentracing.log.resource.method.finished=Resource method finished. +opentracing.log.response.filtering.finished=Response filtering finished, {0} filter(s) applied. +opentracing.log.applied.response.filters=Applied reqsponse filters: {0}. +opentracing.log.exception=Exception while processing the request: {0}. +opentracing.log.exception.mapper.found=Exception mapper found: {0}. +opentracing.log.exception.mapping.finished=Exception mapping finished: +opentracing.log.exception.mapping.success=successfully mapped +opentracing.log.exception.mapping.noexception.or.failed=no exception or mapping failed. +opentracing.tag.request.headers=requestHeaders +opentracing.tag.response.headers=responseHeaders +opentracing.tag.has.request.entity=hasRequestEntity +opentracing.tag.has.response.entity=hasResponseEntity +opentracing.tag.response.length=responseLength +opentracing.tag.acceptable.media.types=acceptableMediaTypes +opentracing.span.prefix.client=jersey-client- +