2020-07-02 16:42:12 +00:00
package relabel
import (
"flag"
"fmt"
2024-06-10 17:36:41 +00:00
"sync"
2020-07-02 16:42:12 +00:00
"sync/atomic"
2024-06-10 17:36:41 +00:00
"time"
2020-07-02 16:42:12 +00:00
2022-11-21 22:47:40 +00:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
2020-07-02 16:42:12 +00:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/procutil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompb"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promrelabel"
2020-07-23 11:57:42 +00:00
"github.com/VictoriaMetrics/metrics"
2020-07-02 16:42:12 +00:00
)
2021-06-04 17:27:55 +00:00
var (
relabelConfig = flag . String ( "relabelConfig" , "" , "Optional path to a file with relabeling rules, which are applied to all the ingested metrics. " +
2021-12-02 22:08:42 +00:00
"The path can point either to local file or to http url. " +
2021-09-29 18:17:25 +00:00
"See https://docs.victoriametrics.com/#relabeling for details. The config is reloaded on SIGHUP signal" )
2022-09-26 10:11:37 +00:00
usePromCompatibleNaming = flag . Bool ( "usePromCompatibleNaming" , false , "Whether to replace characters unsupported by Prometheus with underscores " +
"in the ingested metric names and label names. For example, foo.bar{a.b='c'} is transformed into foo_bar{a_b='c'} during data ingestion if this flag is set. " +
"See https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels" )
2024-06-10 17:36:41 +00:00
relabelConfigCheckInterval = flag . Duration ( "relabelConfigCheckInterval" , 0 , "Interval for checking for changes in '-relabelConfig' file. " +
"By default the checking is disabled. Send SIGHUP signal in order to force config check for changes" )
2021-06-04 17:27:55 +00:00
)
2020-07-02 16:42:12 +00:00
// Init must be called after flag.Parse and before using the relabel package.
func Init ( ) {
2021-05-21 13:34:03 +00:00
// Register SIGHUP handler for config re-read just before loadRelabelConfig call.
// This guarantees that the config will be re-read if the signal arrives during loadRelabelConfig call.
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1240
sighupCh := procutil . NewSighupChan ( )
2021-02-22 14:33:55 +00:00
pcs , err := loadRelabelConfig ( )
2020-07-02 16:42:12 +00:00
if err != nil {
logger . Fatalf ( "cannot load relabelConfig: %s" , err )
}
2021-02-22 14:33:55 +00:00
pcsGlobal . Store ( pcs )
2022-11-21 22:47:40 +00:00
configSuccess . Set ( 1 )
configTimestamp . Set ( fasttime . UnixTimestamp ( ) )
2020-07-02 16:42:12 +00:00
if len ( * relabelConfig ) == 0 {
return
}
2024-06-10 17:36:41 +00:00
globalStopChan = make ( chan struct { } )
relabelWG . Add ( 1 )
2020-07-02 16:42:12 +00:00
go func ( ) {
2024-06-10 17:36:41 +00:00
defer relabelWG . Done ( )
var tickerCh <- chan time . Time
if * relabelConfigCheckInterval > 0 {
ticker := time . NewTicker ( * relabelConfigCheckInterval )
tickerCh = ticker . C
defer ticker . Stop ( )
}
2024-06-11 10:08:00 +00:00
var noChangesLogFn func ( )
2024-06-10 17:36:41 +00:00
for {
select {
case <- sighupCh :
configReloads . Inc ( )
logger . Infof ( "received SIGHUP; reloading -relabelConfig=%q..." , * relabelConfig )
2024-06-11 10:08:00 +00:00
noChangesLogFn = func ( ) {
2024-06-10 17:36:41 +00:00
logger . Infof ( "nothing changed in %q" , relabelConfig )
}
case <- tickerCh :
2024-06-11 10:08:00 +00:00
// silently skip logging for the unchanged config files
noChangesLogFn = func ( ) { }
2024-06-10 17:36:41 +00:00
case <- globalStopChan :
2024-06-11 10:08:00 +00:00
logger . Infof ( "stopping relabel config reloader" )
2024-06-10 17:36:41 +00:00
return
2020-07-02 16:42:12 +00:00
}
2024-06-11 10:08:00 +00:00
pcsNew , err := loadRelabelConfig ( )
if err != nil {
configReloadErrors . Inc ( )
configSuccess . Set ( 0 )
logger . Errorf ( "cannot load the updated relabelConfig: %s; preserving the previous config" , err )
continue
}
if pcsNew . String ( ) == pcs . String ( ) {
// set success to 1 since previous reload could have been unsuccessful
// do not update configTimestamp as config version remains old.
configSuccess . Set ( 1 )
noChangesLogFn ( )
continue
}
pcs = pcsNew
pcsGlobal . Store ( pcsNew )
2022-11-21 22:47:40 +00:00
configSuccess . Set ( 1 )
configTimestamp . Set ( fasttime . UnixTimestamp ( ) )
2020-07-02 16:42:12 +00:00
logger . Infof ( "successfully reloaded -relabelConfig=%q" , * relabelConfig )
}
} ( )
}
2024-06-11 10:08:00 +00:00
// Stop stops relabel config reloader watchers
2024-06-10 17:36:41 +00:00
func Stop ( ) {
if len ( * relabelConfig ) == 0 {
return
}
close ( globalStopChan )
relabelWG . Wait ( )
}
var (
globalStopChan chan struct { }
relabelWG sync . WaitGroup
)
2022-11-21 22:47:40 +00:00
var (
configReloads = metrics . NewCounter ( ` vm_relabel_config_reloads_total ` )
configReloadErrors = metrics . NewCounter ( ` vm_relabel_config_reloads_errors_total ` )
2023-12-20 12:23:38 +00:00
configSuccess = metrics . NewGauge ( ` vm_relabel_config_last_reload_successful ` , nil )
2022-11-21 22:47:40 +00:00
configTimestamp = metrics . NewCounter ( ` vm_relabel_config_last_reload_success_timestamp_seconds ` )
)
2023-07-20 00:37:49 +00:00
var pcsGlobal atomic . Pointer [ promrelabel . ParsedConfigs ]
2020-07-02 16:42:12 +00:00
2023-04-01 04:27:45 +00:00
// CheckRelabelConfig checks config pointed by -relabelConfig
func CheckRelabelConfig ( ) error {
_ , err := loadRelabelConfig ( )
return err
}
2021-02-22 14:33:55 +00:00
func loadRelabelConfig ( ) ( * promrelabel . ParsedConfigs , error ) {
2020-07-02 16:42:12 +00:00
if len ( * relabelConfig ) == 0 {
return nil , nil
}
2022-12-10 10:09:21 +00:00
pcs , err := promrelabel . LoadRelabelConfigs ( * relabelConfig )
2020-07-02 16:42:12 +00:00
if err != nil {
return nil , fmt . Errorf ( "error when reading -relabelConfig=%q: %w" , * relabelConfig , err )
}
2021-02-22 14:33:55 +00:00
return pcs , nil
2020-07-02 16:42:12 +00:00
}
// HasRelabeling returns true if there is global relabeling.
func HasRelabeling ( ) bool {
2023-07-20 00:37:49 +00:00
pcs := pcsGlobal . Load ( )
2022-09-26 10:11:37 +00:00
return pcs . Len ( ) > 0 || * usePromCompatibleNaming
2020-07-02 16:42:12 +00:00
}
// Ctx holds relabeling context.
type Ctx struct {
// tmpLabels is used during ApplyRelabeling call.
tmpLabels [ ] prompbmarshal . Label
}
// Reset resets ctx.
func ( ctx * Ctx ) Reset ( ) {
2020-11-07 14:16:56 +00:00
promrelabel . CleanLabels ( ctx . tmpLabels )
2020-07-02 16:42:12 +00:00
ctx . tmpLabels = ctx . tmpLabels [ : 0 ]
}
// ApplyRelabeling applies relabeling to the given labels and returns the result.
//
// The returned labels are valid until the next call to ApplyRelabeling.
func ( ctx * Ctx ) ApplyRelabeling ( labels [ ] prompb . Label ) [ ] prompb . Label {
2023-07-20 00:37:49 +00:00
pcs := pcsGlobal . Load ( )
2022-09-26 10:11:37 +00:00
if pcs . Len ( ) == 0 && ! * usePromCompatibleNaming {
2020-07-02 20:13:13 +00:00
// There are no relabeling rules.
2020-07-02 16:42:12 +00:00
return labels
}
2022-09-26 10:11:37 +00:00
// Convert labels to prompbmarshal.Label format suitable for relabeling.
2020-07-02 16:42:12 +00:00
tmpLabels := ctx . tmpLabels [ : 0 ]
for _ , label := range labels {
2024-01-14 20:33:19 +00:00
name := label . Name
if name == "" {
2020-07-02 16:42:12 +00:00
name = "__name__"
}
2024-01-14 20:33:19 +00:00
value := label . Value
2020-07-02 16:42:12 +00:00
tmpLabels = append ( tmpLabels , prompbmarshal . Label {
Name : name ,
Value : value ,
} )
}
2022-09-26 10:11:37 +00:00
if * usePromCompatibleNaming {
// Replace unsupported Prometheus chars in label names and metric names with underscores.
for i := range tmpLabels {
label := & tmpLabels [ i ]
if label . Name == "__name__" {
2023-08-14 14:14:40 +00:00
label . Value = promrelabel . SanitizeMetricName ( label . Value )
2022-09-26 10:11:37 +00:00
} else {
2023-08-14 14:14:40 +00:00
label . Name = promrelabel . SanitizeLabelName ( label . Name )
2022-09-26 10:11:37 +00:00
}
}
}
if pcs . Len ( ) > 0 {
// Apply relabeling
2022-10-09 11:51:14 +00:00
tmpLabels = pcs . Apply ( tmpLabels , 0 )
tmpLabels = promrelabel . FinalizeLabels ( tmpLabels [ : 0 ] , tmpLabels )
2022-09-26 10:11:37 +00:00
if len ( tmpLabels ) == 0 {
metricsDropped . Inc ( )
}
2020-07-23 11:57:42 +00:00
}
2020-07-02 16:42:12 +00:00
2022-09-26 10:11:37 +00:00
ctx . tmpLabels = tmpLabels
2020-07-02 16:42:12 +00:00
// Return back labels to the desired format.
dst := labels [ : 0 ]
for _ , label := range tmpLabels {
2024-01-14 20:33:19 +00:00
name := label . Name
2020-07-02 16:42:12 +00:00
if label . Name == "__name__" {
2024-01-14 20:33:19 +00:00
name = ""
2020-07-02 16:42:12 +00:00
}
2024-01-14 20:33:19 +00:00
value := label . Value
2020-07-02 16:42:12 +00:00
dst = append ( dst , prompb . Label {
Name : name ,
Value : value ,
} )
}
return dst
}
2020-07-23 11:57:42 +00:00
var metricsDropped = metrics . NewCounter ( ` vm_relabel_metrics_dropped_total ` )