blob: e0f99016c566e283d799eb04b9607f3ccf1a971f [file] [log] [blame]
/*
* Copyright (c) 2017, 2019 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package org.glassfish.jersey.examples.opentracing;
import java.util.concurrent.Executors;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.opentracing.OpenTracingFeature;
import org.glassfish.jersey.opentracing.OpenTracingUtils;
import org.glassfish.jersey.server.ManagedAsync;
import org.glassfish.jersey.server.Uri;
import io.opentracing.Span;
/**
* OpenTracing example resource.
* <p>
* Jersey (with registered {@link OpenTracingFeature} will automatically
* create and start span for each request ("root" span or "request" span) and a child span to be used in the resource method
* ("resource" span). The root span is used for Jersey-level event logging (resource matching started, request filters applied,
* etc). The resource span serves for application-level event logging purposes (used-defined). Both are automatically created
* and also automatically finished.
* <p>
* Resource span is created right before the resource method invocation and finished right after resource method finishes. It
* can be resolved by calling {@link OpenTracingUtils#getRequestSpan(ContainerRequestContext)}.
* <p>
* Application code can also create ad-hoc spans as child spans of the resource span. This can be achieved by calling one of the
* convenience methods {@link OpenTracingUtils#getRequestChildSpan(ContainerRequestContext)}.
* <p>
* {@link ContainerRequestContext} can be obtained via injection.
* <p>
* All the ad-hoc created spans MUST be {@link Span#finish() finished} explicitly.
*
* @author Adam Lindenthal
*/
@Path(value = "/resource")
public class TracedResource {
/**
* Resource method with no explicit tracing.
* <p>
* One span (jersey-server) will be created and finished automatically.
*
* @return dummy response
*/
@GET
@Path("defaultTrace")
public Response defaultTrace() {
return Response.ok("foo").build();
}
/**
* Resource method with explicit logging into resource span.
*
* @param context injected request context with resource-level span reference
* @return dummy response
* @throws InterruptedException if interrupted
*/
@GET
@Path("appLevelLogging")
public Response appLevelLogging(@Context ContainerRequestContext context) throws InterruptedException {
final Span resourceSpan = OpenTracingUtils
.getRequestSpan(context)
.orElseThrow(() -> new RuntimeException("Tracing has failed"));
resourceSpan.log("Starting expensive operation.");
// Do the business
Thread.sleep(200);
resourceSpan.log("Expensive operation finished.");
resourceSpan.setTag("expensiveOperationSuccess", true);
return Response.ok("SUCCESS").build();
}
/**
* Similar as {@link #appLevelLogging(ContainerRequestContext)}, just with {@code POST} method.
*
* @param entity posted entity
* @param context injected context
* @return dummy response
*/
@POST
@Path("appLevelPost")
public Response tracePost(String entity, @Context ContainerRequestContext context) {
final Span resourceSpan = OpenTracingUtils
.getRequestSpan(context)
.orElseThrow(() -> new RuntimeException("Tracing has failed"));
resourceSpan.setTag("result", "42");
resourceSpan.setBaggageItem("entity", entity);
return Response.ok("Done!").build();
}
/**
* Resource method with explicit child span creation.
*
* @param context injected request context with resource-level (parent) span reference
* @return dummy response
* @throws InterruptedException if interrupted
*/
@GET
@Path("childSpan")
public Response childSpan(@Context ContainerRequestContext context) throws InterruptedException {
final Span childSpan = OpenTracingUtils.getRequestChildSpan(context, "AppCreatedSpan");
childSpan.log("Starting expensive operation.");
// Do the business
Thread.sleep(200);
childSpan.log("Expensive operation finished.");
childSpan.setTag("expensiveOperationSuccess", true);
childSpan.finish();
return Response.ok("SUCCESS").build();
}
/**
* Resource method with explicit span creation and propagation into injected managed client.
* <p>
* Shows how to propagate the server-side span into managed client (or any common Jersey client).
* This way, the client span will be child of the resource span.
*
* @param context injected context
* @param wt injected web target
* @return dummy response
*/
@GET
@Path("managedClient")
public Response traceWithManagedClient(@Context ContainerRequestContext context,
@Uri("resource/appLevelPost") WebTarget wt) {
final Span providedSpan = OpenTracingUtils
.getRequestSpan(context)
.orElseThrow(() -> new RuntimeException("Tracing failed"));
providedSpan.log("Resource method started.");
final Response response = wt.request()
.property(OpenTracingFeature.SPAN_CONTEXT_PROPERTY, providedSpan.context()) // <--- span propagation
.post(Entity.text("Hello"));
providedSpan.log("1st Response received from managed client");
providedSpan.log("Firing 1st request from managed client");
providedSpan.log("Creating child span");
final Span childSpan = OpenTracingUtils.getRequestChildSpan(context, "jersey-resource-child-span");
childSpan.log("Firing 2nd request from managed client");
final Response response2 = wt.request()
.property(OpenTracingFeature.SPAN_CONTEXT_PROPERTY, childSpan.context()) // <--- span propagation
.post(Entity.text("World"));
childSpan.log("2st Response received from managed client");
childSpan.finish();
return Response.ok("Result: " + response.getStatus() + ", " + response2.getStatus()).build();
}
@GET
@Path("async")
@ManagedAsync
public void traceWithAsync(@Suspended final AsyncResponse asyncResponse, @Context ContainerRequestContext context) {
final Span span = OpenTracingUtils.getRequestSpan(context).orElseThrow(() -> new RuntimeException("tracing failed"));
span.log("In the resource method.");
Executors.newSingleThreadExecutor().submit(() -> {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
span.log("Interrupted");
e.printStackTrace();
}
span.log("Resuming");
asyncResponse.resume("OK");
});
span.log("Before exiting the resource method");
}
@GET
@Path("error")
public String failTrace(@Context ContainerRequestContext context) {
throw new RuntimeException("Failing just for fun.");
}
}