go.temporal.io/server@v1.23.0/common/metrics/opentelemetry_provider.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package metrics 26 27 import ( 28 "context" 29 "net/http" 30 "time" 31 32 "github.com/prometheus/client_golang/prometheus" 33 "github.com/prometheus/client_golang/prometheus/promhttp" 34 exporters "go.opentelemetry.io/otel/exporters/prometheus" 35 "go.opentelemetry.io/otel/metric" 36 sdkmetrics "go.opentelemetry.io/otel/sdk/metric" 37 38 "go.temporal.io/server/common/log" 39 "go.temporal.io/server/common/log/tag" 40 ) 41 42 var _ OpenTelemetryProvider = (*openTelemetryProviderImpl)(nil) 43 44 type ( 45 OpenTelemetryProvider interface { 46 Stop(logger log.Logger) 47 GetMeter() metric.Meter 48 } 49 50 openTelemetryProviderImpl struct { 51 meter metric.Meter 52 config *PrometheusConfig 53 server *http.Server 54 } 55 ) 56 57 func NewOpenTelemetryProvider( 58 logger log.Logger, 59 prometheusConfig *PrometheusConfig, 60 clientConfig *ClientConfig, 61 ) (*openTelemetryProviderImpl, error) { 62 reg := prometheus.NewRegistry() 63 exporter, err := exporters.New(exporters.WithRegisterer(reg)) 64 if err != nil { 65 logger.Error("Failed to initialize prometheus exporter.", tag.Error(err)) 66 return nil, err 67 } 68 69 var views []sdkmetrics.View 70 for _, u := range []string{Dimensionless, Bytes, Milliseconds} { 71 views = append(views, sdkmetrics.NewView( 72 sdkmetrics.Instrument{ 73 Kind: sdkmetrics.InstrumentKindHistogram, 74 Unit: u, 75 }, 76 sdkmetrics.Stream{ 77 Aggregation: sdkmetrics.AggregationExplicitBucketHistogram{ 78 Boundaries: clientConfig.PerUnitHistogramBoundaries[u], 79 }, 80 }, 81 )) 82 } 83 provider := sdkmetrics.NewMeterProvider( 84 sdkmetrics.WithReader(exporter), 85 sdkmetrics.WithView(views...), 86 ) 87 metricServer := initPrometheusListener(prometheusConfig, reg, logger) 88 meter := provider.Meter("temporal") 89 reporter := &openTelemetryProviderImpl{ 90 meter: meter, 91 config: prometheusConfig, 92 server: metricServer, 93 } 94 95 return reporter, nil 96 } 97 98 func initPrometheusListener(config *PrometheusConfig, reg *prometheus.Registry, logger log.Logger) *http.Server { 99 handlerPath := config.HandlerPath 100 if handlerPath == "" { 101 handlerPath = "/metrics" 102 } 103 104 handler := http.NewServeMux() 105 handler.HandleFunc(handlerPath, promhttp.HandlerFor(reg, promhttp.HandlerOpts{Registry: reg}).ServeHTTP) 106 107 if config.ListenAddress == "" { 108 logger.Fatal("Listen address must be specified.", tag.Address(config.ListenAddress)) 109 } 110 server := &http.Server{Addr: config.ListenAddress, Handler: handler} 111 112 go func() { 113 err := server.ListenAndServe() 114 if err != http.ErrServerClosed { 115 logger.Fatal("Failed to initialize prometheus listener.", tag.Address(config.ListenAddress)) 116 } 117 }() 118 119 return server 120 } 121 122 func (r *openTelemetryProviderImpl) GetMeter() metric.Meter { 123 return r.meter 124 } 125 126 func (r *openTelemetryProviderImpl) Stop(logger log.Logger) { 127 ctx, closeCtx := context.WithTimeout(context.Background(), time.Second) 128 defer closeCtx() 129 if err := r.server.Shutdown(ctx); !(err == nil || err == http.ErrServerClosed) { 130 logger.Error("Prometheus metrics server shutdown failure.", tag.Address(r.config.ListenAddress), tag.Error(err)) 131 } 132 }