example/metric/exponential_histogram/example.go (250 lines of code) (raw):

// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "context" "log" "math/rand" "os" "os/signal" "syscall" "time" "gonum.org/v1/gonum/stat" "gonum.org/v1/gonum/stat/distuv" mexporter "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric" "go.opentelemetry.io/contrib/detectors/gcp" "go.opentelemetry.io/otel/attribute" "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.21.0" ) func main() { ctx := context.Background() // Resource for GCP and SDK detectors res, err := resource.New(ctx, resource.WithDetectors(gcp.NewDetector()), resource.WithTelemetrySDK(), resource.WithAttributes( semconv.ServiceNameKey.String("example-application"), ), ) if err != nil { log.Fatalf("resource.New: %v", err) } // Create exporter pipeline exporter, err := mexporter.New() if err != nil { log.Fatalf("Failed to create exporter: %v", err) } clabels := []attribute.KeyValue{attribute.Key("key").String("value")} defaultBuckets := []float64{0, 5, 10, 25, 50, 75, 100, 250, 500, 1000} linearBuckets := make([]float64, 35) for i := 0; i < 35; i++ { linearBuckets[i] = float64(i*10 + 10) } viewLatencyA := sdkmetric.NewView( sdkmetric.Instrument{ Name: "latency", }, sdkmetric.Stream{ Name: "latency_a", Aggregation: sdkmetric.AggregationBase2ExponentialHistogram{ MaxSize: 160, MaxScale: 20, }, }, ) viewLatencyB := sdkmetric.NewView( sdkmetric.Instrument{ Name: "latency", }, sdkmetric.Stream{ Name: "latency_b", Aggregation: sdkmetric.AggregationExplicitBucketHistogram{ Boundaries: linearBuckets, }, }, ) viewLatencyC := sdkmetric.NewView( sdkmetric.Instrument{ Name: "latency", }, sdkmetric.Stream{ Name: "latency_c", Aggregation: sdkmetric.AggregationExplicitBucketHistogram{ Boundaries: defaultBuckets, }, }, ) viewLatencyShiftedA := sdkmetric.NewView( sdkmetric.Instrument{ Name: "latency_shifted", }, sdkmetric.Stream{ Name: "latency_shifted_a", Aggregation: sdkmetric.AggregationBase2ExponentialHistogram{ MaxSize: 160, MaxScale: 20, }, }, ) viewLatencyShiftedB := sdkmetric.NewView( sdkmetric.Instrument{ Name: "latency_shifted", }, sdkmetric.Stream{ Name: "latency_shifted_b", Aggregation: sdkmetric.AggregationExplicitBucketHistogram{ Boundaries: linearBuckets, }, }, ) viewLatencyShiftedC := sdkmetric.NewView( sdkmetric.Instrument{ Name: "latency_shifted", }, sdkmetric.Stream{ Name: "latency_shifted_c", Aggregation: sdkmetric.AggregationExplicitBucketHistogram{ Boundaries: defaultBuckets, }, }, ) viewLatencyMultimodalA := sdkmetric.NewView( sdkmetric.Instrument{ Name: "latency_multimodal", }, sdkmetric.Stream{ Name: "latency_multimodal_a", Aggregation: sdkmetric.AggregationBase2ExponentialHistogram{ MaxSize: 160, MaxScale: 20, }, }, ) viewLatencyMultimodalB := sdkmetric.NewView( sdkmetric.Instrument{ Name: "latency_multimodal", }, sdkmetric.Stream{ Name: "latency_multimodal_b", Aggregation: sdkmetric.AggregationExplicitBucketHistogram{ Boundaries: linearBuckets, }, }, ) viewLatencyMultimodalC := sdkmetric.NewView( sdkmetric.Instrument{ Name: "latency_multimodal", }, sdkmetric.Stream{ Name: "latency_multimodal_c", Aggregation: sdkmetric.AggregationExplicitBucketHistogram{ Boundaries: defaultBuckets, }, }, ) // initialize a MeterProvider with that periodically exports to the GCP exporter. provider := sdkmetric.NewMeterProvider( sdkmetric.WithView( viewLatencyA, viewLatencyB, viewLatencyC, viewLatencyShiftedA, viewLatencyShiftedB, viewLatencyShiftedC, viewLatencyMultimodalA, viewLatencyMultimodalB, viewLatencyMultimodalC, ), sdkmetric.WithReader( sdkmetric.NewPeriodicReader( exporter, sdkmetric.WithInterval(10*time.Second), ), ), sdkmetric.WithResource(res), ) defer func() { if err = provider.Shutdown(ctx); err != nil { log.Fatalf("failed to shutdown meter provider: %v", err) } }() // Create a meter with which we will record metrics for our package. meter := provider.Meter("github.com/GoogleCloudPlatform/opentelemetry-operations-go/example/metric") _, err = meter.Float64ObservableGauge( "latency", metric.WithDescription( "Latency", ), metric.WithUnit("s"), metric.WithFloat64Callback(func(_ context.Context, o metric.Float64Observer) error { points := 1000 // Create a log normal distribution to simulate latency // from server response. dist := distuv.LogNormal{ Mu: 3.5, Sigma: .5, } data := make([]float64, points) // Draw some random values from the log normal distribution for i := range data { data[i] = dist.Rand() // Gauge metric observation o.Observe(data[i], metric.WithAttributes(clabels...)) } mean, std := stat.MeanStdDev(data, nil) log.Printf("Sent Latency Data (Original Distribution): #points %d , mean %v, sdv %v", points, mean, std) return nil }), ) _, err = meter.Float64ObservableGauge( "latency_shifted", metric.WithDescription( "Latency Shifted", ), metric.WithUnit("s"), metric.WithFloat64Callback(func(_ context.Context, o metric.Float64Observer) error { points := 1000 // Create a log normal distribution to simulate latency // from server response. dist := distuv.LogNormal{ Mu: 5.5, Sigma: .5, } data := make([]float64, points) // Draw some random values from the log normal distribution for i := range data { data[i] = dist.Rand() // Gauge metric observation o.Observe(data[i], metric.WithAttributes(clabels...)) } mean, std := stat.MeanStdDev(data, nil) log.Printf("Sent Latency Data (Shifted Distribution): #points %d , mean %v, sdv %v", points, mean, std) return nil }), ) _, err = meter.Float64ObservableGauge( "latency_multimodal", metric.WithDescription( "Latency Multimodal", ), metric.WithUnit("s"), metric.WithFloat64Callback(func(_ context.Context, o metric.Float64Observer) error { points := 1000 // Create a multimodal normal dist1 := distuv.LogNormal{ Mu: 3.5, Sigma: .5, } dist2 := distuv.LogNormal{ Mu: 5.5, Sigma: .5, } data := make([]float64, points) // Draw some random values from the log normal distribution for i := range data { if rand.Float64() < .5 { data[i] = dist1.Rand() } else { data[i] = dist2.Rand() } // Gauge metric observation o.Observe(data[i], metric.WithAttributes(clabels...)) } mean, std := stat.MeanStdDev(data, nil) log.Printf("Sent Latency Data (Multimodal Distribution): #points %d , mean %v, sdv %v", points, mean, std) return nil }), ) if err != nil { log.Fatalf("Failed to create gauge: %v", err) } // Wait for interrupt var stopChan = make(chan os.Signal, 2) signal.Notify(stopChan, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) <-stopChan }