Spring Boot Grafana Tempo Tracing | Observability Sinhala Guide

Spring Boot Grafana Tempo Tracing | Observability Sinhala Guide

කොහොමද යාලුවනේ! අද අපි කතා කරන්න යන්නේ දැන් දවසෙ Software Engineering ලෝකේ ගොඩක් වැදගත් මාතෘකාවක් ගැන. ඒ තමයි Distributed Tracing සහ Advanced Observability. විශේෂයෙන්ම, Spring Boot application එකක් හදනකොට අපිට Grafana Tempo භාවිතයෙන් Traces Visualize කරගන්නේ කොහොමද කියලා අද අපි පැහැදිලිව ඉගෙනගමු.

දැන් දවසෙ Software Development වලදි අපිට application එකක් හදනවා වගේම, ඒක හරියට වැඩ කරනවද, කොහෙන්ද ප්‍රශ්න එන්නේ, මොනවද Performance Bottlenecks වගේ දේවල් බලාගන්න එකත් ගොඩක් වැදගත්. විශේෂයෙන්ම Microservices Architecture එකක් භාවිත කරනකොට මේ දේවල් හොයාගන්න එක ලොකු අභියෝගයක් වෙනවා. මෙන්න මේ අභියෝගය ජයගන්න තමයි Distributed Tracing කියන සංකල්පය අපිට උදව් කරන්නේ.

මේ ලිපියෙන් අපි:

  • Distributed Tracing සහ Observability කියන්නේ මොනවද කියලා තේරුම් ගන්නවා.
  • Spring Boot application එකක් OpenTelemetry එක්ක සෙට් කරලා Traces generate කරන්නේ කොහොමද කියලා බලනවා.
  • Grafana Tempo ස්ථාපනය කරලා, ඒකට Traces යවන්නේ කොහොමද කියලා ඉගෙනගන්නවා.
  • අන්තිමට, Grafana භාවිතයෙන් Tempo වලින් එන Traces Visualize කරලා, අපේ application එකේ ගැටලු හොයාගන්නේ කොහොමද කියලා ප්‍රායෝගිකව කරලා බලනවා.

ඉතින්, ඔයා Spring Boot Developer කෙනෙක් නම්, නැත්නම් Distributed Systems ගැන උනන්දු නම්, මේ ලිපිය ඔයාට ගොඩක් ප්‍රයෝජනවත් වෙයි. එහෙනම්, අපි පටන් ගමු!

1. Distributed Tracing සහ Observability කියන්නේ මොනවද?

සරලව කිව්වොත්, Observability කියන්නේ application එකක internals, external outputs වලින් තේරුම් ගන්න පුළුවන් හැකියාවට. මේක මූලිකවම ත්‍රිත්වයකින් (three pillars) සමන්විත වෙනවා:

  • Logs: Events වලට අදාල text records. (Error messages, info messages වගේ)
  • Metrics: Numeric measurements over time. (CPU usage, memory usage, request count වගේ)
  • Traces: Single request එකක් application එකක components කිහිපයක් හරහා ගමන් කරන ආකාරය පෙන්වන දත්ත.

මේ හැම එකක්ම වැදගත් වුණත්, Microservices වගේ සංකීර්ණ පද්ධතිවල ගැටලු හඳුනාගන්න Distributed Tracing කියන එක අත්‍යවශ්‍යයි. හිතන්න ඔයාට Order Processing System එකක් තියෙනවා කියලා. ඒකේ Order Service, Payment Service, Inventory Service වගේ Microservices ගොඩක් තියෙන්න පුළුවන්. Customer කෙනෙක් Order එකක් දානකොට, ඒ request එක මේ Services ගොඩක් හරහා ගමන් කරනවා. යම් කිසි අවස්ථාවක Order එක Fail වුණොත්, ඒක Fail වුණේ Order Service එකේද, Payment Service එකේද, නැත්නම් Inventory Service එකේද කියලා හොයාගන්න එක ලොකු අභියෝගයක්. මෙන්න මේ වගේ අවස්ථාවකදී Distributed Tracing අපිට උදව් කරනවා.

Trace එකක් කියන්නේ සම්පූර්ණ request එකක end-to-end path එක. මේ Trace එක Spans කියන පොඩි කොටස් වලට බෙදෙනවා. හැම Span එකක්ම තනි මෙහෙයුමක් (operation) නියෝජනය කරනවා. උදාහරණයක් විදියට, Payment Service එකේදී සිදුවන database call එකක්, external API call එකක් වගේ දේවල් වෙනම Span එකක් විදියට පෙන්වන්න පුළුවන්. මේ Spans වලට unique ID, start/end time, duration, attributes (වැඩිදුර තොරතුරු) වගේ දේවල් අඩංගු වෙනවා.

