Skip to content

Commit

Permalink
Adds Micrometer Tracing Example (#110)
Browse files Browse the repository at this point in the history
Signed-off-by: Adrian Cole <[email protected]>
  • Loading branch information
codefromthecrypt authored Feb 6, 2024
1 parent f2ec882 commit d14a9b4
Show file tree
Hide file tree
Showing 14 changed files with 332 additions and 3 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
!jersey2-cassandra3/src/main/**
!netty4-grpc/src/main/**
!webflux5-sleuth/src/main/**
!webflux6-micrometer/src/main/**
!webmvc3-jetty/src/main/**
!webmvc4-boot/src/main/**
!webmvc4-jetty/src/main/**
Expand Down
46 changes: 46 additions & 0 deletions .github/workflows/deploy-webflux6-micrometer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# yamllint --format github .github/workflows/deploy.yml
---
name: deploy webflux6-micrometer

on:
# We deploy non-tagged pushes to master relevant for this project. We can't opt out of
# documentation-only commits because GH actions does not permit paths and paths-ignore.
push:
tags: ''
branches: master
paths:
- "build-bin/**"
- "docker/**"
- "webflux6-micrometer/**"
- ".github/workflows/deploy-webflux6-micrometer.yaml"
- "parent-pom.xml"

jobs:
deploy:
runs-on: ubuntu-22.04 # newest available distribution, aka jellyfish
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Cache local Maven repository
uses: actions/cache@v3
with:
path: ./m2repository # Shared with the Docker build context via .dockerignore
key: ${{ runner.os }}-webflux6-micrometer-maven-${{ hashFiles('parent-pom.xml', 'webflux6-micrometer/pom.xml') }}
restore-keys: ${{ runner.os }}-webflux6-micrometer-maven-
# Don't attempt to cache Docker. Sensitive information can be stolen
# via forks, and login session ends up in ~/.docker. This is ok because
# we publish DOCKER_PARENT_IMAGE to ghcr.io, hence local to the runner.
- name: Deploy webflux6-micrometer
env:
# GH_USER=<user that created GH_TOKEN>
GH_USER: ${{ secrets.GH_USER }}
# GH_TOKEN=<hex token value>
# - pushes Docker images to ghcr.io
# - create via https://github.com/settings/tokens
# - needs repo:status, public_repo, write:packages, delete:packages
GH_TOKEN: ${{ secrets.GH_TOKEN }}
run: |
build-bin/configure_deploy webflux6-micrometer &&
build-bin/deploy webflux6-micrometer
37 changes: 37 additions & 0 deletions .github/workflows/test-webflux6-micrometer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# yamllint --format github .github/workflows/test.yml
---
name: test webflux6-micrometer

on:
# We deploy non-tagged pushes to master relevant for this project. We can't opt out of
# documentation-only commits because GH actions does not permit paths and paths-ignore.
pull_request:
branches: master
paths:
- "build-bin/**"
- "docker/**"
- "webflux6-micrometer/**"
- ".github/workflows/test-webflux6-micrometer.yaml"
- "parent-pom.xml"

jobs:
test:
runs-on: ubuntu-22.04 # newest available distribution, aka jellyfish
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Cache local Maven repository
uses: actions/cache@v3
with:
path: ./m2repository # Shared with the Docker build context via .dockerignore
key: ${{ runner.os }}-webflux6-micrometer-maven-${{ hashFiles('parent-pom.xml', 'webflux6-micrometer/pom.xml') }}
restore-keys: ${{ runner.os }}-webflux6-micrometer-maven-
# Don't attempt to cache Docker. Sensitive information can be stolen
# via forks, and login session ends up in ~/.docker. This is ok because
# we publish DOCKER_PARENT_IMAGE to ghcr.io, hence local to the runner.
- name: Test webflux6-micrometer
run: |
build-bin/configure_test webflux6-micrometer &&
build-bin/test webflux6-micrometer
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ Here are the example projects you can try:
* Trace Instrumentation: [WebFlux Server](https://github.com/spring-cloud/spring-cloud-sleuth/blob/2.2.x/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebFilter.java), [WebFlux Client](https://github.com/spring-cloud/spring-cloud-sleuth/blob/2.2.x/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientBeanPostProcessor.java), [Reactor Context](https://github.com/spring-cloud/spring-cloud-sleuth/blob/2.2.x/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/reactor/ScopePassingSpanSubscriber.java), [SLF4J](https://github.com/openzipkin/brave/tree/master/context/slf4j)
* Trace Configuration: [Spring Cloud Sleuth](https://github.com/spring-cloud/spring-cloud-sleuth/tree/2.2.x/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig) [Properties](webflux5-sleuth/src/main/resources/application.properties)

* [webflux6-micrometer](webflux6-micrometer) `BRAVE_EXAMPLE=webflux6-micrometer docker-compose up`
* Runtime: Spring 6, Reactor Netty, Spring Boot 3, Micrometer, Log4J 2, JRE 21
* Trace Configuration: [Spring Boot Actuator](https://github.com/spring-projects/spring-boot/blob/3.2.x/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/TracingProperties.java)

* [webmvc25-jetty](webmvc25-jetty) `BRAVE_EXAMPLE=webmvc25-jetty docker-compose up`
* Runtime: Spring 2.5, Apache HttpClient 4.3, Servlet 2.5, Jetty 7.6, Log4J 1.2, JRE 6
* Trace Instrumentation: [Servlet](https://github.com/openzipkin/brave/tree/master/instrumentation/servlet), [Spring MVC](https://github.com/openzipkin/brave/tree/master/instrumentation/spring-webmvc), [Apache HttpClient](https://github.com/openzipkin/brave/tree/master/instrumentation/httpclient), [Log4J 1.2](https://github.com/openzipkin/brave/tree/master/context/log4j12)
Expand Down
8 changes: 8 additions & 0 deletions build-bin/docker/docker_args
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ if [ -n "${DOCKER_TARGET}" ]; then
docker_args="${docker_args} --target ${DOCKER_TARGET}"
fi

# When non-empty, becomes the layer that builds the maven projects.
# e.g. ghcr.io/openzipkin/java:11.0.22_p7
#
# This must include maven and a full JDK.
if [ -n "${DOCKER_BUILD_IMAGE}" ]; then
docker_args="${docker_args} --build-arg docker_build_image=${DOCKER_BUILD_IMAGE}"
fi

# When non-empty, becomes the base layer including tag appropriate for the image being built.
# e.g. ghcr.io/openzipkin/java:21.0.2_p13-jre
#
Expand Down
8 changes: 7 additions & 1 deletion build-bin/docker_args
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,36 @@ DOCKER_ARCHS="amd64 arm64"

case "${JRE_VERSION}" in
6 )
DOCKER_BUILD_IMAGE=ghcr.io/openzipkin/java:11.0.22_p7
DOCKER_PARENT_IMAGE=ghcr.io/openzipkin/java:1.6.0-119
# single arch image
DOCKER_ARCHS=amd64
;;
7 )
DOCKER_BUILD_IMAGE=ghcr.io/openzipkin/java:11.0.22_p7
DOCKER_PARENT_IMAGE=ghcr.io/openzipkin/java:1.7.0_285
# single arch image
DOCKER_ARCHS=amd64
;;
8 )
DOCKER_BUILD_IMAGE=ghcr.io/openzipkin/java:11.0.22_p7
DOCKER_PARENT_IMAGE=ghcr.io/openzipkin/java:8.392.08-jre
;;
11 )
DOCKER_BUILD_IMAGE=ghcr.io/openzipkin/java:11.0.22_p7
DOCKER_PARENT_IMAGE=ghcr.io/openzipkin/java:11.0.22_p7-jre
;;
17 )
DOCKER_BUILD_IMAGE=ghcr.io/openzipkin/java:21.0.2_p13
DOCKER_PARENT_IMAGE=ghcr.io/openzipkin/java:17.0.10_p7-jre
;;
21 )
DOCKER_BUILD_IMAGE=ghcr.io/openzipkin/java:21.0.2_p13
DOCKER_PARENT_IMAGE=ghcr.io/openzipkin/java:21.0.2_p13-jre
;;
* )
echo "Invalid JRE_VERSION: ${JRE_VERSION}"
exit 1
esac

export DOCKER_PARENT_IMAGE DOCKER_ARCHS
export DOCKER_BUILD_IMAGE DOCKER_PARENT_IMAGE DOCKER_ARCHS
5 changes: 3 additions & 2 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

# The image binaries this example builds are installed over
ARG docker_parent_image=ghcr.io/openzipkin/java:21.0.2_p13
## Use JDK 11 to build projects, as that can still compile Java 6
ARG docker_build_image=ghcr.io/openzipkin/java:11.0.22_p7

# We copy files from the context into a scratch container first to avoid a problem where docker and
# docker-compose don't share layer hashes https://github.com/docker/compose/issues/883 normally.
Expand All @@ -11,8 +13,7 @@ FROM scratch as scratch

COPY . /code/

## Use JDK 11 to build projects, as that can still compile Java 6
FROM ghcr.io/openzipkin/java:11.0.22_p7 as install
FROM $docker_build_image as install

WORKDIR /code
COPY --from=scratch /code .
Expand Down
15 changes: 15 additions & 0 deletions webflux6-micrometer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## Tracing Example: Spring 6/Reactor Netty/Spring Boot 3/Micrometer/Log4J 2

Instead of servlet, this uses Spring Boot 3 to create a self-contained
application that runs Spring WebFlux 6 controllers.

* brave.example.Frontend and Backend: Rest controllers
* brave.example.AppAutoConfiguration: Sets up the WebClient used in the Frontend.

Application code doesn't show any tracing configuration because that's handled
by [Micrometer Tracing](https://docs.micrometer.io/tracing/reference/index.html),
configured by [Spring Boot](https://docs.spring.io/spring-boot/docs/3.2.2/reference/htmlsingle/#actuator.micrometer-tracing.tracer-implementations.brave-zipkin).

Micrometer Tracing has its own Tracer API that can bridge to Brave's with a
plugin. This allows users to mix Micrometer with native Brave instrumentation.
However, this example does not use any native Brave instrumentation.
91 changes: 91 additions & 0 deletions webflux6-micrometer/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.zipkin.brave.example</groupId>
<artifactId>brave-example-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../parent-pom.xml</relativePath>
</parent>

<artifactId>brave-example-webflux6-micrometer</artifactId>
<packaging>jar</packaging>

<name>brave-example-webflux6-micrometer</name>
<description>Tracing Example: Spring 6, Reactor Netty, Spring Boot 3, Micrometer, Log4J 2, JRE 21</description>

<properties>
<jre.version>21</jre.version>
<maven.compiler.release>17</maven.compiler.release>

<spring-boot.version>3.2.2</spring-boot.version>
<micrometer.version>1.2.2</micrometer.version>

<!-- pinned to old brave until this commit is released
https://github.com/micrometer-metrics/tracing/commit/d11d8039c73d901e44197b87103e66c392b6d7a8 -->
<brave.version>5.17.1</brave.version>
<zipkin-reporter.version>2.17.2</zipkin-reporter.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<!-- Configures Spring Boot with WebFlux SLF4J logging -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- Adds /actuator/health endpoint we re-map to /health used by our Docker HEALTHCHECK -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>${spring-boot.version}</version>
</dependency>

<!-- micrometer-tracing-bridge-brave configures instrumentation including what's in Brave and their own. -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
<version>${micrometer.version}</version>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
<version>${zipkin-reporter.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- This can be overridden to brave.example.Frontend also -->
<mainClass>brave.example.Backend</mainClass>
<classifier>exec</classifier>
<executable>true</executable>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package brave.example;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;

/** The application is simple, it only uses WebFlux. */
// This type makes support easier as forks can make a diff off a working AutoConfiguration type */
@Configuration
public class AppAutoConfiguration {
@Bean WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
}
30 changes: 30 additions & 0 deletions webflux6-micrometer/src/main/java/brave/example/Backend.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package brave.example;

import java.time.LocalDate;
import java.util.Optional;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

@EnableAutoConfiguration
@RestController
public class Backend {

@GetMapping("/api")
public Mono<String> printDate(@RequestHeader("user_name") Optional<String> username) {
return Mono.fromSupplier(() -> {
String date = LocalDate.now().toString();
return username.map(u -> date + " " + u).orElse(date);
});
}

public static void main(String[] args) {
SpringApplication.run(Backend.class,
"--spring.application.name=backend",
"--server.port=9000"
);
}
}
32 changes: 32 additions & 0 deletions webflux6-micrometer/src/main/java/brave/example/Frontend.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package brave.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

@EnableAutoConfiguration
@RestController
public class Frontend {
final WebClient webClient;

@Autowired Frontend(WebClient.Builder webClientBuilder,
@Value("${backend.endpoint:http://127.0.0.1:9000/api}") String backendEndpoint) {
this.webClient = webClientBuilder.baseUrl(backendEndpoint).build();
}

@GetMapping("/") public Mono<String> callBackend() {
return webClient.get().uri("").retrieve().bodyToMono(String.class);
}

public static void main(String[] args) {
SpringApplication.run(Frontend.class,
"--spring.application.name=frontend",
"--server.port=8081"
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
brave.example.AppAutoConfiguration
Loading

0 comments on commit d14a9b4

Please sign in to comment.