All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mp.tracing.adoc Maven / Gradle / Ivy

///////////////////////////////////////////////////////////////////////////////

    Copyright (c) 2018, 2024 Oracle and/or its affiliates.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

///////////////////////////////////////////////////////////////////////////////

= Tracing
:description: Helidon MP Tracing Support
:feature-name: MicroProfile Tracing
:keywords: helidon, tracing, microprofile, micro-profile
:microprofile-bundle: true
:rootdir: {docdir}/..

include::{rootdir}/includes/mp.adoc[]

== Contents

- <>
- <>
- <>
- <>
- <>
- <>
** <>
** <>
** <>
- <>

== Overview

WARNING: The OpenTracing Specification that MP OpenTracing is based on is no longer maintained.
The MP OpenTracing specification is no longer required by MicroProfile. This feature is marked as `@Deprecated` in Helidon. The specification is superseded by the link:https://github.com/eclipse/microprofile-telemetry[MicroProfile Telemetry specification].

Distributed tracing is a critical feature of micro-service based applications, since it traces workflow both
within a service and across multiple services. This provides insight to sequence and timing data for specific blocks of work,
which helps you identify performance and operational issues. Helidon MP includes support for distributed tracing
through the https://opentracing.io[OpenTracing API]. Tracing is integrated with WebServer
and Security.

include::{rootdir}/includes/dependencies.adoc[]

// tag::tracing-dependency[]
[source,xml]
----

    io.helidon.microprofile.tracing
    helidon-microprofile-tracing

----
// end::tracing-dependency[]

== Usage

This section explains a few concepts that you need to understand before you get started with tracing.

* In the context of this document, a _service_ is synonymous with an application.
* A _span_ is the basic unit of work done within a single service, on a single host.
Every span has a name, starting timestamp, and duration.
For example, the work done by a REST endpoint is a span.
A span is associated to a single service, but its descendants can belong to different services and hosts.
* A _trace_ contains a collection of spans from one or more services, running on one or more hosts. For example,
if you trace a service endpoint that calls another service, then the trace would contain spans from both services.
Within a trace, spans are organized as a directed acyclic graph (DAG) and
can belong to multiple services, running on multiple hosts.  The _OpenTracing Data Model_ describes the details
at https://opentracing.io/specification[The OpenTracing Semantic Specification].
Spans are automatically created by Helidon as needed during execution of the REST request. Additional spans can be added
through MP annotation `@Traced` or through OpenTracing APIs.

include::{rootdir}/includes/tracing/common-spans.adoc[]

== Configuration

=== Enabling and Disabling Tracing

You can configure a custom service name using the `tracing.service` configuration property. If this
property is undefined, name is created from JAX-RS Application name, or `Helidon MP` is used if no application
is defined.

include::{rootdir}/config/io_helidon_tracing_Tracer.adoc[tag=config,levelOffset=2]

To disable Helidon tracing for web server and security:
[source,properties]
----
tracing.components.web-server.enabled=false
tracing.components.security.enabled=false
----

To disables MP Tracing as by specification:
[source,properties]
----
mp.opentracing.server.skip-pattern=.*
----

Tracing configuration can be defined in `application.yaml` file.

[source,yaml]
.Tracing configuration example
----
tracing:
  paths:
    - path: "/favicon.ico"
      enabled: false
    - path: "/metrics"
      enabled: false
    - path: "/health"
      enabled: false
  components:
    web-server:
      spans:
        - name: "HTTP Request"
          logs:
            - name: "content-write"
              enabled: false
----

=== Controlling Tracing Output

For Web Server we have a path based support for configuring tracing, in addition
to the configuration described above.

Configuration of path can use any path string supported by the
Web Server. The configuration itself has the same possibilities
as traced configuration described above. The path specific configuration
will be merged with global configuration (path is the "newer" configuration, global is the "older")

==== Renaming top level span using request properties

To have a nicer overview in search pane of a tracer, you can customize the top-level span name using configuration.

Example:
[source,properties]
.Configuration properties
----
tracing.components.web-server.spans.0.name="HTTP Request"
tracing.components.web-server.spans.0.new-name: "HTTP %1$s %2$s"
----

This is supported ONLY for the span named "HTTP Request" on component "web-server".

Parameters provided:

1. Method - HTTP method
2. Path - path of the request (such as '/greet')
3. Query - query of the request (may be null)


== Examples

The examples in this guide demonstrate how to integrate tracing with Helidon, how to view traces, how to trace
across multiple services, and how to integrate tracing with Kubernetes.  All examples use Jaeger and traces
will be viewed using both the Jaeger UI.


=== Set up Jaeger

First, you need to run the Jaeger tracer.  Helidon will communicate with this tracer at runtime.

