{"id":18311347,"url":"https://github.com/t0malexander/observabilidade","last_synced_at":"2025-12-30T23:05:09.533Z","repository":{"id":195466831,"uuid":"692276631","full_name":"T0mAlexander/Observabilidade","owner":"T0mAlexander","description":"Repositório com pilares da Observabilidade","archived":false,"fork":false,"pushed_at":"2024-05-26T16:06:46.000Z","size":90584,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"spring-maven","last_synced_at":"2025-02-15T06:16:58.468Z","etag":null,"topics":["docker","go","grafana","postgresql","prometheus"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/T0mAlexander.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-09-16T01:51:42.000Z","updated_at":"2024-05-27T21:19:20.000Z","dependencies_parsed_at":"2023-09-18T05:23:35.793Z","dependency_job_id":"665f11c7-1ff5-49c6-a91e-1fff7257dc25","html_url":"https://github.com/T0mAlexander/Observabilidade","commit_stats":{"total_commits":37,"total_committers":1,"mean_commits":37.0,"dds":0.0,"last_synced_commit":"1c2e165a19d2baf0e34ef8d9fd5f52dfd1517774"},"previous_names":["t0malexander/observabilidade-alura","t0malexander/observabilidade"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/T0mAlexander%2FObservabilidade","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/T0mAlexander%2FObservabilidade/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/T0mAlexander%2FObservabilidade/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/T0mAlexander%2FObservabilidade/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/T0mAlexander","download_url":"https://codeload.github.com/T0mAlexander/Observabilidade/tar.gz/refs/heads/spring-maven","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248036065,"owners_count":21037092,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","go","grafana","postgresql","prometheus"],"created_at":"2024-11-05T16:17:23.613Z","updated_at":"2025-12-30T23:05:09.520Z","avatar_url":"https://github.com/T0mAlexander.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Spring Boot com Observabilidade\n\nMonitoramento de uma aplicação em Spring Boot com os três pilares da observabilidade no [Grafana](https://grafana.com/docs/grafana/latest/):\n\n- Rastreamento com [Grafana Tempo](https://grafana.com/docs/tempo/latest/) e [OpenTelemetry para Java](https://opentelemetry.io/docs/languages/java/getting-started/)\n\n- Métricas com [Prometheus](https://prometheus.io/), [Spring Boot Actuator](https://docs.spring.io/spring-boot/docs/current/actuator-api/htmlsingle/) e [Micrometer](https://micrometer.io/)\n\n- Centralização de logs com [Grafana Loki](https://grafana.com/docs/loki/latest/) e [Logback](https://www.slf4j.org/manual.html)\n\n![Arquitetutra](./images/observability-arch.jpg)\n\u003e Errata: a rota de extração de métricas de todas as aplicações é a **`/actuator/prometheus`**\n\n## Indíce\n\n- [Aplicação](#indíce)\n  - [Inicialização rápida](#inicialização-rápida)\n  - [Explorando o Grafana](#explorando-o-grafana)\n    - [Métricas em Rastreamento](#métricas-em-rastreamento)\n    - [Rastreamento em Logs](#rastreamento-em-logs)\n    - [Logs em Rastreamento](#logs-em-rastreamento)\n\n  - [Detalhes](#detalhes)\n    - [Aplicação Spring Boot](#aplicação-spring-boot)\n      - [Instrumentação com OpenTelemetry](#instrumentação-com-opentelemetry)\n      - [Logs](#logs)\n      - [Rastreamento](#rastreamento)\n      - [Métricas](#métricas)\n        - [Métricas com Exemplar](#métricas-com-exemplar)\n\n    - [Prometheus - Métricas](#prometheus---métricas)\n      - [Configuração do Prometheus](#configuração-do-prometheus)\n      - [Transmitindo para o Grafana](#transmitindo-para-o-grafana)\n\n    - [Grafana Tempo - Rastreamento](#grafana-tempo---rastreamento)\n      - [Transmitindo para o Grafana](#transmitindo-para-o-grafana-1)\n\n    - [Grafana Loki - Logs](#grafana-loki---logs)\n      - [Driver do Docker](#driver-do-docker)\n      - [Transmitindo para o Grafana](#transmitindo-para-o-grafana-2)\n    - [Grafana](#grafana)\n  - [Referências](#referências)\n\n## Inicialização rápida\n\n1. Instale o [driver do Loki](https://grafana.com/docs/loki/latest/send-data/docker-driver/) para extração de logs dos containers\n\n   ```bash\n   docker plugin install grafana/loki-docker-driver:latest --alias loki --grant-all-permissions\n   ```\n\n  \u003e **Atenção**: atente-se a versão instalada pois ela deverá ser a mesma do container do Grafana Loki\n\n2. Inicialize todos os contêineres\n\n   ```bash\n   docker compose up -d\n   ```\n\n3. Execute scripts simulando interações nas aplicações\n\n   ```bash\n   sh scripts/tracing.sh \u0026\u0026 sh scripts/requests.sh\n   ```\n\n   \u003e **Obrigatório**: é necessário instalar o pacote [siege](https://linux.die.net/man/1/siege)\n   \u003e\n   \u003e**Dica**: se preferir, remova comentários na parte de configuração do container do [Grafana K6](https://k6.io/) no `docker-compose.yml` ou através do Swagger UI disponível na rota **`/swagger-ui/index.html`**\n\n4. Acesse o Grafana através do endereço [localhost:3000](http://localhost:3000/) e vá até o painel `Aplicação`\n\n\u003e**Credenciais**: o login é `admin-a` e senha é `grafana`\n\n   Captura de tela exemplar:\n\n   ![Painel de Monitoramento](./images/dashboard.png)\n\n   \u003eEste painel predefinido para aplicações em Spring Boot está disponível no [Marketplace de dashboards do Grafana](https://grafana.com/grafana/dashboards/17175).\n\n## Explorando o Grafana\n\nGrafana fornece uma grande solução, no qual podemos monitorar ações específicas em um serviço entre rastreamentos, métricas e logs através do ID de rastreamento.\n\n![Correlação da Observabilidade](./images/observability-correlations.jpeg)\n\n\u003eFonte da imagem: [Grafana](https://grafana.com/blog/2021/03/31/intro-to-exemplars-which-enable-grafana-tempos-distributed-tracing-at-massive-scale/)\n\n### Métricas em Rastreamento\n\nObtenha o ID de rastreamento de um exemplo nas métricas, e então busque-a no Grafana Tempo.\n\nPesquise por ```histogram_quantile(.99,sum(rate(http_server_requests_seconds_bucket{application=\"app-a\", uri!=\"/actuator/prometheus\"}[1m])) by(uri, le))``` e converta em um exemplar nas opções.\n\n![Metrics to Traces](./images/metrics-to-traces.png)\n\n### Rastreamento em logs\n\nObtenha o ID de rastreamento e tags (que é `compose.service`) definida na fonte de dados do Grafana Tempo, e então faça a busca no Grafana Loki.\n\n![Traces to Logs](./images/traces-to-logs.png)\n\n### Logs em rastreamento\n\nObtenha o ID de rastreamento no log (RegExp estão habilitadas a partir da fonte de dados do Grafana Loki), e então pesquise através do Grafana Tempo.\n\n![Logs to Traces](./images/logs-to-traces.png)\n\n## Detalhes\n\n### Aplicação Spring Boot\n\nPara um cenário mais complexo, usaremos estas aplicações em Spring Boot com o mesmo código nesta demonstração. Há uma ação de múltiplos serviços na rota `/chain`, na qual fornece um bom exemplo de como a instrumentação do OpenTelemtry funciona e como o Grafana apresenta essa informação para rastreamento.\n\n#### Instrumentação com OpenTelemetry\n\n[OpenTelemetry para Java](https://github.com/open-telemetry/opentelemetry-java-instrumentation) possui um SDK para [instrumentação automática](https://opentelemetry.io/docs/instrumentation/java/automatic/):\n\n```bash\njava -javaagent:localização/do/opentelemetry-agent.jar -jar [nome da aplicação].jar\n```\n\nO agente suporta diversas [bibliotecas](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md), incluindo o Spring Web MVC. Segundo o documento traduzido do inglês:\n\n\u003e Pode ser usado para capturar dados de telemetria nos \"limites\" de uma aplicação ou serviço, tais como requisições de entrada, chamadas de saídas HTTP, chamadas ao banco de dados e muito mais\n\nConfiguração do OpenTelemetry\n\n```java\n// Fornecedor automatizado de logs, métricas e traces do OpenTelemetry\n\npackage com.example.app;\n\nimport io.prometheus.client.exemplars.tracer.otel_agent.OpenTelemetryAgentSpanContextSupplier;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class OpenTelemetryConfig {\n  @Bean\n  public OpenTelemetryAgentSpanContextSupplier OTelAgentSupplier() {\n    return new OpenTelemetryAgentSpanContextSupplier();\n  }\n}\n```\n\nEntão não precisamos modificar qualquer linha de código de nosso repositório. O agente se encarregará de tudo automaticamente. Neste projeto, temos três tipos de ações que podem ser capturadas pelo agente:\n\n1. Requisições HTTP: captura informações HTTP tais como método da requisição, status e entre outros.\n\n    ![Span data of HTTP requests](./images/span-data-http.png)\n\n2. Ações do PostgreSQL (POST `/peanuts` e a primeira requisição do GET `/peanuts/{id}`): captura informação do banco de dados como a declaração em SQL, nome da tabela e muito mais.\n\n    ![Span data of PostgreSQL actions](./images/span-data-postgresql.png)\n\n3. Comandos do Redis(Da segunda requisição GET `/peanuts/{id}`): captura informação do Redis como comandos, chaves e outros detalhes.\n\n    ![Span data of Redis commands](./images/span-data-redis.png)\n\nAs confurações como as do exportador, são listadas no [documento](https://github.com/open-telemetry/opentelemetry-java/tree/main/sdk-extensions/autoconfigure), e são utilizadas pelo agente a partir de um ou mais fontes seguintes (ordenadas da alta até a menor prioridade):\n\n- Propriedades do sistema\n- Variáveis de ambiente\n- O arquivo de configuração\n- O SPI do [ConfigPropertySource](https://github.com/open-telemetry/opentelemetry-java/tree/main/sdk-extensions/autoconfigure#customizing-the-opentelemetry-sdk)\n\nNeste projeto de apresentação, usaremos variáveis de ambiente para definir as configurações do agente:\n\n```yml\napp-a:\n  build: ./app/\n  environment:\n    - OTEL_EXPORTER_OTLP_ENDPOINT=http://tempo:4317\n    - OTEL_SERVICE_NAME=app-a\n    - OTEL_RESOURCE_ATTRIBUTES=compose_service=app-a\n    - OTEL_METRICS_EXPORTER=none\n  ports:\n    - 8080:8080\n```\n\nOu usando um arquivo de configuração, que é uma outra alternativa comum para configurar o agente:\n\n```properties\n# otel.properties\notel.exporter.otlp.endpoint=http://tempo:4317\notel.service.name=app-a\notel.resource.attributes=compose_service=app-a\notel.metrics.exporter=none\n```\n\n```bash\n# Defina o \"otel.javaagent.configuration-file\" com as propriedades do sistema\njava -javaagent:path/to/opentelemetry-javaagent.jar \\\n     -Dotel.javaagent.configuration-file=path/to/otel.properties \\\n     -jar myapp.jar\n```\n\nPara mais detalhes de configuração, consulte a [documentação oficial do OpenTelemetry](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/).\n\n#### Logs\n\nO agente do OpenTelemetry adicionará informação automaticamente para cada log.\n\n\u003eAgente do OpenTelemtry injeta a informação atual em cada evento de log da cópia do MDC (Mapped Diagnostic Context):\n\u003e\n\u003e - **`trace_id`** - o atual ID de rastreamento (equivalente ao Span.current().getSpanContext().getTraceId());\n\u003e - **`span_id`** - o atual ID (equivalente a **`Span.current().getSpanContext().getSpanId());`**)\n\u003e - **`trace_flags`** - flags de rastreamento atual, formatada de acordo com as regras de traceflags da W3C (equivalente a **`Span.current().getSpanContext().getTraceFlags().asHex()`**).\n\nReferências técnicas: [Instrumentação automática com Logger MDC](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/logger-mdc-instrumentation.md)\n\nNeste projeto demonstrativo, trocamos o `logging.pattern.level` no arquivo `application.yaml`:\n\n```yml\nlogging:\n  pattern:\n    level: \"trace_id=%mdc{trace_id} span_id=%mdc{span_id} trace_flags=%mdc{trace_flags} %p\"\n```\n\nO log terá sua saída neste formato:\n\n```log\n2022-10-10 15:18:54.893 trace_id=605c7adf03bdb2f2917206de1eae8f72 span_id=c581b882e2d252c2 trace_flags=01 ERROR 1 --- [nio-8080-exec-6] com.example.app.AppApplication           : Olá Java!\n```\n\n#### Rastreamento\n\nComo mencionado anteriromente, o agente do OpenTelemtry captura dados nos \"limites\" de uma aplicação ou serviço, como requisições de entradas e saída de chamadas HTTP. Não necessitamos de adicionar nada no código. Para demonstrar, providenciamos a rota `/chain` na aplicação a seguir:\n\n```java\n@GetMapping(\"/chain\")\npublic String chain() throws InterruptedException, IOException {\n  String TARGET_ONE_HOST = System.getenv().getOrDefault(\"TARGET_ONE_HOST\", \"localhost\");\n  String TARGET_TWO_HOST = System.getenv().getOrDefault(\"TARGET_TWO_HOST\", \"localhost\");\n  logger.debug(\"Cadeia inicializando ...\");\n  Request.Get(\"http://localhost:8080/\").execute().returnContent();\n  Request.Get(String.format(\"http://%s:8080/io_task\", TARGET_ONE_HOST)).execute().returnContent();\n  Request.Get(String.format(\"http://%s:8080/cpu_task\", TARGET_TWO_HOST)).execute().returnContent();\n  logger.debug(\"Cadeia encerrada\");\n  return \"chain\";\n}\n```\n\nAo acessar a rota `chain` do container `app-a`, irá enviar requisições para sua própria rota base (`/`) e outros dois serviços como o `io_task` e `cpu_task` em ordem. No processo todo, não digitamos nenhuma linha de código do OpenTelemetry, rastreamento ou até mesmo operação da aplicação. Mas o log mostra todas as requisições recebidas e saídas de chamada HTTP adicionadas a informação de operação a seguir:\n\n```log\n# Start from app-a chain\n2022-10-10 15:57:12.828 trace_id=743ae05db90d00fd65998ff30cf7094d span_id=6cc84ac5ed4cf68c trace_flags=01 DEBUG 1 --- [nio-8080-exec-6] com.example.app.AppApplication           : Cadeia inicializando ...\n\n# In app-a root\n2022-10-10 15:57:13.106 trace_id=743ae05db90d00fd65998ff30cf7094d span_id=4745d1a1f588a949 trace_flags=01 ERROR 1 --- [nio-8080-exec-7] com.example.app.AppApplication           : [traceparent:\"00-743ae05db90d00fd65998ff30cf7094d-d72a1422522ce837-01\", host:\"localhost:8080\", connection:\"Keep-Alive\", user-agent:\"Apache-HttpClient/4.5.13 (Java/1.8.0_342)\", accept-encoding:\"gzip,deflate\"]\n2022-10-10 15:57:13.106 trace_id=743ae05db90d00fd65998ff30cf7094d span_id=4745d1a1f588a949 trace_flags=01 ERROR 1 --- [nio-8080-exec-7] com.example.app.AppApplication           : Olá Java!\n2022-10-10 15:57:13.106 trace_id=743ae05db90d00fd65998ff30cf7094d span_id=4745d1a1f588a949 trace_flags=01 DEBUG 1 --- [nio-8080-exec-7] com.example.app.AppApplication           : Log de depuração\n2022-10-10 15:57:13.106 trace_id=743ae05db90d00fd65998ff30cf7094d span_id=4745d1a1f588a949 trace_flags=01 INFO 1 --- [nio-8080-exec-7] com.example.app.AppApplication           : Log de informação\n2022-10-10 15:57:13.106 trace_id=743ae05db90d00fd65998ff30cf7094d span_id=4745d1a1f588a949 trace_flags=01 WARN 1 --- [nio-8080-exec-7] com.example.app.AppApplication           : Log de aviso!\n2022-10-10 15:57:13.106 trace_id=743ae05db90d00fd65998ff30cf7094d span_id=4745d1a1f588a949 trace_flags=01 ERROR 1 --- [nio-8080-exec-7] com.example.app.AppApplication           : Log de erro\n\n# In app-b io_task\n2022-10-10 15:57:14.141 trace_id=743ae05db90d00fd65998ff30cf7094d span_id=b97df0b1834ab84a trace_flags=01 INFO 1 --- [nio-8080-exec-4] com.example.app.AppApplication           : Tarefa I/O\n\n# In app-c cpu_task\n2022-10-10 15:57:14.191 trace_id=743ae05db90d00fd65998ff30cf7094d span_id=7fd693eefc0d3387 trace_flags=01 INFO 1 --- [nio-8080-exec-4] com.example.app.AppApplication           : Tarefa da CPU\n\n# Back to app-a chain\n2022-10-10 15:57:14.199 trace_id=743ae05db90d00fd65998ff30cf7094d span_id=6cc84ac5ed4cf68c trace_flags=01 DEBUG 1 --- [nio-8080-exec-6] com.example.app.AppApplication           : Cadeia encerrada\n```\n\nCada rota possui a mesma propriedade **trace_id** como `743ae05db90d00fd65998ff30cf7094d` inicializando a partir do container `app-a` na rota `chain`. A auto-injeção do `traceparent` (pode ser vista na primeira linha nos logs do container `app-a`) é como o agnete do OpenTelemetry transmite através destes serviços\n\nEach endpoint got the same trace_id `743ae05db90d00fd65998ff30cf7094d` start from app-a `chain`. The auto-injected `traceparent`(could saw in the first line in app-a root log) is how OpenTelemetry Agent passed through all these services.\n\n#### Métricas\n\nPara obter as métricas do Prometheus vindas da aplicação, precisamos de duas dependências:\n\n1. [Spring Boot Actuator](https://github.com/spring-projects/spring-boot/tree/v2.7.4/spring-boot-project/spring-boot-actuator):\nO Actuator fornece muitas funcionalidades para monitoramente por HTTP ou rotas JMX para aplicações em Spring Boot.\n2. [Micrometer](https://github.com/micrometer-metrics/micrometer):\nMicrometer fornece uma API geral para coletar métricas e transforma o formato para diferentes sistemas de monitoramento, incluindo o Prometheus.\n\nAdicione estas duas dependências para o arquivo `pom.xml` e configuração para o `application.yaml` conforme o exemplo abaixo:\n\n```xml\n\u003c!-- pom.xml --\u003e\n\n\u003cdependency\u003e\n   \u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e\n   \u003cartifactId\u003espring-boot-starter-actuator\u003c/artifactId\u003e\n\u003c/dependency\u003e\n\u003cdependency\u003e\n   \u003cgroupId\u003eio.micrometer\u003c/groupId\u003e\n   \u003cartifactId\u003emicrometer-registry-prometheus\u003c/artifactId\u003e\n\u003c/dependency\u003e\n```\n\n```yml\n# application.yaml\n\nmanagement:\n  endpoints:\n    web:\n      exposure:\n        include: prometheus # Apenas para exposição da rota \"/actuator/prometheus\"\n\n  metrics:\n    tags:\n      application: app # Adicione uma tag para cada métrica do Prometheus\n```\n\nMétricas do Prometheus parecerão como isto na rota `/actuator/prometheus`:\n\n```txt\n# HELP executor_active_threads The approximate number of threads that are actively executing tasks\n# TYPE executor_active_threads gauge\nexecutor_active_threads{application=\"app\",name=\"applicationTaskExecutor\",} 0.0\n[...]\n\n HELP http_server_requests_seconds Duration of HTTP server request handling\n# TYPE http_server_requests_seconds summary\nhttp_server_requests_seconds_count{application=\"app\",exception=\"None\",method=\"GET\",outcome=\"SUCCESS\",status=\"200\",uri=\"/\",} 1.0\nhttp_server_requests_seconds_sum{application=\"app\",exception=\"None\",method=\"GET\",outcome=\"SUCCESS\",status=\"200\",uri=\"/\",} 0.047062542\nhttp_server_requests_seconds_count{application=\"app\",exception=\"None\",method=\"GET\",outcome=\"SUCCESS\",status=\"200\",uri=\"/actuator/prometheus\",} 2.0\nhttp_server_requests_seconds_sum{application=\"app\",exception=\"None\",method=\"GET\",outcome=\"SUCCESS\",status=\"200\",uri=\"/actuator/prometheus\",} 0.053801375\n# HELP http_server_requests_seconds_max Duration of HTTP server request handling\n# TYPE http_server_requests_seconds_max gauge\nhttp_server_requests_seconds_max{application=\"app\",exception=\"None\",method=\"GET\",outcome=\"SUCCESS\",status=\"200\",uri=\"/\",} 0.047062542\nhttp_server_requests_seconds_max{application=\"app\",exception=\"None\",method=\"GET\",outcome=\"SUCCESS\",status=\"200\",uri=\"/actuator/prometheus\",} 0.045745625\n\n[...]\n```\n\n##### Métricas com Exemplar\n\nExemplar é um novo tipo de dado proposto no [OpenMetrics](https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#exemplars). Para habilitar a funcionalidade do Exemplar, há alguns requisitos de dependências:\n\n1. Spring Boot em versão igual ou superior a **`2.7.0`**: Spring Boot suporta o Exemplar do Prometheus desde a versão [v2.7.0-RC1](https://github.com/spring-projects/spring-boot/releases/tag/v2.7.0-RC1).\n2. Micrometer em versão igual ou superior a **`1.10.0`**: Micrometer suporta Exemplar para o Histogramas e Contadores do Prometheus desde a versão [1.9.0](https://github.com/micrometer-metrics/micrometer/releases/tag/v1.9.0) e usando `io.prometheus.simpleclient_common` em versão 0.16.0 desde a versão [1.10.0](https://mvnrepository.com/artifact/io.micrometer/micrometer-registry-prometheus/1.10.0).\n\nAlém disso, necessitaremos de adicionar uma amostra do Exemplar [Exemplar Sampler](app/src/main/java/com/example/app/PrometheusExemplarSamplerConfiguration.java) (Fonte: [qaware/cloud-observability-grafana-spring-boot](https://github.com/qaware/cloud-observability-grafana-spring-boot/blob/b331b87b1a7f0f5b5d57150e0356e6a26af967a2/spring-boot-app/src/main/java/de/qaware/demo/cloudobservability/PrometheusExemplarSamplerConfiguration.java)) como mostra a seguir:\n\n```java\npackage com.example.app;\n\nimport io.prometheus.client.exemplars.tracer.otel_agent.OpenTelemetryAgentSpanContextSupplier;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class PrometheusExemplarSamplerConfiguration {\n  @Bean\n  public OpenTelemetryAgentSpanContextSupplier openTelemetryAgentSpanContextSupplier() {\n    return new OpenTelemetryAgentSpanContextSupplier();\n  }\n}\n```\n\nA dicussão sobre uma amostra do Exemplar está em [Suporte de Exemplares para o Histograma do Prometheus #2812](https://github.com/micrometer-metrics/micrometer/issues/2812#issuecomment-1086001766) no repositório do Micrometer.\n\nQuando todas as dependências estiverem adicionadas, podemos adicionar distribuição de métrica para o Prometheus no arquivo `application.yaml`.\n\n```yml\nmanagement:\n  metrics:\n    distribution:\n      percentiles-histogram:\n        http:\n          server:\n            requests: 'true'\n```\n\nVerifique por mais opções de distribuição de métricas na [documentação oficial do Spring Boot](https://docs.spring.io/spring-boot/docs/2.7.3/reference/html/actuator.html#actuator.metrics.customizing.per-meter-properties).\n\nComo mencionado anteriormente, Exemplar é um novo tipo de dado proposto no OpenMetrics e o padrão da rota `/actuator/prometheus` para fornecer métricas com o formato do Prometheus. Então necessitamos adicionar alguns [cabeçalhos](https://docs.spring.io/spring-boot/docs/current/actuator-api/htmlsingle/#prometheus.retrieving:~:text=The%20default%20response%20content%20type%20is%20text/plain%3Bversion%3D0.0.4.%20The%20endpoint%20can%20also%20produce%20application/openmetrics%2Dtext%3Bversion%3D1.0.0%20when%20called%20with%20an%20appropriate%20Accept%20header%2C%20as%20shown%20in%20the%20following%20curl%2Dbased%20example%3A) para obter as métricas com o formato do OpenMetrics como mostrar a seguir:\n\n```bash\ncurl 'http://localhost:8080/actuator/prometheus' -i -X GET \\\n   -H 'Accept: application/openmetrics-text; version=1.0.0; charset=utf-8'\n```\n\nAs métricas do histograma com ID de rastreamento (que começam com `#`), irão parecer como isto:\n\n```txt\n# TYPE http_server_requests_seconds histogram\n# HELP http_server_requests_seconds Duration of HTTP server request handling\nhttp_server_requests_seconds_bucket{application=\"app\",exception=\"None\",method=\"GET\",outcome=\"SUCCESS\",status=\"200\",uri=\"/\",le=\"0.001\"} 0.0\nhttp_server_requests_seconds_bucket{application=\"app\",exception=\"None\",method=\"GET\",outcome=\"SUCCESS\",status=\"200\",uri=\"/\",le=\"0.001048576\"} 0.0\nhttp_server_requests_seconds_bucket{application=\"app\",exception=\"None\",method=\"GET\",outcome=\"SUCCESS\",status=\"200\",uri=\"/\",le=\"0.001398101\"} 0.0\nhttp_server_requests_seconds_bucket{application=\"app\",exception=\"None\",method=\"GET\",outcome=\"SUCCESS\",status=\"200\",uri=\"/\",le=\"0.001747626\"} 0.0\nhttp_server_requests_seconds_bucket{application=\"app\",exception=\"None\",method=\"GET\",outcome=\"SUCCESS\",status=\"200\",uri=\"/\",le=\"0.002097151\"} 0.0\nhttp_server_requests_seconds_bucket{application=\"app\",exception=\"None\",method=\"GET\",outcome=\"SUCCESS\",status=\"200\",uri=\"/\",le=\"0.002446676\"} 0.0\nhttp_server_requests_seconds_bucket{application=\"app\",exception=\"None\",method=\"GET\",outcome=\"SUCCESS\",status=\"200\",uri=\"/\",le=\"0.002796201\"} 1.0 # {span_id=\"55255da260e873d9\",trace_id=\"21933703cb442151b1cef583714eb42e\"} 0.002745959 1665676383.654\nhttp_server_requests_seconds_bucket{application=\"app\",exception=\"None\",method=\"GET\",outcome=\"SUCCESS\",status=\"200\",uri=\"/\",le=\"0.003145726\"} 1.0\nhttp_server_requests_seconds_bucket{application=\"app\",exception=\"None\",method=\"GET\",outcome=\"SUCCESS\",status=\"200\",uri=\"/\",le=\"0.003495251\"} 2.0 # {span_id=\"81222a08c4f050fe\",trace_id=\"eddcd9569d20b4aa48c06d3b905f32ea\"} 0.003224625 1665676382.620\n```\n\n### Prometheus - Métricas\n\nColeta métricas das aplicações e expõe em uma rota\n\n#### Configuração do Prometheus\n\nDefina todo o rascunho/scrape de métricas de todas as aplicações no arquivo localizado em `./configs/prometheus/prometheus.yml`.\n\nPrometheus irá extrair automaticamente o formato de métricas do OpenMetrics, e não há necessidade de adicionar configuração especifica de cabeçalhos quando extraindo da rota `/actuator/prometheus`.\n\n```yml\n# [...]\nscrape_configs:\n  - job_name: 'app-a'\n    scrape_interval: 5s\n    metrics_path: \"/actuator/prometheus\"\n    static_configs:\n      - targets: ['app-a:8080']\n\n  - job_name: 'app-b'\n    scrape_interval: 5s\n    metrics_path: \"/actuator/prometheus\"\n    static_configs:\n      - targets: ['app-b:8080']\n\n  - job_name: 'app-c'\n    scrape_interval: 5s\n    metrics_path: \"/actuator/prometheus\"\n    static_configs:\n      - targets: ['app-c:8080']\n```\n\n#### Transmitindo para o Grafana\n\nAdicione um Exemplar no qual usa o valor do rótulo `TraceID` para vincular ao Grafana Tempo.\n\nExemplo de configuração de transmissão de dados ao Grafana:\n\n![Data Source of Prometheus: Exemplars](./images/prometheus-exemplars.png)\n\nExemplo de configuração predefinida de fonte de dados do Grafana:\n\n```yaml\nname: Prometheus\ntype: prometheus\ntypeName: Prometheus\naccess: proxy\nurl: http://prometheus:9090\npassword: ''\nuser: ''\ndatabase: ''\nbasicAuth: false\nisDefault: true\njsonData:\nexemplarTraceIdDestinations:\n  - datasourceUid: tempo\n    name: TraceID\nhttpMethod: POST\nreadOnly: false\neditable: true\n```\n\n### Grafana Tempo - Rastreamento\n\nReceptor de operações das aplicações\n\n#### Transmitindo para o Grafana\n\nConfiguração de [rastreamento em logs](https://grafana.com/docs/grafana/latest/datasources/tempo/#trace-to-logs):\n\n1. Fonte de dados: fonte do log alvo.\n2. Tags: Chave de tags ou atributos em nível de processo vindo do rastreamento, no qual irá ser o critério de busca de log se a chave existir no rastreamento.\n\n3. Mapeamento de nomes de tag: converte uma chave existente de tags ou atributos em nível de processo do rastreamento para outra chave, e então usado como critério de busca de log. Use esta funcionalidade quando os valores de tag de rastreamento e rótulo do log são idênticos mas com chaves diferentes.\n\nExemplo de configuração predefinida de fonte de dados para o Grafana:\n\n![Data Source of Tempo: Trace to logs](./images/tempo-trace-to-logs.png)\n\nExemplo de configuração de fonte de dados do Grafana:\n\n```yaml\nname: Tempo\ntype: tempo\ntypeName: Tempo\naccess: proxy\nurl: http://tempo\npassword: ''\nuser: ''\ndatabase: ''\nbasicAuth: false\nisDefault: false\njsonData:\nnodeGraph:\n   enabled: true\ntracesToLogs:\n   datasourceUid: loki\n   filterBySpanID: false\n   filterByTraceID: true\n   mapTagNamesEnabled: false\n   tags:\n      - compose_service\nreadOnly: false\neditable: true\n```\n\n### Grafana Loki - Logs\n\nCentralizador de logs com o plugin do Docker de todos os serviços\n\n#### Driver do Docker\n\n1. Use a funcionalidade de [ancoramento e apelidação no YAML](https://support.atlassian.com/bitbucket-cloud/docs/yaml-anchors/) para definir opções de log em cada serviço.\n2. Defina as [opções do driver do Grafana Loki no Docker](https://grafana.com/docs/loki/latest/send-data/docker-driver/configuration/)\n   1. loki-url: endereço do Grafana Loki\n   2. loki-pipeline-stages: processa múltiplas linhas de log da aplicação com estágios de múltiplas linhas de RegExp ([referência](https://grafana.com/docs/loki/latest/send-data/promtail/stages/multiline/))\n\n```yml\nversion: '3.9'\n\nx-logging: \u0026default-logging # ancôra(\u0026): 'default-logging' que define um pedaço/parte (chunk) da configuração\n  driver: loki\n  options:\n    loki-url: http://loki:3100/api/prom/push\n    loki-pipeline-stages: |\n      - multiline:\n          firstline: '^\\d{4}-\\d{2}-\\d{2} \\d{1,2}:\\d{2}:\\d{2}'\n          max_wait_time: 3s\n      - regex:\n          expression: '^(?P\u003ctime\u003e\\d{4}-\\d{2}-\\d{2} \\d{1,2}:\\d{2}:\\d{2},d{3}) (?P\u003cmessage\u003e(?s:.*))$$'\n# Use os caracteres $$ (símbolo do dólar) quando sua configuração necessita de um símbolo literal do dólar.\n\nservices:\n  container-1:\n    image: nginx:latest\n    logging: *default-logging # apelido(*): refere-se a parte chamada 'default-logging' \n```\n\n#### Transmitindo para o Grafana\n\nAdiciona um ID de rastreamento derivado do campo para extrair o ID e vincular ao Grafana Tempo a partir deste mesmo ID.\n\nConfiguração exemplar de fonte de dados do Grafana Tempo:\n\n![Data Source of Loki: Derived fields](./images/loki-derive-filed.png)\n\nExemplo de configuração predefinida do Grafana Tempo:\n\n```yml\nname: Loki\ntype: loki\ntypeName: Loki\naccess: proxy\nurl: http://loki:3100\npassword: ''\nuser: ''\ndatabase: ''\nbasicAuth: false\nisDefault: false\njsonData:\nderivedFields:\n   - datasourceUid: tempo\n      matcherRegex: (?:trace_id)=(\\w+)\n      name: TraceID\n      url: $${__value.raw}\n      # Use os caracteres $$ (símbolo do dólar) quando sua configuração necessita de um símbolo literal do dólar.\nreadOnly: false\neditable: true\n```\n\n### Grafana\n\n1. Adicione o Prometheus, Tempo e Loki para a fonte de dados com o arquivo de configuração no caminho `./configs/grafana/datasource.yml`.\n\n2. Carregue um painel predefinido a partir dos arquivos localizados em `.configs/grafana/dashboards.yaml` e também `./configs/grafana/dashboards/spring-boot-observability.json`\n\n```yml\n# Configuração individual do container do Grafana no docker-compose.yml\n\n  grafana:\n    container_name: grafana\n    build: ./configs/grafana/\n    user: root\n    ports:\n      - 3000:3000\n    volumes:\n      - ./configs/grafana/datasource.yml:/usr/share/grafana/conf/provisioning/datasources/datasources.yml # Fonte de dados\n      - ./configs/grafana/dashboards.yml:/usr/share/grafana/conf/provisioning/dashboards/dashboards.yml # Configurações do painel\n      - ./configs/grafana/dashboards:/usr/share/grafana/dashboards # Diretório de arquivos JSON de painéis\n```\n\n## Referências\n\n1. [Repositório base](https://github.com/blueswen/spring-boot-observability)\n2. [Suporte de Histograma do Prometheus para Exemplar](https://github.com/micrometer-metrics/micrometer/issues/2812)\n3. [Auto-configuração do SDK do OpenTelemetry](https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/README.md)\n4. [Propriedades sistêmicas e variáveis de ambiente do Java](https://stackoverflow.com/questions/7054972/java-system-properties-and-environment-variables)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ft0malexander%2Fobservabilidade","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ft0malexander%2Fobservabilidade","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ft0malexander%2Fobservabilidade/lists"}