trpc.group/trpc-go/trpc-go@v1.0.3/metrics/sink_console.go (about)

     1  //
     2  //
     3  // Tencent is pleased to support the open source community by making tRPC available.
     4  //
     5  // Copyright (C) 2023 THL A29 Limited, a Tencent company.
     6  // All rights reserved.
     7  //
     8  // If you have downloaded a copy of the tRPC source code from Tencent,
     9  // please note that tRPC source code is licensed under the  Apache 2.0 License,
    10  // A copy of the Apache 2.0 License is included in this file.
    11  //
    12  //
    13  
    14  package metrics
    15  
    16  import (
    17  	"encoding/json"
    18  	"fmt"
    19  	"sort"
    20  	"sync"
    21  	"time"
    22  )
    23  
    24  // NewConsoleSink creates a new console sink.
    25  func NewConsoleSink() Sink {
    26  	return &ConsoleSink{
    27  		counters:   make(map[string]float64),
    28  		gauges:     make(map[string]float64),
    29  		timers:     make(map[string]timer),
    30  		histograms: make(map[string]histogram),
    31  	}
    32  }
    33  
    34  // ConsoleSink defines the console sink.
    35  type ConsoleSink struct {
    36  	countersMu sync.RWMutex
    37  	counters   map[string]float64
    38  
    39  	gaugesMu sync.RWMutex
    40  	gauges   map[string]float64
    41  
    42  	timersMu sync.RWMutex
    43  	timers   map[string]timer
    44  
    45  	histogramsMu sync.RWMutex
    46  	histograms   map[string]histogram
    47  }
    48  
    49  // Name returns console sink name.
    50  func (c *ConsoleSink) Name() string {
    51  	return "console"
    52  }
    53  
    54  // Report reports a record.
    55  func (c *ConsoleSink) Report(rec Record, opts ...Option) error {
    56  	if len(rec.dimensions) <= 0 {
    57  		return c.reportSingleDimensionMetrics(rec, opts...)
    58  	}
    59  	return c.reportMultiDimensionMetrics(rec, opts...)
    60  }
    61  
    62  func (c *ConsoleSink) reportSingleDimensionMetrics(rec Record, _ ...Option) error {
    63  	// almost all monitor systems support cumulant and instant.
    64  	for _, m := range rec.metrics {
    65  		switch m.policy {
    66  		case PolicySUM:
    67  			c.incrCounter(m.name, m.value)
    68  		case PolicySET:
    69  			c.setGauge(m.name, m.value)
    70  		case PolicyTimer:
    71  			c.recordTimer(m.name, time.Duration(m.value))
    72  		case PolicyHistogram:
    73  			c.addSample(m.name, m.value)
    74  		default:
    75  			// not supported policies
    76  		}
    77  	}
    78  	return nil
    79  }
    80  
    81  func (c *ConsoleSink) reportMultiDimensionMetrics(rec Record, opts ...Option) error {
    82  	options := Options{}
    83  	for _, o := range opts {
    84  		o(&options)
    85  	}
    86  
    87  	type metric struct {
    88  		Name   string  `json:"name"`
    89  		Value  float64 `json:"value"`
    90  		Policy Policy  `json:"policy"`
    91  	}
    92  	metrics := make([]*metric, 0, len(rec.GetMetrics()))
    93  	for _, m := range rec.GetMetrics() {
    94  		metrics = append(metrics, &metric{Name: m.Name(), Value: m.Value(), Policy: m.Policy()})
    95  	}
    96  
    97  	buf, err := json.Marshal(struct {
    98  		Name       string       `json:"name"`
    99  		Dimensions []*Dimension `json:"dimensions"`
   100  		Metrics    []*metric    `json:"metrics"`
   101  	}{
   102  		Name:       rec.GetName(),
   103  		Dimensions: rec.GetDimensions(),
   104  		Metrics:    metrics,
   105  	})
   106  	if err != nil {
   107  		return err
   108  	}
   109  
   110  	// a common multiple dimension report.
   111  	fmt.Printf("metrics multi-dimension = %s\n", string(buf))
   112  	return nil
   113  }
   114  
   115  // incrCounter increases counter.
   116  func (c *ConsoleSink) incrCounter(key string, value float64) {
   117  	if c.counters == nil {
   118  		return
   119  	}
   120  	c.countersMu.Lock()
   121  	c.counters[key] += value
   122  	c.countersMu.Unlock()
   123  	fmt.Printf("metrics counter[key] = %s val = %v\n", key, value)
   124  }
   125  
   126  // setGauge sets gauge.
   127  func (c *ConsoleSink) setGauge(key string, value float64) {
   128  	if c.gauges == nil {
   129  		return
   130  	}
   131  	c.countersMu.Lock()
   132  	c.gauges[key] = value
   133  	c.countersMu.Unlock()
   134  	fmt.Printf("metrics gauge[key] = %s val = %v\n", key, value)
   135  }
   136  
   137  // recordTimer records timer.
   138  func (c *ConsoleSink) recordTimer(key string, duration time.Duration) {
   139  	fmt.Printf("metrics timer[key] = %s val = %v\n", key, duration)
   140  }
   141  
   142  // addSample adds a sample.
   143  func (c *ConsoleSink) addSample(key string, value float64) {
   144  	histogramsMutex.RLock()
   145  	h := histograms[key]
   146  	histogramsMutex.RUnlock()
   147  
   148  	v, ok := h.(*histogram)
   149  	if !ok {
   150  		return
   151  	}
   152  	hist := *v
   153  
   154  	histogramsMutex.Lock()
   155  	idx := sort.SearchFloat64s(hist.LookupByValue, value)
   156  	upperBound := hist.Buckets[idx].ValueUpperBound
   157  	hist.Buckets[idx].samples += value
   158  	histogramsMutex.Unlock()
   159  
   160  	fmt.Printf("metrics histogram[%s.%v] = %v\n", hist.Name, upperBound, hist.Buckets[idx].samples)
   161  }