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  }