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

    Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.

    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 Tracing Support
= :keywords: helidon, tracing

== Tracing Support
Helidon includes support for tracing through the `https://opentracing.io/[OpenTracing]` APIs.
Tracing is integrated with WebServer, gRPC Server, and Security.

Support for specific tracers is abstracted. Your application can depend on
the abstraction layer and provide a specific tracer implementation as a Java
`ServiceLoader` service.


== Configuring Tracing with Helidon SE

Declare the following dependency in your project to use the tracer abstraction:

[source,xml]
.Tracer Abstraction
----
<dependency>
    <groupId>io.helidon.tracing</groupId>
    <artifactId>helidon-tracing</artifactId>
</dependency>
----

=== Configuring Tracing with WebServer

To configure tracer with WebServer:

[source,java]
.Configuring OpenTracing `Tracer`
----
ServerConfiguration.builder()
                   .tracer(TracerBuilder.create("my-application")                    // <1>
                                 .collectorUri(URI.create("http://10.0.0.18:9411"))  // <2>
                                 .build())
                   .build()
----
<1> The name of the application (service) to associate with the tracing events
<2> The endpoint for tracing events, specific to the tracer used, usually loaded from Config

=== Configuring Tracing with gRPC Server

[source,java]

.Configuring OpenTracing `Tracer`
----
Tracer tracer = (Tracer) TracerBuilder.create("Server")
        .collectorUri(URI.create("http://10.0.0.18:9411"))   // <1>
        .build();
----
<1> If using zipkin tracing system, the endpoint would be:
----
http://10.0.0.18:9411/api/v2/spans
----

.Configuring Tracing Attributes
----
TracingConfiguration tracingConfig = new TracingConfiguration.Builder()
                .withStreaming()
                .withVerbosity()
                .withTracedAttributes(ServerRequestAttribute.CALL_ATTRIBUTES,
                     ServerRequestAttribute.HEADERS,
                     ServerRequestAttribute.METHOD_NAME)
                .build();
----

.Configuring gRPC Server
----
GrpcServerConfiguration serverConfig = GrpcServerConfiguration.builder().port(0)
                .tracer(tracer)
                .tracingConfig(tracingConfig)
                .build();
----

=== Configuration using Helidon Config [[Tracing-config]]
There is a set of common configuration options that this section describes. In addition each tracer implementation
may have additional configuration options - please see the documentation of each of them.

Each implementation may provide defaults for these options.

All common configuration options:
|===
|key |description

|service |Name of the service sending the tracing information. This is usually visible in the trace data to
                distinguish actors in a conversation (e.g. when multiple microservices are connected together)
|protocol |Protocol of the tracing collector (e.g. `http`, `https`)
|host |Host of the tracing collector (e.g. `localhost`)
|port |Port of the tracing collector (e.g. `9411`)
|path |Path of the tracing collector service that is used to send spans to
|enabled |If set to false, tracing would be disabled
|tags |String tags that are to be added to each span reported (object node of string-string pairs)
|boolean-tags |Boolean tags that are to be added to each span reported (object node of string-boolean pairs)
|int-tags |Int tags that are to be added to each span reported (object node of string-int pairs)

|===

== Creating custom spans
To create a custom span that is a child of the WebServer request:

[source,java]
----
// or you can register the tracer as a global tracer
// and use GlobalTracer.get()
Tracer tracer = request.webServer().configuration().tracer();
Span span = tracer
        .buildSpan("my-operation")
        .asChildOf(serverRequest.spanContext())
                         .start();
----


== Trace propagation across services
Semi-automated trace propagation is supported currently only with Jersey client.

Currently you need to provide the tracer and parent span context to the client:

[source,java]
.Tracing propagation with Jersey client
----
import static io.helidon.tracing.jersey.client.ClientTracingFilter.CURRENT_SPAN_CONTEXT_PROPERTY_NAME;
import static io.helidon.tracing.jersey.client.ClientTracingFilter.TRACER_PROPERTY_NAME;

//...

Response response = client.target(serviceEndpoint)
    .request()
    // tracer should be provided unless available as GlobalTracer
    .property(TRACER_PROPERTY_NAME, tracer)
    .property(CURRENT_SPAN_CONTEXT_PROPERTY_NAME, spanContext)
    .get();
----

`Tracer` and `SpanContext` can be obtained from `ServerRequest`.