diff --git a/.editorconfig b/.editorconfig index f9c0dc9b3f..3a77f6181c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -663,10 +663,8 @@ ij_groovy_while_brace_force = never ij_groovy_while_on_new_line = false ij_groovy_wrap_long_lines = false -[{*.gradle.kts,*.kt,*.kts,*.main.kts}] -indent_size = 4 -tab_width = 4 -ij_continuation_indent_size = 8 +[{*.kt,*.kts}] +ij_continuation_indent_size = 2 ij_kotlin_align_in_columns_case_branch = false ij_kotlin_align_multiline_binary_operation = false ij_kotlin_align_multiline_extends_list = false diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000000..503bd4c990 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,14 @@ +#!/bin/bash +# http://redsymbol.net/articles/unofficial-bash-strict-mode/ +set -euo pipefail +IFS=$'\n\t' + +if ! ./gradlew spotlessCheck; then + ./gradlew spotlessApply + echo "" + echo "" + echo -e "\033[0;33mCode has been formatted; please git diff/add and recommit." + echo "" + echo "" + exit 1 +fi diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 58ce41a433..53c83d8b82 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -83,8 +83,8 @@ Gradle plugin. #### Snapshot builds For developers testing code changes before a release is complete, there are -snapshot builds of the `master` branch. They are available from -[JFrog OSS repository](https://oss.jfrog.org/artifactory/oss-snapshot-local/io/opentelemetry/auto/) +snapshot builds of the `master` branch. They are available from +[JFrog OSS repository](https://oss.jfrog.org/artifactory/oss-snapshot-local/io/opentelemetry/auto/) #### Building from source @@ -145,31 +145,34 @@ We follow the [Google Java Style Guide](https://google.github.io/styleguide/javaguide.html). Our build will fail if source code is not formatted according to that style. -To verify code style manually run the following command, which uses -[google-java-format](https://github.com/google/google-java-format) library: +The main goal is to avoid extensive reformatting caused by different IDEs having different opinion +about how things should be formatted by establishing. -`./gradlew verifyGoogleJavaFormat` +Running -or on Windows +```bash +./gradlew spotlessApply +``` -`gradlew.bat verifyGoogleJavaFormat` +reformats all the files that need reformatting. -Instead of fixing style inconsistencies by hand, you can run gradle task -`googleJavaFormat` to automatically fix all found issues: +Running -`./gradlew googleJavaFormat` +```bash +./gradlew spotlessCheck +``` -or on Windows - -`gradlew.bat googleJavaFormat` +runs formatting verify task only. #### Pre-commit hook -To completely delegate code style formatting to the machine, you can add [git -pre-commit hook](https://git-scm.com/docs/githooks). We provide an example -script in `buildscripts/pre-commit` file. Just copy or symlink it into -`.git/hooks` folder. +To completely delegate code style formatting to the machine, +there is a pre-commit hook setup to verify formatting before committing. +It can be activated with this command: +```bash +git config core.hooksPath .githooks +``` #### Editorconfig diff --git a/README.md b/README.md index 421485cafa..eeabbf6991 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,8 @@ This project provides a Java agent JAR that can be attached to any Java 7+ application and dynamically injects bytecode to capture telemetry from a -number of popular libraries and frameworks. -The telemetry data can be exported in a variety of formats. +number of popular libraries and frameworks. +The telemetry data can be exported in a variety of formats. In addition, the agent and exporter can be configured via command line arguments or environment variables. The net result is the ability to gather telemetry data from a Java application without code changes. @@ -29,11 +29,11 @@ The instrumentation agent is enabled using the `-javaagent` flag to the JVM. java -javaagent:path/to/opentelemetry-auto-all.jar \ -jar myapp.jar ``` -By default OpenTelemetry Java agent uses +By default OpenTelemetry Java agent uses [OTLP exporter](https://github.com/open-telemetry/opentelemetry-java/tree/master/exporters/otlp) -configured to send data to +configured to send data to [OpenTelemetry collector](https://github.com/open-telemetry/opentelemetry-collector/blob/master/receiver/otlpreceiver/README.md) -at `localhost:55680`. +at `localhost:55680`. Configuration parameters are passed as Java system properties (`-D` flags) or as environment variables (see below for full list). For example: @@ -67,7 +67,7 @@ A simple wrapper for the Zipkin exporter of opentelemetry-java. It POSTs json in |----------------------------------|----------------------------------|----------------------------------------------------------------------| | ota.exporter=zipkin | OTA_EXPORTER=zipkin | To select Zipkin exporter | | ota.exporter.zipkin.endpoint | OTA_EXPORTER_ZIPKIN_ENDPOINT | The Zipkin endpoint to connect to. Currently only HTTP is supported. | -| ota.exporter.zipkin.service.name | OTA_EXPORTER_ZIPKIN_SERVICE_NAME | The service name of this JVM instance +| ota.exporter.zipkin.service.name | OTA_EXPORTER_ZIPKIN_SERVICE_NAME | The service name of this JVM instance #### OTLP exporter @@ -94,7 +94,7 @@ attributes to stdout. It is used mainly for testing and debugging. *This is highly advanced behavior and still in the prototyping phase. It may change drastically or be removed completely. Use with caution* -The OpenTelemetry API exposes SPI [hooks](https://github.com/open-telemetry/opentelemetry-java/blob/master/api/src/main/java/io/opentelemetry/trace/spi/TracerProviderFactory.java) +The OpenTelemetry API exposes SPI [hooks](https://github.com/open-telemetry/opentelemetry-java/blob/master/api/src/main/java/io/opentelemetry/trace/spi/TracerProviderFactory.java) for customizing its behavior, such as the `Resource` attached to spans or the `Sampler`. Because the auto instrumentation runs in a separate classpath than the instrumented application, it is not possible for customization in the application to take advantage of this customization. In order to provide such customization, you can @@ -163,7 +163,7 @@ For this reason the following instrumentations are disabled by default: - `jdbc-datasource` which creates spans whenever `java.sql.DataSource#getConnection` method is called. - `servlet-filter` which creates spans around Servlet Filter methods. - `servlet-service` which creates spans around Servlet methods. - + To enable them, add `ota.integration..enabled` system property: `-Dota.integration.jdbc-datasource.enabled=true` diff --git a/RELEASING.md b/RELEASING.md index 737de680e1..2fb392a465 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -9,7 +9,7 @@ to calculate the current version based on git tags. This plugin looks for the la ## Snapshot builds Every successful CI build of the master branch automatically executes `./gradlew snapshot` as the last task. -This signals Nebula plugin to build and publish to +This signals Nebula plugin to build and publish to [JFrog OSS repository](https://oss.jfrog.org/artifactory/oss-snapshot-local/io/opentelemetry/auto/)next _minor_ release version. This means version `vX.(Y+1).0-SNAPSHOT`. @@ -23,12 +23,12 @@ Do the following: On new tag creation a CI will start a new release build. It will do the following: - Checkout requested tag. -- Run `./gradlew -Prelease.useLastTag=true final`. -This signals Nebula plugin to build `X.Y.0` version and to publish it to +- Run `./gradlew -Prelease.useLastTag=true final`. +This signals Nebula plugin to build `X.Y.0` version and to publish it to [Bintray repository](https://bintray.com/open-telemetry/maven/opentelemetry-java-instrumentation). ## Patch releases Whenever a fix is needed to any older branch, a PR should be made into the corresponding maintenance branch. When that PR is merge, CI will notice the new commit into maintenance branch and will initiate a new build for this. That build, after usual building and checking, will run `./gradlew -Prelease.scope=patch final`. -This will signal Nebula plugin to build a new version `vX.Y.(Z+1)` and publish it to Bintray repo. +This will signal Nebula plugin to build a new version `vX.Y.(Z+1)` and publish it to Bintray repo. diff --git a/build.gradle b/build.gradle index a28c40188f..e36ff70eb6 100644 --- a/build.gradle +++ b/build.gradle @@ -8,14 +8,11 @@ plugins { id 'org.unbroken-dome.test-sets' version '2.2.1' id 'com.github.ben-manes.versions' version '0.27.0' - // Not applying google java format by default because it gets confused by stray java build - // files in 'workspace' build directory in CI - id 'com.github.sherter.google-java-format' version '0.8' apply false id 'com.dorongold.task-tree' version '1.5' id "com.github.johnrengelman.shadow" version "5.2.0" - id "com.diffplug.gradle.spotless" version "3.28.1" + id "com.diffplug.gradle.spotless" version "4.3.0" id "com.github.spotbugs" version "4.0.1" } @@ -67,3 +64,19 @@ task writeMuzzleTasksToFile { .join('\n') } } + +apply plugin: 'com.diffplug.gradle.spotless' + +spotless { + // this formatting is applied at the root level, as some of these files are not in a submodules + // and would be missed otherwise + format 'misc', { + target '**/.gitignore', '**/*.md', '**/*.sh' + indentWithSpaces() + trimTrailingWhitespace() + endWithNewline() + } +} + +task formatCode(dependsOn: ['spotlessApply']) +check.dependsOn 'spotlessCheck' diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 353ea0a28b..0c8175b7be 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -1,6 +1,7 @@ plugins { groovy `java-gradle-plugin` + id("com.diffplug.gradle.spotless") version "4.3.0" } gradlePlugin { diff --git a/buildscripts/pre-commit b/buildscripts/pre-commit deleted file mode 100755 index 0b7b81378a..0000000000 --- a/buildscripts/pre-commit +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env sh - -./gradlew googleJavaFormat \ No newline at end of file diff --git a/gradle/checkstyle.gradle b/gradle/checkstyle.gradle index 7096ccd954..654a12be1d 100644 --- a/gradle/checkstyle.gradle +++ b/gradle/checkstyle.gradle @@ -34,15 +34,3 @@ check.dependsOn checkstyleTasks tasks.withType(Test).configureEach { mustRunAfter checkstyleTasks } - -// Verification seems broken on Java 9. -apply plugin: 'com.github.sherter.google-java-format' - -googleJavaFormat { - source = sourceSets*.allJava - exclude '**/build/**/*.java' -} - -tasks.withType(Checkstyle).configureEach { - mustRunAfter verifyGoogleJavaFormat -} diff --git a/gradle/enforcement/spotless-groovy.properties b/gradle/enforcement/spotless-groovy.properties new file mode 100644 index 0000000000..c1d6b757d6 --- /dev/null +++ b/gradle/enforcement/spotless-groovy.properties @@ -0,0 +1,12 @@ +# Disable formatting errors +ignoreFormatterProblems=true +org.eclipse.jdt.core.formatter.tabulation.char=space +org.eclipse.jdt.core.formatter.tabulation.size=2 +org.eclipse.jdt.core.formatter.indentation.size=1 +org.eclipse.jdt.core.formatter.indentation.text_block_indentation=indent by one +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.continuation_indentation=1 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=1 +groovy.formatter.longListLength=50 +groovy.formatter.multiline.indentation=1 +groovy.formatter.remove.unnecessary.semicolons=true diff --git a/gradle/spotless.gradle b/gradle/spotless.gradle index 518251f8c6..d034ceb964 100644 --- a/gradle/spotless.gradle +++ b/gradle/spotless.gradle @@ -2,6 +2,7 @@ apply plugin: 'com.diffplug.gradle.spotless' spotless { java { + googleJavaFormat() licenseHeaderFile rootProject.file('gradle/enforcement/spotless.license.java'), '(package|import|public)' target 'src/**/*.java' } @@ -9,9 +10,15 @@ spotless { licenseHeaderFile rootProject.file('gradle/enforcement/spotless.license.java'), '(package|import|class)' } scala { + scalafmt() licenseHeaderFile rootProject.file('gradle/enforcement/spotless.license.java'), '(package|import|public)' } kotlin { + // ktfmt() // only supports 4 space indentation + ktlint().userData(['indent_size': '2', 'continuation_indent_size': '2']) licenseHeaderFile rootProject.file('gradle/enforcement/spotless.license.java'), '(package|import|public)' } } + +task formatCode(dependsOn: ['spotlessApply']) +check.dependsOn 'spotlessApply' diff --git a/instrumentation-core/spring/README.md b/instrumentation-core/spring/README.md index 1ed169d972..432545e44b 100644 --- a/instrumentation-core/spring/README.md +++ b/instrumentation-core/spring/README.md @@ -2,11 +2,11 @@ -This package streamlines the manual instrumentation process of OpenTelemetry for [Spring](https://spring.io/projects/spring-framework) and [Spring Boot](https://spring.io/projects/spring-boot) applications. It will enable you to add traces to requests and database calls with minimal changes to application code. This package will not fully automate your OpenTelemetry instrumentation, instead, it will provide you with better tools to instrument your own code. +This package streamlines the manual instrumentation process of OpenTelemetry for [Spring](https://spring.io/projects/spring-framework) and [Spring Boot](https://spring.io/projects/spring-boot) applications. It will enable you to add traces to requests and database calls with minimal changes to application code. This package will not fully automate your OpenTelemetry instrumentation, instead, it will provide you with better tools to instrument your own code. -The [first section](#manual-instrumentation-with-java-sdk) will walk you through span creation and propagation using the OpenTelemetry Java API and [Spring's RestTemplate Http Web Client](https://spring.io/guides/gs/consuming-rest/). This approach will use the "vanilla" OpenTelemetry API to make explicit tracing calls within an application's controller. +The [first section](#manual-instrumentation-with-java-sdk) will walk you through span creation and propagation using the OpenTelemetry Java API and [Spring's RestTemplate Http Web Client](https://spring.io/guides/gs/consuming-rest/). This approach will use the "vanilla" OpenTelemetry API to make explicit tracing calls within an application's controller. -The [second section](#manual-instrumentation-using-handlers-and-filters) will build on the first. It will walk you through implementing spring-web handler and filter interfaces to create traces with minimal changes to existing application code. Using the OpenTelemetry API, this approach involves copy and pasting files and a significant amount of manual configurations. +The [second section](#manual-instrumentation-using-handlers-and-filters) will build on the first. It will walk you through implementing spring-web handler and filter interfaces to create traces with minimal changes to existing application code. Using the OpenTelemetry API, this approach involves copy and pasting files and a significant amount of manual configurations. The third section will walk you through the annotations and configurations defined in the opentelemetry-instrumentation-spring package. This section will equip you with new tools to streamline the setup and instrumentation of OpenTelemetry on Spring and Spring Boot applications. With these tools you will be able to setup distributed tracing with little to no changes to existing configurations and easily customize traces with minor additions to application code. @@ -16,14 +16,14 @@ In this guide we will be using a running example. In section one and two, we wil ## Create two Spring Projects -Using the [spring project initializer](https://start.spring.io/), we will create two spring projects. Name one project `MainService` and the other `TimeService`. In this example `MainService` will be a client of `TimeService` and they will be dealing with time. Make sure to select maven, Spring Boot 2.3, Java, and add the spring-web dependency. After downloading the two projects include the OpenTelemetry dependencies and configuration listed below. +Using the [spring project initializer](https://start.spring.io/), we will create two spring projects. Name one project `MainService` and the other `TimeService`. In this example `MainService` will be a client of `TimeService` and they will be dealing with time. Make sure to select maven, Spring Boot 2.3, Java, and add the spring-web dependency. After downloading the two projects include the OpenTelemetry dependencies and configuration listed below. ## Setup for Manual Instrumentation -Add the dependencies below to enable OpenTelemetry in `MainService` and `TimeService`. The Jaeger and LoggingExporter packages are recommended for exporting traces but are not required. As of May 2020, Jaeger, Zipkin, OTLP, and Logging exporters are supported by opentelemetry-java. Feel free to use whatever exporter you are most comfortable with. +Add the dependencies below to enable OpenTelemetry in `MainService` and `TimeService`. The Jaeger and LoggingExporter packages are recommended for exporting traces but are not required. As of May 2020, Jaeger, Zipkin, OTLP, and Logging exporters are supported by opentelemetry-java. Feel free to use whatever exporter you are most comfortable with. ### Maven - + #### OpenTelemetry ```xml @@ -35,7 +35,7 @@ Add the dependencies below to enable OpenTelemetry in `MainService` and `TimeSer io.opentelemetry opentelemetry-sdk 0.5.0 - + io.grpc grpc-context @@ -73,7 +73,7 @@ Add the dependencies below to enable OpenTelemetry in `MainService` and `TimeSer ``` ### Gradle - + #### OpenTelemetry ```gradle compile "io.opentelemetry:opentelemetry-api:0.5.0" @@ -97,7 +97,7 @@ compile "io.grpc:grpc-netty:1.27.2" To enable tracing in your OpenTelemetry project configure a Tracer Bean. This bean will be auto wired to controllers to create and propagate spans. This can be seen in the `Tracer otelTracer()` method below. If you plan to use a trace exporter remember to also include it in this configuration class. In section 3 we will use an annotation to set up this configuration. -A sample OpenTelemetry configuration using LoggingExporter is shown below: +A sample OpenTelemetry configuration using LoggingExporter is shown below: ```java import org.springframework.context.annotation.Bean; @@ -114,21 +114,21 @@ import io.opentelemetry.exporters.logging.*; @Configuration public class OtelConfig { - private static final tracerName = "fooTracer"; + private static final tracerName = "fooTracer"; @Bean public Tracer otelTracer() throws Exception { final Tracer tracer = OpenTelemetry.getTracer(tracerName); SpanProcessor logProcessor = SimpleSpanProcessor.newBuilder(new LoggingSpanExporter()).build(); OpenTelemetrySdk.getTracerProvider().addSpanProcessor(logProcessor); - + return tracer; } } ``` -The file above configures an OpenTelemetry tracer and a span processor. The span processor builds a log exporter which will output spans to the console. Similarly, one could add another exporter, such as the `JaegerExporter`, to visualize traces on a different back-end. Similar to how the `LoggingExporter` is configured, a Jaeger configuration can be added to the `OtelConfig` class above. +The file above configures an OpenTelemetry tracer and a span processor. The span processor builds a log exporter which will output spans to the console. Similarly, one could add another exporter, such as the `JaegerExporter`, to visualize traces on a different back-end. Similar to how the `LoggingExporter` is configured, a Jaeger configuration can be added to the `OtelConfig` class above. Sample configuration for a Jaeger Exporter: @@ -141,11 +141,11 @@ SpanProcessor jaegerProcessor = SimpleSpanProcessor .build(); OpenTelemetrySdk.getTracerProvider().addSpanProcessor(jaegerProcessor); ``` - + ### Project Background Here we will create REST controllers for `MainService` and `TimeService`. -`MainService` will send a GET request to `TimeService` to retrieve the current time. After this request is resolved, `MainService` then will append a message to time and return a string to the client. +`MainService` will send a GET request to `TimeService` to retrieve the current time. After this request is resolved, `MainService` then will append a message to time and return a string to the client. ## Manual Instrumentation with Java SDK @@ -190,7 +190,7 @@ import HttpUtils; public class MainServiceController { private static int requestCount = 1; private static final String TIME_SERVICE_URL = "http://localhost:8081/time"; - + @Autowired private Tracer tracer; @@ -246,7 +246,7 @@ public class HttpUtils { headers.set(key, value); } }; - + @Autowired private Tracer tracer; @@ -314,7 +314,7 @@ public class TimeServiceController { @GetMapping public String time() { Span span = tracer.spanBuilder("time").startSpan(); - + try (Scope scope = tracer.withSpan(span)) { span.addEvent("TimeServiceController Entered"); span.setAttribute("what.am.i", "Tu es une legume"); @@ -328,17 +328,17 @@ public class TimeServiceController { ### Run MainService and TimeService -***To view your distributed traces ensure either LogExporter or Jaeger is configured in the OtelConfig.java file*** +***To view your distributed traces ensure either LogExporter or Jaeger is configured in the OtelConfig.java file*** To view traces on the Jaeger UI, deploy a Jaeger Exporter on localhost by running the command in terminal: -`docker run --rm -it --network=host jaegertracing/all-in-one` +`docker run --rm -it --network=host jaegertracing/all-in-one` After running Jaeger locally, navigate to the url below. Make sure to refresh the UI to view the exported traces from the two web services: `http://localhost:16686` -Run MainService and TimeService from command line or using an IDE. The end point of interest for MainService is `http://localhost:8080/message` and `http://localhost:8081/time` for TimeService. Entering `localhost:8080/message` in a browser should call MainService and then TimeService, creating a trace. +Run MainService and TimeService from command line or using an IDE. The end point of interest for MainService is `http://localhost:8080/message` and `http://localhost:8081/time` for TimeService. Entering `localhost:8080/message` in a browser should call MainService and then TimeService, creating a trace. ***Note: The default port for the Apache Tomcat is 8080. On localhost both MainService and TimeService services will attempt to run on this port raising an error. To avoid this add `server.port=8081` to the resources/application.properties file. Ensure the port specified corresponds to port referenced by MainServiceController.TIME_SERVICE_URL. *** @@ -346,13 +346,13 @@ Congrats, we just created a distributed service with OpenTelemetry! ## Manual Instrumentation using Handlers and Filters -In this section, we will implement the javax Servlet Filter interface to wrap all requests to MainService and TimeService controllers in a span. +In this section, we will implement the javax Servlet Filter interface to wrap all requests to MainService and TimeService controllers in a span. -We will also use the RestTemplate HTTP client to send requests from MainService to TimeService. To propagate the trace in this request we will also implement the ClientHttpRequestInterceptor interface. This implementation is only required for projects that send outbound requests. In this example it is only required for MainService. +We will also use the RestTemplate HTTP client to send requests from MainService to TimeService. To propagate the trace in this request we will also implement the ClientHttpRequestInterceptor interface. This implementation is only required for projects that send outbound requests. In this example it is only required for MainService. ### Set up MainService and TimeService -Using the earlier instructions [create two spring projects](#create-two-spring-projects) and add the required [dependencies and configurations](#setup-for-manual-instrumentation). +Using the earlier instructions [create two spring projects](#create-two-spring-projects) and add the required [dependencies and configurations](#setup-for-manual-instrumentation). ### Instrumentation of TimeService @@ -396,16 +396,16 @@ public class TimeServiceController { #### Create Controller Filter -Add the class below to wrap all requests to the TimeServiceController in a span. This class will call the preHandle method before the REST controller is entered and the postHandle method after a response is created. +Add the class below to wrap all requests to the TimeServiceController in a span. This class will call the preHandle method before the REST controller is entered and the postHandle method after a response is created. -The preHandle method starts a span for each request. This implementation is shown below: +The preHandle method starts a span for each request. This implementation is shown below: ```java @Component public class ControllerFilter implements Filter { private static final Logger LOG = Logger.getLogger(ControllerFilter.class.getName()); - + @Autowired Tracer tracer; @@ -420,7 +420,7 @@ public class ControllerFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { LOG.info("start doFilter"); - + HttpServletRequest req = (HttpServletRequest) request; Span currentSpan; try (Scope scope = tracer.withSpan(currentSpan)) { @@ -432,10 +432,10 @@ public class ControllerFilter implements Filter { } finally { currentSpan.end(); } - - LOG.info("end doFilter"); + + LOG.info("end doFilter"); } - + private Span createSpanWithParent(HttpServletRequest request, Context context) { return tracer.spanBuilder(request.getRequestURI()).setSpanKind(Span.Kind.SERVER).startSpan(); } @@ -478,7 +478,7 @@ public class MainServiceController { @Autowired private Tracer tracer; - + @Autowired private RestTemplate restTemplate; @@ -504,7 +504,7 @@ As seen in the setup of TimeService, implement the javax servlet filter interfac Next, we will configure the ClientHttpRequestInterceptor to intercept all client HTTP requests made using RestTemplate. -To propagate the span context from MainService to TimeService we must inject the trace parent and trace state into the outgoing request header. In section 1 this was done using the helper class HttpUtils. In this section, we will implement the ClientHttpRequestInterceptor interface and register this interceptor in our application. +To propagate the span context from MainService to TimeService we must inject the trace parent and trace state into the outgoing request header. In section 1 this was done using the helper class HttpUtils. In this section, we will implement the ClientHttpRequestInterceptor interface and register this interceptor in our application. Include the two classes below to your MainService project to add this functionality: @@ -544,10 +544,10 @@ public class RestTemplateInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { - + String spanName = request.getMethodValue() + " " + request.getURI().toString(); Span currentSpan = tracer.spanBuilder(spanName).setSpanKind(Span.Kind.CLIENT).startSpan(); - + try (Scope scope = tracer.withSpan(currentSpan)) { OpenTelemetry.getPropagators().getHttpTextFormat().inject(Context.current(), request, setter); ClientHttpResponse response = execution.execute(request, body); @@ -589,12 +589,12 @@ public class RestClientConfig { } ``` -### Create a distributed trace +### Create a distributed trace -By default Spring Boot runs a Tomcat server on port 8080. This tutorial assumes MainService runs on the default port (8080) and TimeService runs on port 8081. This is because we hard coded the TimeService end point in MainServiceController.TIME_SERVICE_URL. To run TimeServiceApplication on port 8081 include `server.port=8081` in the resources/application.properties file. +By default Spring Boot runs a Tomcat server on port 8080. This tutorial assumes MainService runs on the default port (8080) and TimeService runs on port 8081. This is because we hard coded the TimeService end point in MainServiceController.TIME_SERVICE_URL. To run TimeServiceApplication on port 8081 include `server.port=8081` in the resources/application.properties file. -Run both the MainService and TimeService projects in terminal or using an IDE (ex. Eclipse). The end point for MainService should be `http://localhost:8080/message` and `http://localhost:8081/time` for TimeService. Type both urls in a browser and ensure you receive a 200 response. +Run both the MainService and TimeService projects in terminal or using an IDE (ex. Eclipse). The end point for MainService should be `http://localhost:8080/message` and `http://localhost:8081/time` for TimeService. Type both urls in a browser and ensure you receive a 200 response. -To visualize this trace add a trace exporter to one or both of your applications. Instructions on how to setup LogExporter and Jaeger can be seen [above](#tracer-configuration). +To visualize this trace add a trace exporter to one or both of your applications. Instructions on how to setup LogExporter and Jaeger can be seen [above](#tracer-configuration). To create a sample trace enter `localhost:8080/message` in a browser. This trace should include a span for MainService and a span for TimeService. diff --git a/instrumentation/akka-http-10.0/src/test/scala/AkkaHttpTestAsyncWebServer.scala b/instrumentation/akka-http-10.0/src/test/scala/AkkaHttpTestAsyncWebServer.scala index 19981235ab..328e07dc34 100644 --- a/instrumentation/akka-http-10.0/src/test/scala/AkkaHttpTestAsyncWebServer.scala +++ b/instrumentation/akka-http-10.0/src/test/scala/AkkaHttpTestAsyncWebServer.scala @@ -34,20 +34,27 @@ object AkkaHttpTestAsyncWebServer { val asyncHandler: HttpRequest => Future[HttpResponse] = { case HttpRequest(GET, uri: Uri, _, _, _) => Future { - val endpoint = HttpServerTest.ServerEndpoint.forPath(uri.path.toString()) - HttpServerTest.controller(endpoint, new Closure[HttpResponse](()) { - def doCall(): HttpResponse = { - val resp = HttpResponse(status = endpoint.getStatus) //.withHeaders(headers.Type)resp.contentType = "text/plain" - endpoint match { - case SUCCESS => resp.withEntity(endpoint.getBody) - case QUERY_PARAM => resp.withEntity(uri.queryString().orNull) - case REDIRECT => resp.withHeaders(headers.Location(endpoint.getBody)) - case ERROR => resp.withEntity(endpoint.getBody) - case EXCEPTION => throw new Exception(endpoint.getBody) - case _ => HttpResponse(status = NOT_FOUND.getStatus).withEntity(NOT_FOUND.getBody) + val endpoint = + HttpServerTest.ServerEndpoint.forPath(uri.path.toString()) + HttpServerTest.controller( + endpoint, + new Closure[HttpResponse](()) { + def doCall(): HttpResponse = { + val resp = HttpResponse(status = endpoint.getStatus) //.withHeaders(headers.Type)resp.contentType = "text/plain" + endpoint match { + case SUCCESS => resp.withEntity(endpoint.getBody) + case QUERY_PARAM => resp.withEntity(uri.queryString().orNull) + case REDIRECT => + resp.withHeaders(headers.Location(endpoint.getBody)) + case ERROR => resp.withEntity(endpoint.getBody) + case EXCEPTION => throw new Exception(endpoint.getBody) + case _ => + HttpResponse(status = NOT_FOUND.getStatus) + .withEntity(NOT_FOUND.getBody) + } } } - }) + ) } } @@ -56,7 +63,10 @@ object AkkaHttpTestAsyncWebServer { def start(port: Int): Unit = synchronized { if (null == binding) { import scala.concurrent.duration._ - binding = Await.result(Http().bindAndHandleAsync(asyncHandler, "localhost", port), 10 seconds) + binding = Await.result( + Http().bindAndHandleAsync(asyncHandler, "localhost", port), + 10 seconds + ) } } diff --git a/instrumentation/akka-http-10.0/src/test/scala/AkkaHttpTestSyncWebServer.scala b/instrumentation/akka-http-10.0/src/test/scala/AkkaHttpTestSyncWebServer.scala index ca79c00c63..4df5c82875 100644 --- a/instrumentation/akka-http-10.0/src/test/scala/AkkaHttpTestSyncWebServer.scala +++ b/instrumentation/akka-http-10.0/src/test/scala/AkkaHttpTestSyncWebServer.scala @@ -34,19 +34,25 @@ object AkkaHttpTestSyncWebServer { val syncHandler: HttpRequest => HttpResponse = { case HttpRequest(GET, uri: Uri, _, _, _) => { val endpoint = HttpServerTest.ServerEndpoint.forPath(uri.path.toString()) - HttpServerTest.controller(endpoint, new Closure[HttpResponse](()) { - def doCall(): HttpResponse = { - val resp = HttpResponse(status = endpoint.getStatus) - endpoint match { - case SUCCESS => resp.withEntity(endpoint.getBody) - case QUERY_PARAM => resp.withEntity(uri.queryString().orNull) - case REDIRECT => resp.withHeaders(headers.Location(endpoint.getBody)) - case ERROR => resp.withEntity(endpoint.getBody) - case EXCEPTION => throw new Exception(endpoint.getBody) - case _ => HttpResponse(status = NOT_FOUND.getStatus).withEntity(NOT_FOUND.getBody) + HttpServerTest.controller( + endpoint, + new Closure[HttpResponse](()) { + def doCall(): HttpResponse = { + val resp = HttpResponse(status = endpoint.getStatus) + endpoint match { + case SUCCESS => resp.withEntity(endpoint.getBody) + case QUERY_PARAM => resp.withEntity(uri.queryString().orNull) + case REDIRECT => + resp.withHeaders(headers.Location(endpoint.getBody)) + case ERROR => resp.withEntity(endpoint.getBody) + case EXCEPTION => throw new Exception(endpoint.getBody) + case _ => + HttpResponse(status = NOT_FOUND.getStatus) + .withEntity(NOT_FOUND.getBody) + } } } - }) + ) } } @@ -55,7 +61,10 @@ object AkkaHttpTestSyncWebServer { def start(port: Int): Unit = synchronized { if (null == binding) { import scala.concurrent.duration._ - binding = Await.result(Http().bindAndHandleSync(syncHandler, "localhost", port), 10 seconds) + binding = Await.result( + Http().bindAndHandleSync(syncHandler, "localhost", port), + 10 seconds + ) } } diff --git a/instrumentation/akka-http-10.0/src/test/scala/AkkaHttpTestWebServer.scala b/instrumentation/akka-http-10.0/src/test/scala/AkkaHttpTestWebServer.scala index 4a7a799c89..c1eff3a4f2 100644 --- a/instrumentation/akka-http-10.0/src/test/scala/AkkaHttpTestWebServer.scala +++ b/instrumentation/akka-http-10.0/src/test/scala/AkkaHttpTestWebServer.scala @@ -33,14 +33,21 @@ object AkkaHttpTestWebServer { implicit val executionContext = system.dispatcher val exceptionHandler = ExceptionHandler { - case ex: Exception => complete(HttpResponse(status = EXCEPTION.getStatus).withEntity(ex.getMessage)) + case ex: Exception => + complete( + HttpResponse(status = EXCEPTION.getStatus).withEntity(ex.getMessage) + ) } val route = { //handleExceptions(exceptionHandler) { path(SUCCESS.rawPath) { - complete(HttpResponse(status = SUCCESS.getStatus).withEntity(SUCCESS.getBody)) + complete( + HttpResponse(status = SUCCESS.getStatus).withEntity(SUCCESS.getBody) + ) } ~ path(QUERY_PARAM.rawPath) { - complete(HttpResponse(status = QUERY_PARAM.getStatus).withEntity(SUCCESS.getBody)) + complete( + HttpResponse(status = QUERY_PARAM.getStatus).withEntity(SUCCESS.getBody) + ) } ~ path(REDIRECT.rawPath) { redirect(Uri(REDIRECT.getBody), StatusCodes.Found) } ~ path(ERROR.rawPath) { @@ -55,7 +62,8 @@ object AkkaHttpTestWebServer { def start(port: Int): Unit = synchronized { if (null == binding) { import scala.concurrent.duration._ - binding = Await.result(Http().bindAndHandle(route, "localhost", port), 10 seconds) + binding = + Await.result(Http().bindAndHandle(route, "localhost", port), 10 seconds) } } diff --git a/instrumentation/finatra-2.9/src/test/scala/ResponseSettingExceptionMapper.scala b/instrumentation/finatra-2.9/src/test/scala/ResponseSettingExceptionMapper.scala index 6c9636454c..b14b262fc1 100644 --- a/instrumentation/finatra-2.9/src/test/scala/ResponseSettingExceptionMapper.scala +++ b/instrumentation/finatra-2.9/src/test/scala/ResponseSettingExceptionMapper.scala @@ -21,7 +21,7 @@ import javax.inject.{Inject, Singleton} @Singleton class ResponseSettingExceptionMapper @Inject()(response: ResponseBuilder) - extends ExceptionMapper[Exception] { + extends ExceptionMapper[Exception] { override def toResponse(request: Request, exception: Exception): Response = { response.internalServerError(exception.getMessage) diff --git a/instrumentation/java-concurrent/akka-testing/src/test/scala/AkkaActors.scala b/instrumentation/java-concurrent/akka-testing/src/test/scala/AkkaActors.scala index b84387c58d..addf7ba418 100644 --- a/instrumentation/java-concurrent/akka-testing/src/test/scala/AkkaActors.scala +++ b/instrumentation/java-concurrent/akka-testing/src/test/scala/AkkaActors.scala @@ -24,7 +24,8 @@ import scala.concurrent.duration._ // ! == send-message object AkkaActors { - val TRACER: Tracer = OpenTelemetry.getTracerProvider.get("io.opentelemetry.auto") + val TRACER: Tracer = + OpenTelemetry.getTracerProvider.get("io.opentelemetry.auto") val system: ActorSystem = ActorSystem("helloAkka") @@ -33,8 +34,10 @@ object AkkaActors { val howdyGreeter: ActorRef = system.actorOf(Greeter.props("Howdy", printer), "howdyGreeter") - val forwarder: ActorRef = system.actorOf(Forwarder.props(printer), "forwarderActor") - val helloGreeter: ActorRef = system.actorOf(Greeter.props("Hello", forwarder), "helloGreeter") + val forwarder: ActorRef = + system.actorOf(Forwarder.props(printer), "forwarderActor") + val helloGreeter: ActorRef = + system.actorOf(Greeter.props("Hello", forwarder), "helloGreeter") def tracedChild(opName: String): Unit = { TRACER.spanBuilder(opName).startSpan().end() @@ -86,7 +89,8 @@ class AkkaActors { } object Greeter { - def props(message: String, receiverActor: ActorRef): Props = Props(new Greeter(message, receiverActor)) + def props(message: String, receiverActor: ActorRef): Props = + Props(new Greeter(message, receiverActor)) final case class WhoToGreet(who: String) @@ -129,7 +133,8 @@ class Receiver extends Actor with ActorLogging { } object Forwarder { - def props(receiverActor: ActorRef): Props = Props(new Forwarder(receiverActor)) + def props(receiverActor: ActorRef): Props = + Props(new Forwarder(receiverActor)) } class Forwarder(receiverActor: ActorRef) extends Actor with ActorLogging { diff --git a/instrumentation/java-concurrent/kotlin-testing/src/test/kotlin/KotlinCoroutineTests.kt b/instrumentation/java-concurrent/kotlin-testing/src/test/kotlin/KotlinCoroutineTests.kt index 7356ed342d..6f62b0b792 100644 --- a/instrumentation/java-concurrent/kotlin-testing/src/test/kotlin/KotlinCoroutineTests.kt +++ b/instrumentation/java-concurrent/kotlin-testing/src/test/kotlin/KotlinCoroutineTests.kt @@ -17,13 +17,22 @@ import io.opentelemetry.OpenTelemetry import io.opentelemetry.trace.Tracer import io.opentelemetry.trace.TracingContextUtils.currentContextWith -import kotlinx.coroutines.* +import java.util.concurrent.TimeUnit +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll import kotlinx.coroutines.channels.actor import kotlinx.coroutines.channels.consumeEach import kotlinx.coroutines.channels.produce import kotlinx.coroutines.channels.toChannel +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.selects.select -import java.util.concurrent.TimeUnit +import kotlinx.coroutines.withTimeout +import kotlinx.coroutines.yield class KotlinCoroutineTests(private val dispatcher: CoroutineDispatcher) { val tracer: Tracer = OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto") @@ -144,4 +153,3 @@ class KotlinCoroutineTests(private val dispatcher: CoroutineDispatcher) { } } } - diff --git a/instrumentation/java-concurrent/scala-testing/src/test/scala/ScalaConcurrentTests.scala b/instrumentation/java-concurrent/scala-testing/src/test/scala/ScalaConcurrentTests.scala index 6dca893fd1..ad24a7a41f 100644 --- a/instrumentation/java-concurrent/scala-testing/src/test/scala/ScalaConcurrentTests.scala +++ b/instrumentation/java-concurrent/scala-testing/src/test/scala/ScalaConcurrentTests.scala @@ -24,11 +24,12 @@ import scala.concurrent.duration._ import scala.concurrent.{Await, Future, Promise} class ScalaConcurrentTests { - val TRACER: Tracer = OpenTelemetry.getTracerProvider.get("io.opentelemetry.auto") + val TRACER: Tracer = + OpenTelemetry.getTracerProvider.get("io.opentelemetry.auto") /** - * @return Number of expected spans in the trace - */ + * @return Number of expected spans in the trace + */ def traceWithFutureAndCallbacks() { val parentSpan = TRACER.spanBuilder("parent").startSpan() val parentScope = TRACER.withSpan(parentSpan) @@ -71,14 +72,15 @@ class ScalaConcurrentTests { 1 } goodFuture onSuccess { - case _ => Future { - 2 - } onSuccess { - case _ => { - tracedChild("callback") - latch.countDown() + case _ => + Future { + 2 + } onSuccess { + case _ => { + tracedChild("callback") + latch.countDown() + } } - } } latch.await() @@ -89,8 +91,8 @@ class ScalaConcurrentTests { } /** - * @return Number of expected spans in the trace - */ + * @return Number of expected spans in the trace + */ def traceWithPromises() { val parentSpan = TRACER.spanBuilder("parent").startSpan() val parentScope = TRACER.withSpan(parentSpan) @@ -137,26 +139,22 @@ class ScalaConcurrentTests { } /** - * @return Number of expected spans in the trace - */ + * @return Number of expected spans in the trace + */ def tracedWithFutureFirstCompletions() { val parentSpan = TRACER.spanBuilder("parent").startSpan() val parentScope = TRACER.withSpan(parentSpan) try { - val completedVal = Future.firstCompletedOf( - List( - Future { - tracedChild("timeout1") - false - }, - Future { - tracedChild("timeout2") - false - }, - Future { - tracedChild("timeout3") - true - })) + val completedVal = Future.firstCompletedOf(List(Future { + tracedChild("timeout1") + false + }, Future { + tracedChild("timeout2") + false + }, Future { + tracedChild("timeout3") + true + })) Await.result(completedVal, 30 seconds) } finally { parentSpan.end() @@ -165,8 +163,8 @@ class ScalaConcurrentTests { } /** - * @return Number of expected spans in the trace - */ + * @return Number of expected spans in the trace + */ def tracedTimeout(): Integer = { val parentSpan = TRACER.spanBuilder("parent").startSpan() val parentScope = TRACER.withSpan(parentSpan) diff --git a/instrumentation/java-concurrent/src/slickTest/scala/SlickUtils.scala b/instrumentation/java-concurrent/src/slickTest/scala/SlickUtils.scala index 57e3a6481e..6233a06999 100644 --- a/instrumentation/java-concurrent/src/slickTest/scala/SlickUtils.scala +++ b/instrumentation/java-concurrent/src/slickTest/scala/SlickUtils.scala @@ -22,11 +22,13 @@ import scala.concurrent.duration.Duration import scala.concurrent.{Await, Future} class SlickUtils { - val TRACER: Tracer = OpenTelemetry.getTracerProvider.get("io.opentelemetry.auto") + val TRACER: Tracer = + OpenTelemetry.getTracerProvider.get("io.opentelemetry.auto") import SlickUtils._ - val database = Database.forURL(Url, + val database = Database.forURL( + Url, user = Username, driver = "org.h2.Driver", keepAliveConnection = true, @@ -34,7 +36,12 @@ class SlickUtils { // wrapped runnables. executor = AsyncExecutor("test", numThreads = 1, queueSize = 1000) ) - Await.result(database.run(sqlu"""CREATE ALIAS IF NOT EXISTS SLEEP FOR "java.lang.Thread.sleep(long)""""), Duration.Inf) + Await.result( + database.run( + sqlu"""CREATE ALIAS IF NOT EXISTS SLEEP FOR "java.lang.Thread.sleep(long)"""" + ), + Duration.Inf + ) def startQuery(query: String): Future[Vector[Int]] = { val span = TRACER.spanBuilder("run query").startSpan() diff --git a/instrumentation/jdbc/jdbc.gradle b/instrumentation/jdbc/jdbc.gradle index ad6b4d021d..e5cb6b4f10 100644 --- a/instrumentation/jdbc/jdbc.gradle +++ b/instrumentation/jdbc/jdbc.gradle @@ -1,5 +1,5 @@ plugins { - id 'com.intershop.gradle.javacc' version '4.0.0' + id 'com.intershop.gradle.javacc' version '4.0.0' } apply from: "$rootDir/gradle/instrumentation.gradle" @@ -21,9 +21,6 @@ javacc { } } -tasks.withType(com.github.sherter.googlejavaformatgradleplugin.VerifyGoogleJavaFormat).configureEach { - exclude '**/jdbc/normalizer/*.java' -} tasks.withType(Checkstyle).configureEach { exclude '**/jdbc/normalizer/*.java' } @@ -36,7 +33,8 @@ testSets { dependencies { // jdbc unit testing - testCompile group: 'com.h2database', name: 'h2', version: '1.3.169' // first version jdk 1.6 compatible + testCompile group: 'com.h2database', name: 'h2', version: '1.3.169' + // first version jdk 1.6 compatible testCompile group: 'org.apache.derby', name: 'derby', version: '10.6.1.0' testCompile group: 'org.hsqldb', name: 'hsqldb', version: '2.0.0' diff --git a/instrumentation/play/play-2.3/src/test/scala/server/AsyncServer.scala b/instrumentation/play/play-2.3/src/test/scala/server/AsyncServer.scala index a14c160e82..0b459a7f38 100644 --- a/instrumentation/play/play-2.3/src/test/scala/server/AsyncServer.scala +++ b/instrumentation/play/play-2.3/src/test/scala/server/AsyncServer.scala @@ -25,18 +25,64 @@ import scala.concurrent.Future object AsyncServer { val routes: PartialFunction[(String, String), Handler] = { - case ("GET", "/success") => Action.async { request => HttpServerTest.controller(SUCCESS, new AsyncControllerClosureAdapter(Future.successful(Results.Status(SUCCESS.getStatus).apply(SUCCESS.getBody)))) } - case ("GET", "/redirect") => Action.async { request => HttpServerTest.controller(REDIRECT, new AsyncControllerClosureAdapter(Future.successful(Results.Redirect(REDIRECT.getBody, REDIRECT.getStatus)))) } - case ("GET", "/query") => Action.async { result => HttpServerTest.controller(QUERY_PARAM, new AsyncControllerClosureAdapter(Future.successful(Results.Status(QUERY_PARAM.getStatus).apply(QUERY_PARAM.getBody)))) } - case ("GET", "/error-status") => Action.async { result => HttpServerTest.controller(ERROR, new AsyncControllerClosureAdapter(Future.successful(Results.Status(ERROR.getStatus).apply(ERROR.getBody)))) } - case ("GET", "/exception") => Action.async { result => - HttpServerTest.controller(EXCEPTION, new AsyncBlockClosureAdapter(() => { - throw new Exception(EXCEPTION.getBody) - })) - } + case ("GET", "/success") => + Action.async { request => + HttpServerTest.controller( + SUCCESS, + new AsyncControllerClosureAdapter( + Future.successful( + Results.Status(SUCCESS.getStatus).apply(SUCCESS.getBody) + ) + ) + ) + } + case ("GET", "/redirect") => + Action.async { request => + HttpServerTest.controller( + REDIRECT, + new AsyncControllerClosureAdapter( + Future.successful( + Results.Redirect(REDIRECT.getBody, REDIRECT.getStatus) + ) + ) + ) + } + case ("GET", "/query") => + Action.async { result => + HttpServerTest.controller( + QUERY_PARAM, + new AsyncControllerClosureAdapter( + Future.successful( + Results.Status(QUERY_PARAM.getStatus).apply(QUERY_PARAM.getBody) + ) + ) + ) + } + case ("GET", "/error-status") => + Action.async { result => + HttpServerTest.controller( + ERROR, + new AsyncControllerClosureAdapter( + Future + .successful(Results.Status(ERROR.getStatus).apply(ERROR.getBody)) + ) + ) + } + case ("GET", "/exception") => + Action.async { result => + HttpServerTest.controller( + EXCEPTION, + new AsyncBlockClosureAdapter(() => { + throw new Exception(EXCEPTION.getBody) + }) + ) + } } def server(port: Int): TestServer = { - TestServer(port, FakeApplication(withGlobal = Some(new Settings()), withRoutes = routes)) + TestServer( + port, + FakeApplication(withGlobal = Some(new Settings()), withRoutes = routes) + ) } } diff --git a/instrumentation/play/play-2.3/src/test/scala/server/ControllerClosureAdapter.scala b/instrumentation/play/play-2.3/src/test/scala/server/ControllerClosureAdapter.scala index e2dbcd4faf..1e84b4748f 100644 --- a/instrumentation/play/play-2.3/src/test/scala/server/ControllerClosureAdapter.scala +++ b/instrumentation/play/play-2.3/src/test/scala/server/ControllerClosureAdapter.scala @@ -29,10 +29,12 @@ class BlockClosureAdapter(block: () => Result) extends Closure[Result] { override def call(): Result = block() } -class AsyncControllerClosureAdapter(response: Future[Result]) extends Closure[Future[Result]] { +class AsyncControllerClosureAdapter(response: Future[Result]) + extends Closure[Future[Result]] { override def call(): Future[Result] = response } -class AsyncBlockClosureAdapter(block: () => Future[Result]) extends Closure[Future[Result]] { +class AsyncBlockClosureAdapter(block: () => Future[Result]) + extends Closure[Future[Result]] { override def call(): Future[Result] = block() } diff --git a/instrumentation/play/play-2.3/src/test/scala/server/Settings.scala b/instrumentation/play/play-2.3/src/test/scala/server/Settings.scala index d79b26c316..56fca48c2b 100644 --- a/instrumentation/play/play-2.3/src/test/scala/server/Settings.scala +++ b/instrumentation/play/play-2.3/src/test/scala/server/Settings.scala @@ -22,7 +22,10 @@ import play.api.mvc.{RequestHeader, Result, Results} import scala.concurrent.Future class Settings extends GlobalSettings { - override def onError(request: RequestHeader, ex: Throwable): Future[Result] = { + override def onError( + request: RequestHeader, + ex: Throwable + ): Future[Result] = { Future.successful(Results.InternalServerError(ex.getCause.getMessage)) } } diff --git a/instrumentation/play/play-2.3/src/test/scala/server/SyncServer.scala b/instrumentation/play/play-2.3/src/test/scala/server/SyncServer.scala index 366365367b..5cf50a739b 100644 --- a/instrumentation/play/play-2.3/src/test/scala/server/SyncServer.scala +++ b/instrumentation/play/play-2.3/src/test/scala/server/SyncServer.scala @@ -23,26 +23,54 @@ import play.api.test.{FakeApplication, TestServer} object SyncServer { val routes: PartialFunction[(String, String), Handler] = { - case ("GET", "/success") => Action { request => - HttpServerTest.controller(SUCCESS, new ControllerClosureAdapter(Results.Status(SUCCESS.getStatus).apply(SUCCESS.getBody))) - } - case ("GET", "/redirect") => Action { request => - HttpServerTest.controller(REDIRECT, new ControllerClosureAdapter(Results.Redirect(REDIRECT.getBody, REDIRECT.getStatus))) - } - case ("GET", "/query") => Action { request => - HttpServerTest.controller(QUERY_PARAM, new ControllerClosureAdapter(Results.Status(QUERY_PARAM.getStatus).apply(QUERY_PARAM.getBody))) - } - case ("GET", "/error-status") => Action { request => - HttpServerTest.controller(ERROR, new ControllerClosureAdapter(Results.Status(ERROR.getStatus).apply(ERROR.getBody))) - } - case ("GET", "/exception") => Action { request => - HttpServerTest.controller(EXCEPTION, new BlockClosureAdapter(() => { - throw new Exception(EXCEPTION.getBody) - })) - } + case ("GET", "/success") => + Action { request => + HttpServerTest.controller( + SUCCESS, + new ControllerClosureAdapter( + Results.Status(SUCCESS.getStatus).apply(SUCCESS.getBody) + ) + ) + } + case ("GET", "/redirect") => + Action { request => + HttpServerTest.controller( + REDIRECT, + new ControllerClosureAdapter( + Results.Redirect(REDIRECT.getBody, REDIRECT.getStatus) + ) + ) + } + case ("GET", "/query") => + Action { request => + HttpServerTest.controller( + QUERY_PARAM, + new ControllerClosureAdapter( + Results.Status(QUERY_PARAM.getStatus).apply(QUERY_PARAM.getBody) + ) + ) + } + case ("GET", "/error-status") => + Action { request => + HttpServerTest.controller( + ERROR, + new ControllerClosureAdapter( + Results.Status(ERROR.getStatus).apply(ERROR.getBody) + ) + ) + } + case ("GET", "/exception") => + Action { request => + HttpServerTest.controller(EXCEPTION, new BlockClosureAdapter(() => { + throw new Exception(EXCEPTION.getBody) + })) + } } def server(port: Int): TestServer = { - TestServer(port, FakeApplication(withGlobal = Some(new Settings()), withRoutes = routes)) + TestServer( + port, + FakeApplication(withGlobal = Some(new Settings()), withRoutes = routes) + ) } } diff --git a/instrumentation/servlet/README.md b/instrumentation/servlet/README.md index 9c8f517044..4ef1424aa3 100644 --- a/instrumentation/servlet/README.md +++ b/instrumentation/servlet/README.md @@ -30,7 +30,7 @@ In the example above this is `ApplicationFilterChain.doFilter(ServletRequest, Se Let us call this first servlet specific method an "entry point". This is the main target for `Servlet3Instrumentation` and `Servlet2Instrumentation` instrumenters: -`public void javax.servlet.FilterChain#doFilter(ServletRequest, ServletResponse)` +`public void javax.servlet.FilterChain#doFilter(ServletRequest, ServletResponse)` `public void javax.servlet.http.HttpServlet#service(ServletRequest, ServletResponse)`. @@ -39,7 +39,7 @@ the second method as instrumentation entry point. These instrumentations are located in two separate submodules `request-3.0` and `request-2.3`, respectively, because they and corresponding tests depend on different versions of servlet specification. -Next, request passes several other methods from Servlet specification, such as +Next, request passes several other methods from Servlet specification, such as `protected void javax.servlet.http.HttpServlet#service(HttpServletRequest, HttpServletResponse)` or @@ -70,5 +70,5 @@ Becaue their root spans will all have the same named based on common entry point In order to alleviate this problem, instrumentations for specific frameworks, such as Spring MVC here, _update_ name of the span corresponding to the entry point. Each framework instrumentation can decide what is the best span name based on framework implementation details. -Of course, still adhering to OpenTelemetry -[semantic conventions](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md). \ No newline at end of file +Of course, still adhering to OpenTelemetry +[semantic conventions](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md).