2020-07-02 18:42:12 +02:00
package relabel
import (
"flag"
"fmt"
"sync/atomic"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
2022-11-21 23:47:40 +01:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
2020-07-02 18:42:12 +02: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 13:57:42 +02:00
"github.com/VictoriaMetrics/metrics"
2020-07-02 18:42:12 +02:00
)
2021-06-04 19:27:55 +02: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 23:08:42 +01:00
"The path can point either to local file or to http url. " +
2021-09-29 20:17:25 +02:00
"See https://docs.victoriametrics.com/#relabeling for details. The config is reloaded on SIGHUP signal" )
2021-06-04 19:27:55 +02:00
relabelDebug = flag . Bool ( "relabelDebug" , false , "Whether to log metrics before and after relabeling with -relabelConfig. If the -relabelDebug is enabled, " +
"then the metrics aren't sent to storage. This is useful for debugging the relabeling configs" )
2022-09-26 12:11:37 +02: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" )
2021-06-04 19:27:55 +02:00
)
2020-07-02 18:42:12 +02:00
// Init must be called after flag.Parse and before using the relabel package.
func Init ( ) {
2021-05-21 15:34:03 +02: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 15:33:55 +01:00
pcs , err := loadRelabelConfig ( )
2020-07-02 18:42:12 +02:00
if err != nil {
logger . Fatalf ( "cannot load relabelConfig: %s" , err )
}
2021-02-22 15:33:55 +01:00
pcsGlobal . Store ( pcs )
2022-11-21 23:47:40 +01:00
configSuccess . Set ( 1 )
configTimestamp . Set ( fasttime . UnixTimestamp ( ) )
2020-07-02 18:42:12 +02:00
if len ( * relabelConfig ) == 0 {
return
}
go func ( ) {
for range sighupCh {
2022-11-21 23:47:40 +01:00
configReloads . Inc ( )
2020-07-02 18:42:12 +02:00
logger . Infof ( "received SIGHUP; reloading -relabelConfig=%q..." , * relabelConfig )
2021-02-22 15:33:55 +01:00
pcs , err := loadRelabelConfig ( )
2020-07-02 18:42:12 +02:00
if err != nil {
2022-11-21 23:47:40 +01:00
configReloadErrors . Inc ( )
configSuccess . Set ( 0 )
2020-07-02 18:42:12 +02:00
logger . Errorf ( "cannot load the updated relabelConfig: %s; preserving the previous config" , err )
continue
}
2021-02-22 15:33:55 +01:00
pcsGlobal . Store ( pcs )
2022-11-21 23:47:40 +01:00
configSuccess . Set ( 1 )
configTimestamp . Set ( fasttime . UnixTimestamp ( ) )
2020-07-02 18:42:12 +02:00
logger . Infof ( "successfully reloaded -relabelConfig=%q" , * relabelConfig )
}
} ( )
}
2022-11-21 23:47:40 +01:00
var (
configReloads = metrics . NewCounter ( ` vm_relabel_config_reloads_total ` )
configReloadErrors = metrics . NewCounter ( ` vm_relabel_config_reloads_errors_total ` )
configSuccess = metrics . NewCounter ( ` vm_relabel_config_last_reload_successful ` )
configTimestamp = metrics . NewCounter ( ` vm_relabel_config_last_reload_success_timestamp_seconds ` )
)
2021-02-22 15:33:55 +01:00
var pcsGlobal atomic . Value
2020-07-02 18:42:12 +02:00
2021-02-22 15:33:55 +01:00
func loadRelabelConfig ( ) ( * promrelabel . ParsedConfigs , error ) {
2020-07-02 18:42:12 +02:00
if len ( * relabelConfig ) == 0 {
return nil , nil
}
2021-06-04 19:27:55 +02:00
pcs , err := promrelabel . LoadRelabelConfigs ( * relabelConfig , * relabelDebug )
2020-07-02 18:42:12 +02:00
if err != nil {
return nil , fmt . Errorf ( "error when reading -relabelConfig=%q: %w" , * relabelConfig , err )
}
2021-02-22 15:33:55 +01:00
return pcs , nil
2020-07-02 18:42:12 +02:00
}
// HasRelabeling returns true if there is global relabeling.
func HasRelabeling ( ) bool {
2021-02-22 15:33:55 +01:00
pcs := pcsGlobal . Load ( ) . ( * promrelabel . ParsedConfigs )
2022-09-26 12:11:37 +02:00
return pcs . Len ( ) > 0 || * usePromCompatibleNaming
2020-07-02 18:42:12 +02: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 15:16:56 +01:00
promrelabel . CleanLabels ( ctx . tmpLabels )
2020-07-02 18:42:12 +02: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 {
2021-02-22 15:33:55 +01:00
pcs := pcsGlobal . Load ( ) . ( * promrelabel . ParsedConfigs )
2022-09-26 12:11:37 +02:00
if pcs . Len ( ) == 0 && ! * usePromCompatibleNaming {
2020-07-02 22:13:13 +02:00
// There are no relabeling rules.
2020-07-02 18:42:12 +02:00
return labels
}
2022-09-26 12:11:37 +02:00
// Convert labels to prompbmarshal.Label format suitable for relabeling.
2020-07-02 18:42:12 +02:00
tmpLabels := ctx . tmpLabels [ : 0 ]
for _ , label := range labels {
name := bytesutil . ToUnsafeString ( label . Name )
if len ( name ) == 0 {
name = "__name__"
}
value := bytesutil . ToUnsafeString ( label . Value )
tmpLabels = append ( tmpLabels , prompbmarshal . Label {
Name : name ,
Value : value ,
} )
}
2022-09-26 12:11:37 +02: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__" {
2022-09-28 08:59:36 +02:00
label . Value = promrelabel . SanitizeName ( label . Value )
2022-09-26 12:11:37 +02:00
} else {
2022-09-28 08:59:36 +02:00
label . Name = promrelabel . SanitizeName ( label . Name )
2022-09-26 12:11:37 +02:00
}
}
}
if pcs . Len ( ) > 0 {
// Apply relabeling
2022-10-09 13:51:14 +02:00
tmpLabels = pcs . Apply ( tmpLabels , 0 )
tmpLabels = promrelabel . FinalizeLabels ( tmpLabels [ : 0 ] , tmpLabels )
2022-09-26 12:11:37 +02:00
if len ( tmpLabels ) == 0 {
metricsDropped . Inc ( )
}
2020-07-23 13:57:42 +02:00
}
2020-07-02 18:42:12 +02:00
2022-09-26 12:11:37 +02:00
ctx . tmpLabels = tmpLabels
2020-07-02 18:42:12 +02:00
// Return back labels to the desired format.
dst := labels [ : 0 ]
for _ , label := range tmpLabels {
name := bytesutil . ToUnsafeBytes ( label . Name )
if label . Name == "__name__" {
name = nil
}
value := bytesutil . ToUnsafeBytes ( label . Value )
dst = append ( dst , prompb . Label {
Name : name ,
Value : value ,
} )
}
return dst
}
2020-07-23 13:57:42 +02:00
var metricsDropped = metrics . NewCounter ( ` vm_relabel_metrics_dropped_total ` )