Skip to content

Commit

Permalink
Merge pull request #83 from fathiraz/docs/add-example-of-metric-feature
Browse files Browse the repository at this point in the history
docs: add example of metric feature
  • Loading branch information
ilhamsyahids authored Dec 11, 2024
2 parents ac30f30 + 1e7c272 commit 518ed46
Show file tree
Hide file tree
Showing 9 changed files with 219 additions and 6 deletions.
12 changes: 11 additions & 1 deletion examples/multi-services/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,17 @@ There are 2 services in this example:

![Architecture Diagram](architecture.svg)

All traces will be collected in [Jaeger](https://www.jaegertracing.io/).
All traces will be collected in [Jaeger](https://www.jaegertracing.io/), and metrics will be collected in [Prometheus](https://prometheus.io/).

## Observability Components

This example demonstrates a complete observability setup with:

- **Distributed Tracing**: Using Jaeger to collect and visualize distributed traces across services
- **Metrics**: Using Prometheus to collect and monitor metrics from both services
- **Visualization**:
- Jaeger UI for trace visualization (`http://localhost:16686`)
- Prometheus UI for metrics querying and visualization (`http://localhost:9090`)

## How to Run

Expand Down
15 changes: 14 additions & 1 deletion examples/multi-services/back-svc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/go-chi/chi/v5"
"github.com/riandyrn/otelchi"
"github.com/riandyrn/otelchi/examples/multi-services/utils"
otelchimetric "github.com/riandyrn/otelchi/metric"
"go.opentelemetry.io/otel/trace"
)

Expand All @@ -25,9 +26,21 @@ func main() {
if err != nil {
log.Fatalf("unable to initialize tracer provider due: %v", err)
}

// setup metrics
metricConfig, err := utils.NewMetricConfig(serviceName)
if err != nil {
log.Fatalf("unable to initialize metrics due: %v", err)
}

// define router
r := chi.NewRouter()
r.Use(otelchi.Middleware(serviceName, otelchi.WithChiRoutes(r)))
r.Use(
otelchi.Middleware(serviceName, otelchi.WithChiRoutes(r)),
otelchimetric.NewRequestDurationMillis(metricConfig),
otelchimetric.NewRequestInFlight(metricConfig),
otelchimetric.NewResponseSizeBytes(metricConfig),
)
r.Get("/", utils.HealthCheckHandler)
r.Get("/name", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(generateName(r.Context(), tracer)))
Expand Down
53 changes: 50 additions & 3 deletions examples/multi-services/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ services:
condition: service_healthy
jaeger:
condition: service_started
prometheus:
condition: service_started
otel-collector:
condition: service_started

front-svc:
build:
Expand All @@ -22,9 +26,17 @@ services:
healthcheck:
test: "curl -XGET http://localhost:8090"
start_period: 5s
depends_on:
otel-collector:
condition: service_started
environment:
- BACK_SERVICE_URL=http://back-svc:8091
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318
- OTEL_EXPORTER_OTLP_PROTOCOL=grpc
- OTEL_EXPORTER_OTLP_ENDPOINT=otel-collector:4317
- OTEL_EXPORTER_OTLP_METRICS_PROTOCOL=http/protobuf
- OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://otel-collector:4318/v1/metrics
- OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/protobuf
- OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://otel-collector:4318/v1/traces
ports:
- 8090:8090

Expand All @@ -40,17 +52,52 @@ services:
healthcheck:
test: "curl -XGET http://localhost:8091"
start_period: 5s
depends_on:
otel-collector:
condition: service_started
environment:
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318
- OTEL_EXPORTER_OTLP_PROTOCOL=grpc
- OTEL_EXPORTER_OTLP_ENDPOINT=otel-collector:4317
- OTEL_EXPORTER_OTLP_METRICS_PROTOCOL=http/protobuf
- OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://otel-collector:4318/v1/metrics
- OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/protobuf
- OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://otel-collector:4318/v1/traces
ports:
- 8091:8091

otel-collector:
# otel-collector to collect all our monitoring data
image: otel/opentelemetry-collector-contrib:0.115.1
command: ["--config=/etc/otel-collector-config.yaml"]
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
ports:
- "4317:4317" # OTLP gRPC receiver
- "4318:4318" # OTLP http receiver
- "8888:8888" # metrics endpoint
- "8889:8889" # Prometheus exporter
logging:
driver: "none"

jaeger:
image: jaegertracing/all-in-one:1.56
environment:
- COLLECTOR_OTLP_ENABLED=true
- LOG_LEVEL=debug
ports:
- 16686:16686 # for serving jaeger front-end
- 4318:4318 # for receiving OTLP data
- 6831:6831/udp # for jaeger-agent UDP
logging:
driver: none # intentionally turnoff the logging so it doesn't clutter the terminal

prometheus:
# prometheus to collect all our metrics
image: prom/prometheus:v3.0.1
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
command:
- '--config.file=/etc/prometheus/prometheus.yml'
ports:
- "9090:9090"
logging:
driver: "none"
15 changes: 14 additions & 1 deletion examples/multi-services/front-svc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/go-chi/chi/v5"
"github.com/riandyrn/otelchi"
"github.com/riandyrn/otelchi/examples/multi-services/utils"
otelchimetric "github.com/riandyrn/otelchi/metric"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
Expand All @@ -28,9 +29,21 @@ func main() {
if err != nil {
log.Fatalf("unable to initialize tracer due: %v", err)
}

// setup metrics
metricConfig, err := utils.NewMetricConfig(serviceName)
if err != nil {
log.Fatalf("unable to initialize metrics due: %v", err)
}

// define router
r := chi.NewRouter()
r.Use(otelchi.Middleware(serviceName, otelchi.WithChiRoutes(r)))
r.Use(
otelchi.Middleware(serviceName, otelchi.WithChiRoutes(r)),
otelchimetric.NewRequestDurationMillis(metricConfig),
otelchimetric.NewRequestInFlight(metricConfig),
otelchimetric.NewResponseSizeBytes(metricConfig),
)
r.Get("/", utils.HealthCheckHandler)
r.Get("/greet", func(w http.ResponseWriter, r *http.Request) {
name, err := getRandomName(r.Context(), tracer)
Expand Down
2 changes: 2 additions & 0 deletions examples/multi-services/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ require (
github.com/riandyrn/otelchi v0.11.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0
go.opentelemetry.io/otel v1.32.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0
go.opentelemetry.io/otel/sdk v1.32.0
go.opentelemetry.io/otel/sdk/metric v1.32.0
go.opentelemetry.io/otel/trace v1.32.0
)

Expand Down
4 changes: 4 additions & 0 deletions examples/multi-services/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuH
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0 h1:t/Qur3vKSkUCcDVaSumWF2PKHt85pc7fRvFuoVT8qFU=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0/go.mod h1:Rl61tySSdcOJWoEgYZVtmnKdA0GeKrSqkHC1t+91CH8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 h1:IJFEoHiytixx8cMiVAO+GmHR6Frwu+u5Ur8njpFO6Ac=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0/go.mod h1:3rHrKNtLIoS0oZwkY2vxi+oJcwFRWdtUyRII+so45p8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 h1:cMyu9O88joYEaI47CnQkxO1XZdpoTF9fEnW2duIddhw=
Expand All @@ -33,6 +35,8 @@ go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZk
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
Expand Down
45 changes: 45 additions & 0 deletions examples/multi-services/otel-collector-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# This is where we set up our OpenTelemetry collector

receivers:
# setting up how we receive data from our services
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318

processors:
# bundle up the data before sending it out
batch:

exporters:
# send metrics to otlp
prometheus:
endpoint: "0.0.0.0:8889"
namespace: "otelchi"
const_labels:
environment: development
cluster: local

# send traces to OTLP
otlp/jaeger:
endpoint: jaeger:4317
tls:
insecure: true

# keep some logs around for when we need to debug stuff
debug:
verbosity: detailed

service:
# here is where everything sets
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp/jaeger, debug]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [prometheus, debug]
17 changes: 17 additions & 0 deletions examples/multi-services/prometheus.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
global:
# check on our services every 15 seconds
scrape_interval: 15s
# how often we should check if anything's wrong
evaluation_interval: 15s

scrape_configs:
# get metrics from our collector
# it's already done all the hard work of gathering everything
- job_name: 'otel-collector'
static_configs:
- targets: ['otel-collector:8889']
# only grab metrics with 'otelchi'
metric_relabel_configs:
- source_labels: [__name__]
regex: '.*otelchi.*'
action: keep
62 changes: 62 additions & 0 deletions examples/multi-services/utils/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package utils

import (
"context"
"go.opentelemetry.io/otel"
"time"

otelchimetric "github.com/riandyrn/otelchi/metric"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
)

// NewMetricConfig creates metric configuration that includes:
// - Request Duration Metrics: measures the latency of HTTP requests
// - Request Inflight Metrics: tracks the number of concurrent requests
// - Response Size Metrics: measures the size of HTTP responses
func NewMetricConfig(serviceName string) (otelchimetric.BaseConfig, error) {
// create context
ctx := context.Background()

// create otlp exporter using HTTP protocol. the endpoint will be loaded from
// OTEL_EXPORTER_OTLP_METRICS_ENDPOINT environment variable
exporter, err := otlpmetrichttp.New(
ctx,
otlpmetrichttp.WithInsecure(),
)
if err != nil {
return otelchimetric.BaseConfig{}, err
}

// create resource with service name
res, err := resource.New(
ctx,
resource.WithAttributes(
semconv.ServiceName(serviceName),
),
)
if err != nil {
return otelchimetric.BaseConfig{}, err
}

// create meter provider with otlp exporter
meterProvider := sdkmetric.NewMeterProvider(
sdkmetric.WithResource(res),
sdkmetric.WithReader(
sdkmetric.NewPeriodicReader(
exporter,
sdkmetric.WithInterval(15*time.Second),
),
),
)

// set global meter provider
otel.SetMeterProvider(meterProvider)

// create and return base config for metrics with meter provider
return otelchimetric.NewBaseConfig(serviceName,
otelchimetric.WithMeterProvider(meterProvider),
), nil
}

0 comments on commit 518ed46

Please sign in to comment.