Skip to content

Instantly share code, notes, and snippets.

@hagen1778
Created October 10, 2024 13:26
Show Gist options
  • Save hagen1778/08aa3b4679654ae20d756010d5c6bc42 to your computer and use it in GitHub Desktop.
Save hagen1778/08aa3b4679654ae20d756010d5c6bc42 to your computer and use it in GitHub Desktop.
app insturemented with otel exp histogram for https://github.com/VictoriaMetrics/VictoriaMetrics/pull/6354
package main
import (
"context"
"fmt"
"go.opentelemetry.io/otel/attribute"
"io/ioutil"
"log"
"net/http"
"time"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
"go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
"go.opentelemetry.io/otel/metric"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
var meter = otel.Meter("go.opentelemetry.io/contrib/examples/otel-collector")
type metrics struct {
requests metric.Int64Counter
duration metric.Float64Histogram
}
var m metrics
func main() {
ctx := context.Background()
shutdownMeterProvider := initMetricProvider(ctx)
defer func() {
if err := shutdownMeterProvider(ctx); err != nil {
log.Fatalf("failed to shutdown MeterProvider: %s", err)
}
}()
var err error
m.requests, err = meter.Int64Counter("service.requests.total", metric.WithDescription("The number of served requests"))
if err != nil {
log.Fatal(err)
}
m.duration, err = meter.Float64Histogram(
"request.duration",
metric.WithDescription("a histogram with custom buckets and rename"),
)
if err != nil {
log.Fatal(err)
}
http.Handle("/", handle(home))
http.HandleFunc("/articles", handle(articles))
log.Fatal(http.ListenAndServe(":8080", nil))
}
func handle(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
attrs := metric.WithAttributes(attribute.String("path", r.URL.Path))
m.requests.Add(context.Background(), 1, attrs)
h.ServeHTTP(w, r)
m.duration.Record(context.Background(), time.Since(start).Seconds(), attrs)
}
}
func home(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Welcome!")
}
func articles(w http.ResponseWriter, r *http.Request) {
resp, err := http.DefaultClient.Get("https://duckduckgo.com/?t=h_&q=observability")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
data, _ := ioutil.ReadAll(resp.Body)
fmt.Fprint(w, string(data))
}
func initMetricProvider(ctx context.Context) func(context.Context) error {
conn, err := initConn()
if err != nil {
log.Fatal(err)
}
res, err := resource.New(ctx,
resource.WithAttributes(
// The service name used to display traces in backends
semconv.ServiceNameKey.String("test-service"),
semconv.ServiceInstanceID("test-service"),
semconv.ServerAddressKey.String("test-service"),
semconv.ServiceNamespace("test-service"),
),
)
if err != nil {
log.Fatal(err)
}
metricExporter, err := otlpmetricgrpc.New(ctx, otlpmetricgrpc.WithGRPCConn(conn))
if err != nil {
log.Fatalf("failed to create metrics exporter: %s", err)
}
stdoutMetricExporter, err := stdoutmetric.New(stdoutmetric.WithPrettyPrint())
if err != nil {
log.Fatalf("Failed to create stdout metric exporter: %v", err)
}
exponentialHistogramView := sdkmetric.NewView(
// The instrument to which this view is attached.
sdkmetric.Instrument{
Name: "request.duration",
Kind: sdkmetric.InstrumentKindHistogram,
},
// The aggregation to use for this view.
sdkmetric.Stream{
Name: "request.duration.exp",
Aggregation: sdkmetric.AggregationBase2ExponentialHistogram{
MaxSize: 160, // Maximum number of buckets.
MaxScale: 20, // Maximum resolution scale.
},
},
)
meterProvider := sdkmetric.NewMeterProvider(
sdkmetric.WithReader(sdkmetric.NewPeriodicReader(metricExporter, sdkmetric.WithInterval(5*time.Second))),
sdkmetric.WithReader(sdkmetric.NewPeriodicReader(stdoutMetricExporter, sdkmetric.WithInterval(5*time.Second))),
sdkmetric.WithResource(res),
sdkmetric.WithView(exponentialHistogramView),
)
otel.SetMeterProvider(meterProvider)
return meterProvider.Shutdown
}
// Initialize a gRPC connection to be used by both the tracer and meter
// providers.
func initConn() (*grpc.ClientConn, error) {
// It connects the OpenTelemetry Collector through local gRPC connection.
// You may replace `localhost:4317` with your endpoint.
conn, err := grpc.NewClient("localhost:4317",
// Note the use of insecure transport here. TLS is recommended in production.
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
return nil, fmt.Errorf("failed to create gRPC connection to collector: %w", err)
}
return conn, err
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment