Настройка интеграции с trace-collector#

Настроить трэйсинг в приложении#

Для подключения трейсинга необходимо добавить в свое приложение следующие зависимости (показан пример для web-приложения с использованием springboot): pom.xml

<dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--зависимости для zipkin-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
            <version>2.2.7.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>p6spy</groupId>
            <artifactId>p6spy</artifactId>
            <version>3.9.1</version>
        </dependency>

        <dependency>
            <groupId>io.zipkin.brave</groupId>
            <artifactId>brave-instrumentation-p6spy</artifactId>
            <version>5.13.3</version>
        </dependency>
    </dependencies>

Так же добавить properties и в секции dependencyManagement указать: pom.xml

<properties>
    <java.version>11</java.version>
    <spring.cloud-version>2020.0.2</spring.cloud-version>
</properties>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring.cloud-version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

В файле application.properties или application.yaml указать строку подключения к сервису трейсинга: application.properties

spring.zipkin.base-url=http://адрес-сервиса-трассировки/trace

Настроить zipkin#

  1. Сгенерировать новый проект, например, с сайта https://start.spring.io/ (используя все настройки по умолчанию).

  2. В pom.xml добавить зависимости в секции dependencies, указанные в разделе «Подключение и конфигурирование». Должен получиться файл со следующим содержанием:

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>ru.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
        <spring.cloud-version>2020.0.2</spring.cloud-version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--зависимости zipkin-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
            <version>2.2.7.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>p6spy</groupId>
            <artifactId>p6spy</artifactId>
            <version>3.9.1</version>
        </dependency>

        <dependency>
            <groupId>io.zipkin.brave</groupId>
            <artifactId>brave-instrumentation-p6spy</artifactId>
            <version>5.13.3</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

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

</project>
  1. Добавить REST-контроллер, с двумя методами:

ZipkinRestController.java

package ru.example.demo.controller;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ZipkinRestController {
    private static final Logger LOGGER = LoggerFactory.getLogger(ZipkinRestController.class);

    @GetMapping("/rest1")
    public String rest1(){
        LOGGER.info("Вызван метод rest1");
        return "OK";
    }

    @GetMapping("/rest2")
    public String rest2(){
        LOGGER.info("Вызван метод rest2");
        return "OK";
    }

    @GetMapping("/rest3")
    public String rest3(){
        LOGGER.info("Вызван метод rest3");
        return "OK";
    }
}
  1. В файл application.propeties добавить строку для REST-сервиса трейсинга: application.properties

spring.zipkin.base-url=http://unver.tracing-collector.ci01976100-edevgen-log1-dev.apps.dev-gen.../trace
  1. Запустить приложение.

  2. Теперь при вызове методов /rest1, /rest2 и /rest3 приложение будет отправлять спаны. Откройте в браузере адрес http://localhost:8080/rest1, в результате в трейсинг отправится спан с таким содержимым: Пример спана

{
    "traceId": "7c10e61aa390c96e",
    "parentId": null,
    "id": "7c10e61aa390c96e",
    "kind": "SERVER",
    "name": "get /rest1",
    "timestamp": 1627571927993,
    "duration": 2172,
    "localServiceName": "default",
    "local_ipv4": "10.6.21.179",
    "remoteServiceName": null,
    "tag_http.method": "GET",
    "tag_http.path": "/rest1",
    "tag_mvc.controller.class": "ZipkinRestController",
    "tag_mvc.controller.method": "rest1"
}
  1. Теперь усложним приложение, чтобы увидеть ветвления спанов. Для этого подключим следующие артефакты: pom.xml

<!--для работы с кафкой-->
<dependency>
    <groupId>org.springframework.kafka</groupId>
    <artifactId>spring-kafka</artifactId>
    <version>2.6.7</version>
</dependency>
<!--для работы с jdbc-->
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
</dependency>
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
</dependency>
  1. Добавим java-конфигурацию ZipkinAppConfig.java

package ru.example.demo.configuration;

import com.p6spy.engine.spy.P6DataSource;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

import javax.sql.DataSource;

@Configuration
public class ZipkinAppConfig {

    @Bean
    RestTemplate getRestTemplate() {
        return new RestTemplate();
    }


    @Bean
    DataSource getDataSource() {
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setDriverClassName("org.postgresql.Driver");
        hikariConfig.setJdbcUrl("jdbc:postgresql://addr:5432/logging");
        hikariConfig.setUsername("login");
        hikariConfig.setPassword("pass");
        hikariConfig.setPoolName("hikariPool");

        HikariDataSource dataSource = new HikariDataSource(hikariConfig);

        return new P6DataSource(dataSource);
    }


}
  1. Для возможности трассировки запросов в бд необходимо добавить в папку resources файл spy.properties со следующей конфигурацией:

    modulelist=brave.p6spy.TracingP6Factory - включает саму трассировку бд includeParameterValues=true - опциональный параметр включает видимость значений параметров в запросе.

В REST-контроллере объявим три bean:

  1. DataSource - для работы с БД.

  2. RestTemplate - для HTTP-вызовов.

  3. KafkaTemplate - для записи в Kafka.

