Настройка интеграции с 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#
Сгенерировать новый проект, например, с сайта https://start.spring.io/ (используя все настройки по умолчанию).
В 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>
Добавить 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";
}
}
В файл application.propeties добавить строку для REST-сервиса трейсинга: application.properties
spring.zipkin.base-url=http://unver.tracing-collector.ci01976100-edevgen-log1-dev.apps.dev-gen.../trace
Запустить приложение.
Теперь при вызове методов /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"
}
Теперь усложним приложение, чтобы увидеть ветвления спанов. Для этого подключим следующие артефакты: 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>
Добавим 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);
}
}
Для возможности трассировки запросов в бд необходимо добавить в папку resources файл spy.properties со следующей конфигурацией:
modulelist=brave.p6spy.TracingP6Factory - включает саму трассировку бд includeParameterValues=true - опциональный параметр включает видимость значений параметров в запросе.
В REST-контроллере объявим три bean:
DataSource - для работы с БД.
RestTemplate - для HTTP-вызовов.
KafkaTemplate - для записи в Kafka.
Напишем в каждом из методов вызовы для них.
Код должен получиться примерно такой: 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";
}
}
Запускаем приложение. Теперь вместо одного спана, при вызове каждого 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"
}
Как видно из примера, первые спаны не имеют родительского и в поле kind у них указано значение SERVER, а вторые спаны ссылаются на первые как на родительский и у них kind уже клиентский. В зависимости от типа взаимодействия так же отличается набор тегов спана.