[source,bash]
.Run Jaeger within a docker container, then check the Jaeger server working:
----
docker run -d --name jaeger \                  <1>
  -e COLLECTOR_OTLP_ENABLED=true \
  -p 6831:6831/udp \
  -p 6832:6832/udp \
  -p 5778:5778 \
  -p 16686:16686 \
  -p 4317:4317 \
  -p 4318:4318 \
  -p 14250:14250 \
  -p 14268:14268 \
  -p 14269:14269 \
  -p 9411:9411 \
  jaegertracing/all-in-one:1.50

----
<1> Run the Jaeger docker image.


[source,bash]
.Check the Jaeger server by opening in browser:
----
http://localhost:16686/search
----

=== Trace Across Services

Helidon automatically traces across services as long as the services use the same tracer, for example, the same instance of Jaeger.
This means a single trace can include spans from multiple services and hosts.  OpenTracing uses a `SpanContext` to propagate tracing information across process boundaries.  When you make client API calls, Helidon will internally call OpenTracing APIs to propagate the `SpanContext`. There is nothing you need to do in your application to make this work.

To demonstrate distributed tracing, you will need to create a second project, where the server listens on port 8081.
Create a new root directory to hold this new project, then do the following steps, similar to
what you did at the start of this guide:

==== Create a second service

[source,bash,subs="attributes+"]
.Run the Maven archetype:
----
mvn -U archetype:generate -DinteractiveMode=false \
    -DarchetypeGroupId=io.helidon.archetypes \
    -DarchetypeArtifactId=helidon-quickstart-mp \
    -DarchetypeVersion={helidon-version} \
    -DgroupId=io.helidon.examples \
    -DartifactId=helidon-quickstart-mp-2 \
    -Dpackage=io.helidon.examples.quickstart.mp
----

[source,bash]
.The project will be built and run from the `helidon-quickstart-mp` directory:
----
cd helidon-quickstart-mp-2
----

[source,xml]
.Add the following dependency to `pom.xml`:
----

    io.helidon.tracing.providers
    helidon-tracing-providers-jaeger

----

[source,bash]
.Replace `META-INF/microprofile-config.properties` with the following:
----
app.greeting=Hello From MP-2
tracing.service=helidon-mp-2

# Microprofile server properties
server.port=8081
----

[source,bash]
.Build the application, skipping unit tests, then run it:
----
mvn package -DskipTests=true
java -jar target/helidon-quickstart-mp-2.jar
----

[source,bash]
.Run the curl command in a new terminal window and check the response (*notice the port is 8081*) :
----
curl http://localhost:8081/greet
----

[source, json]
.Response body
----
{
  "message": "Hello From MP-2 World!"
}
----

==== Modify the first service

Once you have validated that the second service is running correctly, you need to modify the original application to
call it.

[source,java]
.Replace the `GreetResource` class with the following code:
----
include::{sourcedir}/mp/TracingSnippets.java[tag=snippet_1, indent=0]
----
<1> This is the `WebTarget` needed to send a request to the second service at port `8081`.
<2> This is the new endpoint that will call the second service.


[source,bash]
.Build and run the application, then invoke the endpoint and check the response:
----
curl -i http://localhost:8080/greet/outbound # <1>
----
<1> The request went to the service on `8080`, which then invoked the service at `8081` to get the greeting.

[source, hocon]
.Response body
----
{
  "message": "Hello From MP-2 World!" // <1>
}
----
<1> Notice the greeting came from the second service.

Refresh the Jaeger UI trace listing page and notice that there is a trace across two services.

.Tracing across multiple services detail view
image::guides/12_tracing_detail_2_services.png[Traces,role="fit"]

In the image above, you can see that the trace includes spans from two services. You will notice there is a gap before the sixth span, which is a `get` operation. This is a one-time client initialization delay.  Run the `/outbound` curl command again and look at the new trace to
see that the delay no longer exists.

You can now stop your second service, it is no longer used in this guide.

== Integration with Kubernetes

The following example demonstrates how to use Jaeger from a Helidon application running in Kubernetes.

[source,bash]
.Update `application.yaml`:
----
tracing:
  host: "jaeger"
----

[source,bash]
.Stop the application and build the docker image for your application:
----
docker build -t helidon-tracing-mp .
----

=== Deploy Jaeger into Kubernetes

[source,yaml]
.Create the Kubernetes YAML specification, named `jaeger.yaml`, with the following contents:
----
apiVersion: v1
kind: Service
metadata:
  name: jaeger
spec:
  ports:
    - port: 16686
      protocol: TCP
  selector:
    app: jaeger
---
kind: Pod
apiVersion: v1
metadata:
  name: jaeger
  labels:
    app: jaeger