OpenTelemetry කියන්නේ tracing data (වෙනත් observability data සමග) එකතු කරන්න, convert කරන්න, සහ export කරන්න භාවිත කරන industry standard එකක්. අපි මේකට Agent එකක් විදියට Spring Boot application එකට එකතු කරනවා.

2. Spring Boot Application එකක් OpenTelemetry එක්ක සෙට් කරගමු

හරි, දැන් අපි බලමු අපේ Spring Boot application එක Traces generate කරන්න සෙට් කරගන්නේ කොහොමද කියලා. අපි මේ සඳහා OpenTelemetry Java Agent එකක් භාවිත කරනවා. මේකෙන් අපිට Code එකේ කිසිම වෙනසක් කරන්නේ නැතුව (නැත්නම් ඉතා අඩු වෙනසකින්) Auto-Instrumentation කරන්න පුළුවන්.

2.1. Project එක සකස් කිරීම

මුලින්ම අපි සරල Spring Boot project එකක් හදාගමු. ඔබට Spring Initializr (start.spring.io) භාවිත කරන්න පුළුවන්. Spring Web dependency එක තෝරගන්න.

දැන් build.gradle (Gradle භාවිත කරනවා නම්) හෝ pom.xml (Maven භාවිත කරනවා නම්) එකට OpenTelemetry Brave Starter dependency එක එකතු කරන්න. Brave කියන්නේ OpenTelemetry API එක implement කරන Tracing Library එකක්. Spring Cloud Sleuth වලට පස්සේ දැන් OpenTelemetry තමයි standard වෙලා තියෙන්නේ.

// build.gradle

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'io.micrometer:micrometer-tracing-bridge-otel'
    implementation 'io.micrometer:micrometer-observation'
    implementation 'io.opentelemetry:opentelemetry-exporter-otlp'

    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

// OpenTelemetry agent එක download කරන්න පුළුවන් dependency management section එකට මේක එකතු කරන්න.
// අපි agent එක command line එකෙන් run කරන නිසා මේක අනිවාර්ය නෑ, ඒත් දැනුවත්ව සිටීම හොඳයි.
// implementation 'io.opentelemetry:opentelemetry-javaagent:1.x.x' // අවශ්‍ය වෙන්නේ නැහැ direct run කරනවා නම්

micrometer-tracing-bridge-otel කියන්නේ Spring Boot application වල tracing enable කරන official bridge එකයි. opentelemetry-exporter-otlp කියන්නේ OpenTelemetry Protocol (OTLP) එක හරහා traces export කරන්න අවශ්‍ය exporter එකයි.

2.2. Sample Spring Boot Application එකක්

දැන් අපි සරල REST Controller එකක් හදාගමු. මේකේදී අපි service calls දෙකක් simulated delay එකක් එක්ක කරලා traces generate කරගන්නවා.

// src/main/java/com/example/tempodemo/HelloController.java
package com.example.tempodemo;

import io.micrometer.observation.annotation.Observed;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@RestController
public class HelloController {

    private final HelloService helloService;

    public HelloController(HelloService helloService) {
        this.helloService = helloService;
    }

    @GetMapping("/hello")
    @Observed(name = "hello.endpoint") // Custom observation name for the endpoint
    public String hello() {
        String message = "Hello from Spring Boot!";
        message += " " + helloService.processData();
        return message;
    }
}

@Service
class HelloService {

    @Observed(name = "hello.service") // Custom observation name for the service method
    public String processData() {
        try {
            TimeUnit.MILLISECONDS.sleep(150);
            String data = getDataFromExternalSource();
            return "Data processed: " + data;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return "Processing interrupted";
        }
    }

    @Observed(name = "external.data.fetch") // Custom observation name for the simulated external call
    private String getDataFromExternalSource() {
        try {
            TimeUnit.MILLISECONDS.sleep(100);
            return "Some external data";
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return "External data fetch interrupted";
        }
    }
}

මෙහිදී @Observed annotation එක Micrometer Observation API එකෙන් එන එකක්. මේකෙන් අපිට Methods වලට observations එකතු කරන්න පුළුවන්. මේවා තමයි Traces විදියට Tempo වලට යන්නේ.

2.3. OpenTelemetry Agent එක භාවිත කිරීම

අපි OpenTelemetry Java Agent එක application එකට attach කරලා, ඒකෙන් Traces OTLP Exporter එක හරහා Grafana Tempo වලට යවන්න සෙට් කරමු. මේ සඳහා, අපි OpenTelemetry Java Agent JAR එක download කරගන්න ඕනේ.