Напишем в каждом из методов вызовы для них.

  1. Код должен получиться примерно такой: ZipkinRestController.java

package ru.example.demo.controller;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

@RestController
public class ZipkinRestController {
    private static final Logger LOGGER = LoggerFactory.getLogger(ZipkinRestController.class);

    @Autowired
    public DataSource dataSource;

    @Autowired
    public RestTemplate restTemplate;

    @Autowired
    private KafkaTemplate<Object, Object> kafkaTemplate;

    @GetMapping("/rest1")
    public String rest1(){
        LOGGER.info("Вызван метод rest1");
        try (
                Connection connection = dataSource.getConnection();
                PreparedStatement statement = connection.prepareStatement("SELECT * FROM property");
                ResultSet result = statement.executeQuery()
                ){
            if (result.next()){
                LOGGER.info("Found some record");
            }
            else {
                LOGGER.info("No records found");
            }
        }
        catch (SQLException ex){
            ex.printStackTrace();
        }
        return "OK";
    }

    @GetMapping("/rest2")
    public String rest2(){
        LOGGER.info("Вызван метод rest2");
        return restTemplate.getForObject("https://someaddress.com",String.class);
    }

    @GetMapping("/rest3")
    public String rest3(){
        LOGGER.info("Вызван метод rest3");
        kafkaTemplate.send("j2","4321");
        return "OK";
    }
}
  1. Запускаем приложение. Теперь вместо одного спана, при вызове каждого endpoint нашего приложения, оно будет генерировать по два спана (первый – начало трейса, второй – его ответвление на другие сервисы): Результат вызова метода rest1

{
    "traceId": "d74804d217ff4f69",
    "parentId": null,
    "id": "d74804d217ff4f69",
    "kind": "SERVER",
    "name": "get /rest1",
    "timestamp": 1627656668323,
    "duration": 44696,
    "localServiceName": "default",
    "local_ipv4": "10.6.26.173",
    "remoteServiceName": null,
    "tag_http.method": "GET",
    "tag_http.path": "/rest1",
    "tag_mvc.controller.class": "ZipkinRestController",
    "tag_mvc.controller.method": "rest1"
}

и

{
    "traceId": "d74804d217ff4f69",
    "parentId": "d74804d217ff4f69",
    "id": "8c31cc7d47f4df0b",
    "kind": "CLIENT",
    "name": "select",
    "timestamp": 1627656668345,
    "duration": 19159,
    "localServiceName": "default",
    "local_ipv4": "10.6.26.173",
    "remoteServiceName": "logging",
    "remote_ipv4": "10.53.88.107",
    "tag_sql.query": "SELECT * FROM property"
}

Результат вызова метода rest2

{
    "traceId": "19ca0739ab8a1810",
    "parentId": null,
    "id": "19ca0739ab8a1810",
    "kind": "SERVER",
    "name": "get /rest2",
    "timestamp": 1627656885318,
    "duration": 1645781,
    "localServiceName": "default",
    "local_ipv4": "10.6.26.173",
    "remoteServiceName": null,
    "tag_error": "Request processing failed; nested exception is org.springframework.web.client.ResourceAccessException: I/O error on GET request for \"https://someaddress.com\": PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target",
    "tag_http.method": "GET",
    "tag_http.path": "/rest2",
    "tag_http.status_code": "500",
    "tag_mvc.controller.class": "ZipkinRestController",
    "tag_mvc.controller.method": "rest2"
}

и

{
    "traceId": "19ca0739ab8a1810",
    "parentId": "19ca0739ab8a1810",
    "id": "aeac85ecebf78c0a",
    "kind": "CLIENT",
    "name": "get",
    "timestamp": 1627656885349,
    "duration": 1604125,
    "localServiceName": "default",
    "local_ipv4": "10.6.26.173",
    "remoteServiceName": null,
    "tag_error": "PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target",
    "tag_http.method": "GET",
    "tag_http.path": ""
}

Результат вызова метода rest3

{
    "traceId": "44c6ef02b7a4b75d",
    "parentId": null,
    "id": "44c6ef02b7a4b75d",
    "kind": "SERVER",
    "name": "get /rest3",
    "timestamp": 1627657059135,
    "duration": 666969,
    "localServiceName": "default",
    "local_ipv4": "10.6.26.173",
    "remoteServiceName": null,
    "tag_http.method": "GET",
    "tag_http.path": "/rest3",
    "tag_mvc.controller.class": "ZipkinRestController",
    "tag_mvc.controller.method": "rest3"
}

и

{
    "traceId": "44c6ef02b7a4b75d",
    "parentId": "44c6ef02b7a4b75d",
    "id": "b8a10f8b991df241",
    "kind": "PRODUCER",
    "name": "send",
    "timestamp": 1627657059323,
    "duration": 558053,
    "localServiceName": "default",
    "local_ipv4": "10.6.26.173",
    "remoteServiceName": "kafka",
    "tag_kafka.topic": "j2"
}
  1. Как видно из примера, первые спаны не имеют родительского и в поле kind у них указано значение SERVER, а вторые спаны ссылаются на первые как на родительский и у них kind уже клиентский. В зависимости от типа взаимодействия так же отличается набор тегов спана.