golang.org/x/tools@v0.21.0/internal/event/export/prometheus/prometheus.go (about)

     1  // Copyright 2019 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 prometheus
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"fmt"
    11  	"net/http"
    12  	"sort"
    13  	"sync"
    14  
    15  	"golang.org/x/tools/internal/event"
    16  	"golang.org/x/tools/internal/event/core"
    17  	"golang.org/x/tools/internal/event/export/metric"
    18  	"golang.org/x/tools/internal/event/label"
    19  )
    20  
    21  func New() *Exporter {
    22  	return &Exporter{}
    23  }
    24  
    25  type Exporter struct {
    26  	mu      sync.Mutex
    27  	metrics []metric.Data
    28  }
    29  
    30  func (e *Exporter) ProcessEvent(ctx context.Context, ev core.Event, lm label.Map) context.Context {
    31  	if !event.IsMetric(ev) {
    32  		return ctx
    33  	}
    34  	e.mu.Lock()
    35  	defer e.mu.Unlock()
    36  	metrics := metric.Entries.Get(lm).([]metric.Data)
    37  	for _, data := range metrics {
    38  		name := data.Handle()
    39  		// We keep the metrics in name sorted order so the page is stable and easy
    40  		// to read. We do this with an insertion sort rather than sorting the list
    41  		// each time
    42  		index := sort.Search(len(e.metrics), func(i int) bool {
    43  			return e.metrics[i].Handle() >= name
    44  		})
    45  		if index >= len(e.metrics) || e.metrics[index].Handle() != name {
    46  			// we have a new metric, so we need to make a space for it
    47  			old := e.metrics
    48  			e.metrics = make([]metric.Data, len(old)+1)
    49  			copy(e.metrics, old[:index])
    50  			copy(e.metrics[index+1:], old[index:])
    51  		}
    52  		e.metrics[index] = data
    53  	}
    54  	return ctx
    55  }
    56  
    57  func (e *Exporter) header(w http.ResponseWriter, name, description string, isGauge, isHistogram bool) {
    58  	kind := "counter"
    59  	if isGauge {
    60  		kind = "gauge"
    61  	}
    62  	if isHistogram {
    63  		kind = "histogram"
    64  	}
    65  	fmt.Fprintf(w, "# HELP %s %s\n", name, description)
    66  	fmt.Fprintf(w, "# TYPE %s %s\n", name, kind)
    67  }
    68  
    69  func (e *Exporter) row(w http.ResponseWriter, name string, group []label.Label, extra string, value interface{}) {
    70  	fmt.Fprint(w, name)
    71  	buf := &bytes.Buffer{}
    72  	fmt.Fprint(buf, group)
    73  	if extra != "" {
    74  		if buf.Len() > 0 {
    75  			fmt.Fprint(buf, ",")
    76  		}
    77  		fmt.Fprint(buf, extra)
    78  	}
    79  	if buf.Len() > 0 {
    80  		fmt.Fprint(w, "{")
    81  		buf.WriteTo(w)
    82  		fmt.Fprint(w, "}")
    83  	}
    84  	fmt.Fprintf(w, " %v\n", value)
    85  }
    86  
    87  func (e *Exporter) Serve(w http.ResponseWriter, r *http.Request) {
    88  	e.mu.Lock()
    89  	defer e.mu.Unlock()
    90  	for _, data := range e.metrics {
    91  		switch data := data.(type) {
    92  		case *metric.Int64Data:
    93  			e.header(w, data.Info.Name, data.Info.Description, data.IsGauge, false)
    94  			for i, group := range data.Groups() {
    95  				e.row(w, data.Info.Name, group, "", data.Rows[i])
    96  			}
    97  
    98  		case *metric.Float64Data:
    99  			e.header(w, data.Info.Name, data.Info.Description, data.IsGauge, false)
   100  			for i, group := range data.Groups() {
   101  				e.row(w, data.Info.Name, group, "", data.Rows[i])
   102  			}
   103  
   104  		case *metric.HistogramInt64Data:
   105  			e.header(w, data.Info.Name, data.Info.Description, false, true)
   106  			for i, group := range data.Groups() {
   107  				row := data.Rows[i]
   108  				for j, b := range data.Info.Buckets {
   109  					e.row(w, data.Info.Name+"_bucket", group, fmt.Sprintf(`le="%v"`, b), row.Values[j])
   110  				}
   111  				e.row(w, data.Info.Name+"_bucket", group, `le="+Inf"`, row.Count)
   112  				e.row(w, data.Info.Name+"_count", group, "", row.Count)
   113  				e.row(w, data.Info.Name+"_sum", group, "", row.Sum)
   114  			}
   115  
   116  		case *metric.HistogramFloat64Data:
   117  			e.header(w, data.Info.Name, data.Info.Description, false, true)
   118  			for i, group := range data.Groups() {
   119  				row := data.Rows[i]
   120  				for j, b := range data.Info.Buckets {
   121  					e.row(w, data.Info.Name+"_bucket", group, fmt.Sprintf(`le="%v"`, b), row.Values[j])
   122  				}
   123  				e.row(w, data.Info.Name+"_bucket", group, `le="+Inf"`, row.Count)
   124  				e.row(w, data.Info.Name+"_count", group, "", row.Count)
   125  				e.row(w, data.Info.Name+"_sum", group, "", row.Sum)
   126  			}
   127  		}
   128  	}
   129  }