ඔබට එය OpenTelemetry වෙබ් අඩවියෙන් download කරගත හැක. සාමාන්‍යයෙන් මේ වගේ command එකකින් download කරන්න පුළුවන්:

wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar

දැන් application එක run කරනකොට මේ Agent එක attach කරන්න පුළුවන්. අපි environment variables කිහිපයක් සෙට් කරන්න ඕනේ:

java -javaagent:/path/to/opentelemetry-javaagent.jar \
     -Dotel.service.name=tempo-demo-service \
     -Dotel.exporter.otlp.traces.endpoint=http://localhost:4317 \
     -jar target/tempo-demo-0.0.1-SNAPSHOT.jar
  • -javaagent:/path/to/opentelemetry-javaagent.jar: OpenTelemetry Agent JAR එකේ path එක.
  • -Dotel.service.name=tempo-demo-service: මේක application එකේ නම. Tempo වල traces filter කරන්න මේක වැදගත්.
  • -Dotel.exporter.otlp.traces.endpoint=http://localhost:4317: Traces යවන්න ඕනේ OTLP collector endpoint එක. අපි මේක Grafana Tempo වල default OTLP port එකට යොමු කරනවා.

ඔබට application.properties හෝ application.yml එකේත් මේවා සෙට් කරන්න පුළුවන්, නමුත් Agent එක භාවිත කරනකොට command line එකෙන් සෙට් කරන එක සාමාන්‍යයි. Micrometer Tracing එක්කනම් application.properties එකේ management.tracing.sampling.probability=1.0 වගේ දේවල් add කරන්න පුළුවන්, ඒකෙන් සියලුම requests වලට traces generate කරනවා.

3. Grafana Tempo ස්ථාපනය කරලා Config කරගමු

දැන් අපේ Spring Boot application එක Traces generate කරන්න සූදානම්. ඊළඟට අපිට මේ Traces collect කරලා store කරන්න තැනක් ඕනේ. ඒකට තමයි Grafana Tempo භාවිත කරන්නේ. Tempo කියන්නේ high-volume, cost-effective distributed tracing backend එකක්. මේකෙන් Traces index කරන්නේ නැතුව raw trace data store කරන නිසා ගොඩක් cost-effective වෙනවා.

අපි Docker Compose භාවිතයෙන් Tempo සහ Grafana පහසුවෙන් ස්ථාපනය කරගමු.

# docker-compose.yml
version: '3.8'

networks:
  tempo-net:

services:
  tempo:
    image: grafana/tempo:latest
    container_name: tempo
    command: [
      "-config.file=/etc/tempo.yaml"
    ]
    volumes:
      - ./tempo-config.yaml:/etc/tempo.yaml
    ports:
      - "14268:14268"  # Jaeger Thrift HTTP
      - "4317:4317"    # OTLP gRPC
      - "4318:4318"    # OTLP HTTP
      - "9411:9411"    # Zipkin
      - "3100:3100"    # Tempo HTTP/gRPC
    networks:
      - tempo-net

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - "3000:3000"
    networks:
      - tempo-net
    environment:
      - GF_AUTH_ANONYMOUS_ENABLED=true
      - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
      - GF_PLUGINS_ALLOW_INSTALL=true

මේ docker-compose.yml එකේ අපි tempo සහ grafana කියන Services දෙක define කරලා තියෙනවා. tempo service එකට අපි custom config file එකක් (tempo-config.yaml) mount කරලා තියෙනවා.

දැන් tempo-config.yaml file එක හදාගමු:

# tempo-config.yaml
server:
  http_listen_port: 3100

ingester:
  trace_shards: 256
  max_block_duration: 5m
  block_retention: 1h
  wal_cleanup_interval: 1h

compactor:
  compaction_interval: 10m
  compaction_windows: [1h, 6h, 24h]

storage:
  path: /tmp/tempo/blocks
  wal: 
    path: /tmp/tempo/wal
  backend: local

# OTLP receiver for traces
receivers:
  otlp:
    protocols:
      grpc:
      http:

# Service Graph generator (useful for advanced visualization)
metrics_generator:
  registry:
    external_labels:
      cluster: production
  storage:
    path: /tmp/tempo/metrics
    wal: 
      path: /tmp/tempo/metrics-wal
    backend: local

# Configure our own instance so we can easily add it to grafana
querier:
  search_enabled: true