spec:
  containers:
    - name: jaeger
      image: jaegertracing/all-in-one
      imagePullPolicy: IfNotPresent
      ports:
        - containerPort: 16686
----

[source,bash]
.Create the Jaeger pod and ClusterIP service:
----
kubectl apply -f ./jaeger.yaml
----

[source,bash]
.Create a Jaeger external server and expose it on port 9142:
----
kubectl expose pod jaeger --name=jaeger-external --port=16687 --target-port=16686 --type=LoadBalancer # <1>
----
<1> Create a service so that you can access the Jaeger UI.

Navigate to http://localhost:16687/search to validate that you can access Jaeger running in Kubernetes. It may
take a few seconds before it is ready.

=== Deploy Your Helidon Application into Kubernetes

[source,yaml]
.Create the Kubernetes YAML specification, named `tracing.yaml`, with the following contents:
----
kind: Service
apiVersion: v1
metadata:
  name: helidon-tracing # <1>
  labels:
    app: helidon-tracing
spec:
  type: NodePort
  selector:
    app: helidon-tracing
  ports:
    - port: 8080
      targetPort: 8080
      name: http
---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: helidon-tracing
spec:
  replicas: 1 # <2>
  selector:
    matchLabels:
      app: helidon-tracing
  template:
    metadata:
      labels:
        app: helidon-tracing
        version: v1
    spec:
      containers:
        - name: helidon-tracing
          image: helidon-tracing-mp
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8080
----
<1> A service of type `NodePort` that serves the default routes on port `8080`.
<2> A deployment with one replica of a pod.

[source,bash]
.Create and deploy the application into Kubernetes:
----
kubectl apply -f ./tracing.yaml
----

=== Access Your Application and the Jaeger Trace

[source,bash]
.Get the application service information:
----
kubectl get service/helidon-tracing
----

[source,bash]
----
NAME             TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
helidon-tracing   NodePort   10.99.159.2           8080:31143/TCP   8s # <1>
----
<1> A service of type `NodePort` that serves the default routes on port `31143`.

[source,bash]
.Verify the tracing endpoint using port `31143`, your port will likely be different:
----
curl http://localhost:31143/greet
----

[source, json]
----
{
  "message": "Hello World!"
}
----

Access the Jaeger UI at http://localhost:16687/search and click on the refresh icon to see the trace that was just created.

=== Cleanup

You can now delete the Kubernetes resources that were just created during this example.

[source,bash]
.Delete the Kubernetes resources:
----
kubectl delete -f ./jaeger.yaml
kubectl delete -f ./tracing.yaml
kubectl delete service jaeger-external
docker rm -f jaeger
----

== Creating custom spans

Helidon MP fully supports MicroProfile OpenTracing.
You can add custom spans using `@Traced` annotation on methods of CDI beans.

*Note for invoking methods on same class:*
_If you invoke a method on the same class, `@Traced` annotation would be ignored, as it is not
invoked through a CDI proxy and as such cannot be intercepted.
To make sure `@Traced` is honored, use it on JAX-RS resource methods and on CDI bean methods used from other beans._

== Trace propagation across services
Automated trace propagation is supported currently only with Jersey client.

Tracing propagation works automatically as long as you run within the scope of
Helidon MP and use Helidon components to invoke external services.

=== Manual handling of traces in Jersey Client

There is an option to provide `SpanContext` programmatically (such as when writing a command line
application that starts the span manually).

You can either configure the span context as the active span, or explicitly define it as client property.

[source,java]
.Tracing propagation with Jersey client
----
include::{sourcedir}/mp/TracingSnippets.java[tag=snippet_2, indent=0]
----

== Additional Information



=== Jaeger Tracing [[jaeger-tracing]]

include::{rootdir}/includes/tracing/tracer-jaeger.adoc[tag=jaeger-dependency]
include::{rootdir}/includes/tracing/tracer-jaeger.adoc[tag=jaeger-configuration]

=== Jaeger Tracing Metrics [[jaeger-tracing-metrics]]

As the <> section describes, you can use Jaeger tracing in your Helidon application.

=== Zipkin Tracing [[zipkin-tracing]]

include::{rootdir}/includes/tracing/tracer-zipkin.adoc[tag=zipkin-dependency]
include::{rootdir}/includes/tracing/tracer-zipkin.adoc[tag=zipkin-configuration]


include::{rootdir}/includes/tracing/common-callbacks.adoc[tags=defs;detailed,leveloffset=+1]

== Reference

* link:{microprofile-tracing-spec-url}[MicroProfile Opentracing Specification]
* link:https://opentracing.io/[Opentracing Project]




© 2015 - 2025 Weber Informatics LLC | Privacy Policy