golang.org/x/build@v0.0.0-20240506185731-218518f32b70/internal/metrics/metrics.go (about) 1 // Copyright 2021 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package metrics provides a service for reporting metrics to 6 // Stackdriver, or locally during development. 7 package metrics 8 9 import ( 10 "errors" 11 "fmt" 12 "net/http" 13 "os" 14 "time" 15 16 "cloud.google.com/go/compute/metadata" 17 "contrib.go.opencensus.io/exporter/prometheus" 18 "contrib.go.opencensus.io/exporter/stackdriver" 19 "go.opencensus.io/stats/view" 20 "golang.org/x/build/buildenv" 21 mrpb "google.golang.org/genproto/googleapis/api/monitoredres" 22 ) 23 24 // NewService initializes a *Service. 25 // 26 // The Service returned is configured to send metric data to 27 // StackDriver. When not running on GCE, it will host metrics through 28 // a prometheus HTTP handler. 29 // 30 // views will be passed to view.Register for export to the metric 31 // service. 32 func NewService(resource *MonitoredResource, views []*view.View) (*Service, error) { 33 err := view.Register(views...) 34 if err != nil { 35 return nil, err 36 } 37 38 if !metadata.OnGCE() { 39 view.SetReportingPeriod(5 * time.Second) 40 pe, err := prometheus.NewExporter(prometheus.Options{}) 41 if err != nil { 42 return nil, fmt.Errorf("prometheus.NewExporter: %w", err) 43 } 44 view.RegisterExporter(pe) 45 return &Service{pExporter: pe}, nil 46 } 47 48 projID, err := metadata.ProjectID() 49 if err != nil { 50 return nil, err 51 } 52 if resource == nil { 53 return nil, errors.New("resource is required, got nil") 54 } 55 sde, err := stackdriver.NewExporter(stackdriver.Options{ 56 ProjectID: projID, 57 MonitoredResource: resource, 58 ReportingInterval: time.Minute, // Minimum interval for Stackdriver is 1 minute. 59 }) 60 if err != nil { 61 return nil, err 62 } 63 64 // Minimum interval for Stackdriver is 1 minute. 65 view.SetReportingPeriod(time.Minute) 66 // Start the metrics exporter. 67 if err := sde.StartMetricsExporter(); err != nil { 68 return nil, err 69 } 70 71 return &Service{sdExporter: sde}, nil 72 } 73 74 // Service controls metric exporters. 75 type Service struct { 76 sdExporter *stackdriver.Exporter 77 pExporter *prometheus.Exporter 78 } 79 80 func (m *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) { 81 if m.pExporter != nil { 82 m.pExporter.ServeHTTP(w, r) 83 return 84 } 85 http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) 86 } 87 88 // Stop flushes metrics and stops exporting. Stop should be called 89 // before exiting. 90 func (m *Service) Stop() { 91 if sde := m.sdExporter; sde != nil { 92 // Flush any unsent data before exiting. 93 sde.Flush() 94 95 sde.StopMetricsExporter() 96 } 97 } 98 99 // MonitoredResource wraps a *mrpb.MonitoredResource to implement the 100 // monitoredresource.MonitoredResource interface. 101 type MonitoredResource mrpb.MonitoredResource 102 103 func (r *MonitoredResource) MonitoredResource() (resType string, labels map[string]string) { 104 return r.Type, r.Labels 105 } 106 107 // GKEResource populates a MonitoredResource with GKE Metadata. 108 // 109 // The returned MonitoredResource will have the type set to "k8s_container". 110 func GKEResource(containerName string) (*MonitoredResource, error) { 111 projID, err := metadata.ProjectID() 112 if err != nil { 113 return nil, err 114 } 115 // https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity#gke_mds 116 location, err := metadata.InstanceAttributeValue("cluster-location") 117 if err != nil { 118 return nil, err 119 } 120 // https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity#gke_mds 121 clusterName, err := metadata.InstanceAttributeValue("cluster-name") 122 if err != nil { 123 return nil, err 124 } 125 podName, err := os.Hostname() 126 if err != nil { 127 return nil, err 128 } 129 130 return (*MonitoredResource)(&mrpb.MonitoredResource{ 131 Type: "k8s_container", // See: https://cloud.google.com/monitoring/api/resources#tag_k8s_container 132 Labels: map[string]string{ 133 "project_id": projID, 134 "location": location, 135 "cluster_name": clusterName, 136 "namespace_name": buildenv.ByProjectID(projID).KubeServices.Namespace, 137 "pod_name": podName, 138 "container_name": containerName, 139 }, 140 }), nil 141 }