මේ config එක සරලයි. අපි receivers section එකේ OTLP protocol එක enable කරලා තියෙනවා. මේක තමයි අපේ Spring Boot application එකෙන් එන traces ලබාගන්න භාවිත කරන්නේ. storage section එකෙන් traces store කරන ආකාරය define කරනවා. අපි දැනට local storage (Docker container එක ඇතුලේ) භාවිත කරනවා.

දැන් මේ services දෙකම start කරන්න:

docker-compose up -d

සියල්ල හොඳින් ක්‍රියාත්මක වෙනවා නම්, ඔබට Grafana http://localhost:3000 වෙතින් ප්‍රවේශ විය හැකි අතර, Tempo port 4317 (OTLP gRPC) listen කරමින් සිටිය යුතුය.

4. Grafana එක්ක Tempo සම්බන්ධ කරලා Traces Visualize කරමු

දැන් අපි Grafana open කරලා, ඒකට Tempo Data Source එක add කරමු. Grafana වලට ගිහින් (http://localhost:3000), default username admin සහ password admin භාවිත කරලා Login වෙන්න. (ඔබ GF_AUTH_ANONYMOUS_ENABLED=true දාලා නම් login වෙන්න අවශ්‍ය වෙන්නේ නැහැ).

4.1. Tempo Data Source එක Add කිරීම

  1. වම් පැත්තේ තියෙන මෙනු එකෙන් Connections > Data sources වලට යන්න.
  2. Add new data source button එක click කරන්න.
  3. Tempo කියලා search කරලා, Tempo data source එක select කරන්න.
  4. Settings page එකේදී:
    • Name: Tempo (ඔබ කැමති නමක් දෙන්න පුළුවන්)
    • URL: http://tempo:3100 (මෙහි tempo කියන්නේ අපේ docker-compose.yml එකේ තියෙන service name එක. ඒක Grafana container එක ඇතුලේ Tempo container එකට communicate කරනවා.)
    • Save & test button එක click කරන්න. Data source is working කියලා message එකක් එන්න ඕනේ.

4.2. Traces Generate කරලා Visualize කිරීම

දැන් Grafana Tempo එක අපේ traces ලබාගන්න සූදානම්. අපි Spring Boot application එක start කරමු.

ඔබේ Terminal එකේ application JAR file එක තියෙන තැනට ගිහින්, කලින් කී විදියට Agent එක attach කරලා run කරන්න:

java -javaagent:/path/to/opentelemetry-javaagent.jar \
     -Dotel.service.name=tempo-demo-service \
     -Dotel.exporter.otlp.traces.endpoint=http://localhost:4317 \
     -jar target/tempo-demo-0.0.1-SNAPSHOT.jar

Application එක start වුණාට පස්සේ, Postman, cURL, නැත්නම් browser එකකින් http://localhost:8080/hello endpoint එකට requests කිහිපයක් යවන්න.

curl http://localhost:8080/hello

දැන් Grafana වල Explore section එකට යන්න. ඉහළින් තියෙන dropdown එකෙන් Tempo data source එක තෝරගන්න. එතන ඔබට traces හොයාගන්න පුළුවන්. ඔබට Service Name එක tempo-demo-service විදියට filter කරන්න පුළුවන්, නැත්නම් වෙනත් attributes (http.method, http.url) භාවිත කරන්න පුළුවන්. Run Query button එක click කරාම, ඔබට ලැබුණු traces ලැයිස්තුවක් පෙනෙයි.

ලැයිස්තුවේ තියෙන Trace ID එකක් click කරාම, ඔබට සම්පූර්ණ Trace එකේ Flame Graph සහ Span List එකක් විදියට Diagrams බලාගන්න පුළුවන්. මේකෙන් hello.endpoint එකෙන් පටන් අරන්, hello.service එක හරහා external.data.fetch එකට යන flow එක පැහැදිලිව පෙනෙයි. ඒ වගේම, හැම Span එකකම duration එක, attributes වගේ දේවල් බලාගන්නත් පුළුවන්. මෙතනින් තමයි Performance issues, Errors වගේ දේවල් pinpoint කරන්න පුළුවන් වෙන්නේ.

5. Advanced Tracing සහ Tempo වල විශේෂාංග

අපි මේකෙන් මූලික tracing එකක් implement කරා. නමුත්, Grafana Tempo තව ගොඩක් දේවල් කරන්න පුළුවන්. මෙන්න තවත් වැදගත් විශේෂාංග කිහිපයක්:

5.1. Custom Instrumentation

අපේ Spring Boot application එකේ @Observed annotation එකෙන් Auto-Instrumentation කරා. නමුත් සමහර වෙලාවට අපිට custom Spans හදන්න, Attributes එකතු කරන්න අවශ්‍ය වෙනවා. ඒකට OpenTelemetry API එක direct භාවිත කරන්න පුළුවන්.

// Example of manual instrumentation

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.context.Context;
import org.springframework.stereotype.Service;

@Service
public class CustomProcessorService {

    private final Tracer tracer;

    public CustomProcessorService(Tracer tracer) {
        this.tracer = tracer;
    }

    public void performComplexOperation() {
        Span span = tracer.spanBuilder("complex-operation")
                .setSpanKind(SpanKind.INTERNAL)
                .startSpan();
        try (io.opentelemetry.context.Scope scope = span.makeCurrent()) {
            // Add custom attributes to the span
            span.setAttribute("component", "business-logic");
            span.setAttribute("input.size", 123);

            // Simulate some work
            TimeUnit.MILLISECONDS.sleep(200);

            // Call another method which might be automatically instrumented or manually handled
            doAnotherSubTask();

        } catch (InterruptedException e) {
            span.recordException(e);
            Thread.currentThread().interrupt();
        } finally {
            span.end();
        }
    }

    private void doAnotherSubTask() throws InterruptedException {
        // This method's trace will be a child of 'complex-operation'
        Span subSpan = tracer.spanBuilder("sub-task")
                .setParent(Context.current())
                .startSpan();
        try (io.opentelemetry.context.Scope scope = subSpan.makeCurrent()) {
            subSpan.setAttribute("task.type", "database-query");
            TimeUnit.MILLISECONDS.sleep(50);
        } finally {
            subSpan.end();
        }
    }
}

මෙහිදී Tracer instance එක Autowire කරලා, අපිට අවශ්‍ය විදියට Spans හදාගන්න පුළුවන්. span.setAttribute() භාවිතයෙන් Custom attributes එකතු කරන්නත් පුළුවන්. මේවා Grafana Explore එකේදී filter කරන්න සහ බලන්න පුළුවන්.

5.2. Service Graph Visualization

ඔබ tempo-config.yaml එකේ metrics_generator section එක enable කරලා තියෙනවා නම්, Tempo විසින් Traces වලින් Metrics generate කරනවා. මේ Metrics භාවිතයෙන් Grafana වල Service Graph එකක් (Node Graph) හදාගන්න පුළුවන්. මේකෙන් Microservices අතර dependencies සහ call flows තව පැහැදිලිව දකින්න පුළුවන්.

5.3. Logs සහ Metrics සමග Correlation

Grafana Tempo එකේ විශේෂත්වය තමයි එය Grafana Loki (logs) සහ Prometheus (metrics) වගේ අනෙකුත් Grafana stack components එක්ක seamless විදියට integrate වෙන එක. Grafana වල Trace එකක් බලනකොට, ඔබට Trace ID එක භාවිතයෙන් අදාල Logs සහ Metrics පහසුවෙන් ලබාගන්න පුළුවන්. මේකෙන් Debugging experience එක ගොඩක් පහසු වෙනවා. මෙය 'Logs to Traces' සහ 'Traces to Logs' functionality එක හරහා සිදු වෙනවා.

අවසන් වශයෙන්

ඉතින් යාලුවනේ, අද අපි Spring Boot application වල Advanced Observability ගැන ගොඩක් දේවල් ඉගෙනගත්තා. Distributed Tracing කියන්නේ මොකක්ද, OpenTelemetry භාවිතයෙන් Traces generate කරන්නේ කොහොමද, Grafana Tempo ස්ථාපනය කරලා Traces store කරන්නේ කොහොමද, සහ Grafana භාවිතයෙන් මේ Traces Visualize කරලා අපේ application එකේ performance issues සහ errors හොයාගන්නේ කොහොමද කියලා අපි ප්‍රායෝගිකවම කරලා බැලුවා.

Microservices Architecture එකක් භාවිත කරනකොට, මේ වගේ Observability tools අත්‍යාවශ්‍යයි. Grafana Tempo කියන්නේ මේ සඳහා තියෙන powerful, scalable, සහ cost-effective solution එකක්. මේක ඔයාගේ ඊළඟ Project එකේ try කරලා බලන්න. ලංකාවේ අපිත් දැන් දියුණු වෙමින් පවතින Software Engineering ලෝකේ ඉදිරියටම යන්න ඕනේ!

ඔබට මේ ගැන තව ප්‍රශ්න තියෙනවා නම්, නැත්නම් මේක implement කරනකොට ගැටලු ආවා නම්, පහලින් comment section එකේ ඔබේ අදහස් සහ ප්‍රශ්න දාන්න. අපි උදව් කරන්න සූදානම්. ඔබට සුබ දවසක්!