github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/engine/pkg/promutil/wrapping.go (about)

     1  // Copyright 2022 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package promutil
    15  
    16  import (
    17  	"github.com/pingcap/log"
    18  	"github.com/prometheus/client_golang/prometheus"
    19  	"go.uber.org/zap"
    20  )
    21  
    22  // WrappingFactory uses inner Factory to create metrics and attaches some information
    23  // to the metrics it created.
    24  // The usage is dataflow engine can wrap prefix and labels for DM, and DM can wrap
    25  // labels for dumpling/lightning.
    26  type WrappingFactory struct {
    27  	inner       Factory
    28  	prefix      string
    29  	constLabels prometheus.Labels
    30  }
    31  
    32  // NewWrappingFactory creates a WrappingFactory.
    33  // if `prefix` is not empty, it is added to the metric name to avoid cross app metric
    34  // conflict, e.g. $prefix_$namespace_$subsystem_$name.
    35  // `labels` is added to user metric when create metrics.
    36  func NewWrappingFactory(f Factory, prefix string, labels prometheus.Labels) Factory {
    37  	return &WrappingFactory{
    38  		inner:       f,
    39  		prefix:      prefix,
    40  		constLabels: labels,
    41  	}
    42  }
    43  
    44  // NewCounter works like the function of the same name in the prometheus package
    45  // except for it will wrap prefix and constLabels. Thread-safe.
    46  func (f *WrappingFactory) NewCounter(opts prometheus.CounterOpts) prometheus.Counter {
    47  	return f.inner.NewCounter(*wrapCounterOpts(f.prefix, f.constLabels, &opts))
    48  }
    49  
    50  // NewCounterVec works like the function of the same name in the prometheus package
    51  // except for it will wrap prefix and constLabels. Thread-safe.
    52  func (f *WrappingFactory) NewCounterVec(opts prometheus.CounterOpts, labelNames []string) *prometheus.CounterVec {
    53  	return f.inner.NewCounterVec(*wrapCounterOpts(f.prefix, f.constLabels, &opts), labelNames)
    54  }
    55  
    56  // NewGauge works like the function of the same name in the prometheus package
    57  // except for it will wrap prefix and constLabels. Thread-safe.
    58  func (f *WrappingFactory) NewGauge(opts prometheus.GaugeOpts) prometheus.Gauge {
    59  	return f.inner.NewGauge(*wrapGaugeOpts(f.prefix, f.constLabels, &opts))
    60  }
    61  
    62  // NewGaugeVec works like the function of the same name in the prometheus package
    63  // except for it will wrap prefix and constLabels. Thread-safe.
    64  func (f *WrappingFactory) NewGaugeVec(opts prometheus.GaugeOpts, labelNames []string) *prometheus.GaugeVec {
    65  	return f.inner.NewGaugeVec(*wrapGaugeOpts(f.prefix, f.constLabels, &opts), labelNames)
    66  }
    67  
    68  // NewHistogram works like the function of the same name in the prometheus package
    69  // except for it will wrap prefix and constLabels. Thread-safe.
    70  func (f *WrappingFactory) NewHistogram(opts prometheus.HistogramOpts) prometheus.Histogram {
    71  	return f.inner.NewHistogram(*wrapHistogramOpts(f.prefix, f.constLabels, &opts))
    72  }
    73  
    74  // NewHistogramVec works like the function of the same name in the prometheus package
    75  // except for it will wrap prefix and constLabels. Thread-safe.
    76  func (f *WrappingFactory) NewHistogramVec(opts prometheus.HistogramOpts, labelNames []string) *prometheus.HistogramVec {
    77  	return f.inner.NewHistogramVec(*wrapHistogramOpts(f.prefix, f.constLabels, &opts), labelNames)
    78  }
    79  
    80  func wrapCounterOpts(prefix string, constLabels prometheus.Labels, opts *prometheus.CounterOpts) *prometheus.CounterOpts {
    81  	if opts.ConstLabels == nil && constLabels != nil {
    82  		opts.ConstLabels = make(prometheus.Labels)
    83  	}
    84  	wrapOptsCommon(prefix, constLabels, &opts.Namespace, opts.ConstLabels)
    85  	return opts
    86  }
    87  
    88  func wrapGaugeOpts(prefix string, constLabels prometheus.Labels, opts *prometheus.GaugeOpts) *prometheus.GaugeOpts {
    89  	if opts.ConstLabels == nil && constLabels != nil {
    90  		opts.ConstLabels = make(prometheus.Labels)
    91  	}
    92  	wrapOptsCommon(prefix, constLabels, &opts.Namespace, opts.ConstLabels)
    93  	return opts
    94  }
    95  
    96  func wrapHistogramOpts(prefix string, constLabels prometheus.Labels, opts *prometheus.HistogramOpts) *prometheus.HistogramOpts {
    97  	if opts.ConstLabels == nil && constLabels != nil {
    98  		opts.ConstLabels = make(prometheus.Labels)
    99  	}
   100  	wrapOptsCommon(prefix, constLabels, &opts.Namespace, opts.ConstLabels)
   101  	return opts
   102  }
   103  
   104  func wrapOptsCommon(prefix string, constLabels prometheus.Labels, namespace *string, cls prometheus.Labels) {
   105  	// namespace SHOULD NOT be nil
   106  	if prefix != "" {
   107  		if *namespace != "" {
   108  			*namespace = prefix + "_" + *namespace
   109  		} else {
   110  			*namespace = prefix
   111  		}
   112  	}
   113  	for name, value := range constLabels {
   114  		if _, exists := cls[name]; exists {
   115  			log.Panic("duplicate label name", zap.String("label", name))
   116  		}
   117  		cls[name] = value
   118  	}
   119  }