mirror of
https://github.com/ClusterCockpit/cc-metric-collector.git
synced 2024-12-25 15:09:05 +01:00
New Message processor (#118)
* Add cpu_used (all-cpu_idle) to CpustatCollector * Update cc-metric-collector.init * Allow selection of timestamp precision in HttpSink * Add comment about precision requirement for cc-metric-store * Fix for API changes in gofish@v0.15.0 * Update requirements to latest version * Read sensors through redfish * Update golang toolchain to 1.21 * Remove stray error check * Update main config in configuration.md * Update Release action to use golang 1.22 stable release, no golang RPMs anymore * Update runonce action to use golang 1.22 stable release, no golang RPMs anymore * New message processor to check whether a message should be dropped or manipulate it in flight * Create a copy of message before manipulation --------- Co-authored-by: Holger Obermaier <Holger.Obermaier@kit.edu> Co-authored-by: Holger Obermaier <40787752+ho-ob@users.noreply.github.com>
This commit is contained in:
parent
8837ff4474
commit
21646e1edf
84
pkg/messageProcessor/README.md
Normal file
84
pkg/messageProcessor/README.md
Normal file
@ -0,0 +1,84 @@
|
||||
# Message Processor Component
|
||||
|
||||
Multiple parts of in the ClusterCockit ecosystem require the processing of CCMessages.
|
||||
The main CC application using it is `cc-metric-collector`. The processing part there was originally in the metric router, the central
|
||||
hub connecting collectors (reading local data), receivers (receiving remote data) and sinks (sending data). Already in early stages, the
|
||||
lack of flexibility caused some trouble:
|
||||
|
||||
> The sysadmins wanted to keep operating their Ganglia based monitoring infrastructure while we developed the CC stack. Ganglia wants the core metrics with
|
||||
> a specific name and resolution (right unit prefix) but there was no conversion of the data in the CC stack, so CC frontend developers wanted a different
|
||||
> resolution for some metrics. The issue was basically the `mem_used` metric showing the currently used memory of the node. Ganglia wants it in `kByte` as provided
|
||||
> by the Linux operating system but CC wanted it in `GByte`.
|
||||
|
||||
## For developers
|
||||
|
||||
Whenever you receive or are about to send a message out, you should provide some processing.
|
||||
|
||||
### Configuration of component
|
||||
|
||||
New operations can be added to the message processor at runtime. Of course, they can also be removed again. For the initial setup, having a configuration file
|
||||
or some fields in a configuration file for the processing.
|
||||
|
||||
The message processor uses the following configuration
|
||||
```golang
|
||||
type messageProcessorConfig struct {
|
||||
DropMessages []string `json:"drop_messages"` // List of metric names to drop. For fine-grained dropping use drop_messages_if
|
||||
DropMessagesIf []string `json:"drop_messages_if"` // List of evaluatable terms to drop messages
|
||||
RenameMessages map[string]string `json:"rename_messages"` // Map to rename metric name from key to value
|
||||
NormalizeUnits bool `json:"normalize_units"` // Check unit meta flag and normalize it using cc-units
|
||||
ChangeUnitPrefix map[string]string `json:"change_unit_prefix"` // Add prefix that should be applied to the messages
|
||||
}
|
||||
```
|
||||
|
||||
In order to load the configuration from a `json.RawMessage`:
|
||||
```golang
|
||||
mp, _ := NewMessageProcessor()
|
||||
|
||||
mp.FromConfigJSON(configJson)
|
||||
```
|
||||
|
||||
### Using the component
|
||||
After initialization and adding the different operations, the `ProcessMessage()` function applies all operations and returns whether the message should be dropped.
|
||||
|
||||
```golang
|
||||
m := lp.CCMetric{}
|
||||
|
||||
drop, err := mp.ProcessMessage(m)
|
||||
if !drop {
|
||||
// process further
|
||||
}
|
||||
```
|
||||
|
||||
#### Overhead
|
||||
|
||||
The operations taking conditions are pre-processed, which is commonly the time consuming part but, of course, with each added operation, the time to process a message
|
||||
increases.
|
||||
|
||||
## For users
|
||||
|
||||
### Syntax for evaluatable terms
|
||||
|
||||
The message processor uses `gval` for evaluating the terms. It provides a basic set of operators like string comparison and arithmetic operations.
|
||||
|
||||
Accessible for operations are
|
||||
- `name` of the message
|
||||
- `timestamp` of the message
|
||||
- `type`, `type-id` of the message (also `tag_type` and `tag_type-id`)
|
||||
- `stype`, `stype-id` of the message (if message has theses tags, also `tag_stype` and `tag_stype-id`)
|
||||
- `value` for a CCMetric message (also `field_value`)
|
||||
- `event` for a CCEvent message (also `field_event`)
|
||||
- `control` for a CCControl message (also `field_control`)
|
||||
- `log` for a CCLog message (also `field_log`)
|
||||
|
||||
Generally, all tags are accessible with `tag_<tagkey>`, all meta information with `meta_<metakey>` and fields with `field_<fieldkey>`.
|
||||
|
||||
- Comparing strings: `==`, `!=`, `match(str, regex)` (use `%` instead of `\`!)
|
||||
- Combining conditions: `&&`, `||`
|
||||
- Comparing numbers: `==`, `!=`, `<`, `>`, `<=`, `>=`
|
||||
- Test lists: `<value> in <list>`
|
||||
- Topological tests: `tag_type-id in getCpuListOfType("socket", "1")` (test if the metric belongs to socket 1 in local node topology)
|
||||
|
||||
Often the operations are written in JSON files for loading them at startup. In JSON, some characters are not allowed. Therefore, the term syntax reflects that:
|
||||
- use `''` instead of `""` for strings
|
||||
- for the regexes, use `%` instead of `\`
|
||||
|
987
pkg/messageProcessor/messageProcessor.go
Normal file
987
pkg/messageProcessor/messageProcessor.go
Normal file
@ -0,0 +1,987 @@
|
||||
package messageprocessor
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
lp2 "github.com/ClusterCockpit/cc-energy-manager/pkg/cc-message"
|
||||
cclog "github.com/ClusterCockpit/cc-metric-collector/pkg/ccLogger"
|
||||
lp "github.com/ClusterCockpit/cc-metric-collector/pkg/ccMetric"
|
||||
"github.com/expr-lang/expr"
|
||||
"github.com/expr-lang/expr/vm"
|
||||
)
|
||||
|
||||
// Message processor add/delete tag/meta configuration
|
||||
type messageProcessorTagConfig struct {
|
||||
Key string `json:"key"` // Tag name
|
||||
Value string `json:"value"` // Tag value
|
||||
Condition string `json:"if"` // Condition for adding or removing corresponding tag
|
||||
}
|
||||
|
||||
type messageProcessorConfig struct {
|
||||
StageOrder []string `json:"stage_order,omitempty"` // List of stages to execute them in the specified order and to skip unrequired ones
|
||||
DropMessages []string `json:"drop_messages,omitempty"` // List of metric names to drop. For fine-grained dropping use drop_messages_if
|
||||
DropMessagesIf []string `json:"drop_messages_if,omitempty"` // List of evaluatable terms to drop messages
|
||||
RenameMessages map[string]string `json:"rename_messages,omitempty"` // Map of metric names to rename
|
||||
RenameMessagesIf map[string]string `json:"rename_messages_if,omitempty"` // Map to rename metric name based on a condition
|
||||
NormalizeUnits bool `json:"normalize_units,omitempty"` // Check unit meta flag and normalize it using cc-units
|
||||
ChangeUnitPrefix map[string]string `json:"change_unit_prefix,omitempty"` // Add prefix that should be applied to the messages
|
||||
AddTagsIf []messageProcessorTagConfig `json:"add_tags_if"` // List of tags that are added when the condition is met
|
||||
DelTagsIf []messageProcessorTagConfig `json:"delete_tags_if"` // List of tags that are removed when the condition is met
|
||||
AddMetaIf []messageProcessorTagConfig `json:"add_meta_if"` // List of meta infos that are added when the condition is met
|
||||
DelMetaIf []messageProcessorTagConfig `json:"delete_meta_if"` // List of meta infos that are removed when the condition is met
|
||||
AddFieldIf []messageProcessorTagConfig `json:"add_fields_if"` // List of fields that are added when the condition is met
|
||||
DelFieldIf []messageProcessorTagConfig `json:"delete_fields_if"` // List of fields that are removed when the condition is met
|
||||
DropByType []string `json:"drop_by_message_type"` // List of message types that should be dropped
|
||||
MoveTagToMeta []messageProcessorTagConfig `json:"move_tag_to_meta_if"`
|
||||
MoveTagToField []messageProcessorTagConfig `json:"move_tag_to_field_if"`
|
||||
MoveMetaToTag []messageProcessorTagConfig `json:"move_meta_to_tag_if"`
|
||||
MoveMetaToField []messageProcessorTagConfig `json:"move_meta_to_field_if"`
|
||||
MoveFieldToTag []messageProcessorTagConfig `json:"move_field_to_tag_if"`
|
||||
MoveFieldToMeta []messageProcessorTagConfig `json:"move_field_to_meta_if"`
|
||||
AddBaseEnv map[string]interface{} `json:"add_base_env"`
|
||||
}
|
||||
|
||||
type messageProcessor struct {
|
||||
|
||||
// For thread-safety
|
||||
mutex sync.RWMutex
|
||||
|
||||
// mapping contains all evalables as strings to gval.Evaluable
|
||||
// because it is not possible to get the original string out of
|
||||
// a gval.Evaluable
|
||||
mapping map[string]*vm.Program
|
||||
|
||||
stages []string // order of stage execution
|
||||
dropMessages map[string]struct{} // internal lookup map
|
||||
dropTypes map[string]struct{} // internal lookup map
|
||||
dropMessagesIf map[*vm.Program]struct{} // pre-processed dropMessagesIf
|
||||
renameMessages map[string]string // internal lookup map
|
||||
renameMessagesIf map[*vm.Program]string // pre-processed RenameMessagesIf
|
||||
changeUnitPrefix map[*vm.Program]string // pre-processed ChangeUnitPrefix
|
||||
normalizeUnits bool
|
||||
addTagsIf map[*vm.Program]messageProcessorTagConfig // pre-processed AddTagsIf
|
||||
deleteTagsIf map[*vm.Program]messageProcessorTagConfig // pre-processed DelTagsIf
|
||||
addMetaIf map[*vm.Program]messageProcessorTagConfig // pre-processed AddMetaIf
|
||||
deleteMetaIf map[*vm.Program]messageProcessorTagConfig // pre-processed DelMetaIf
|
||||
addFieldIf map[*vm.Program]messageProcessorTagConfig // pre-processed AddFieldIf
|
||||
deleteFieldIf map[*vm.Program]messageProcessorTagConfig // pre-processed DelFieldIf
|
||||
moveTagToMeta map[*vm.Program]messageProcessorTagConfig // pre-processed MoveTagToMeta
|
||||
moveTagToField map[*vm.Program]messageProcessorTagConfig // pre-processed MoveTagToField
|
||||
moveMetaToTag map[*vm.Program]messageProcessorTagConfig // pre-processed MoveMetaToTag
|
||||
moveMetaToField map[*vm.Program]messageProcessorTagConfig // pre-processed MoveMetaToField
|
||||
moveFieldToTag map[*vm.Program]messageProcessorTagConfig // pre-processed MoveFieldToTag
|
||||
moveFieldToMeta map[*vm.Program]messageProcessorTagConfig // pre-processed MoveFieldToMeta
|
||||
}
|
||||
|
||||
type MessageProcessor interface {
|
||||
// Functions to set the execution order of the processing stages
|
||||
SetStages([]string) error
|
||||
DefaultStages() []string
|
||||
// Function to add variables to the base evaluation environment
|
||||
AddBaseEnv(env map[string]interface{}) error
|
||||
// Functions to add and remove rules
|
||||
AddDropMessagesByName(name string) error
|
||||
RemoveDropMessagesByName(name string)
|
||||
AddDropMessagesByCondition(condition string) error
|
||||
RemoveDropMessagesByCondition(condition string)
|
||||
AddRenameMetricByCondition(condition string, name string) error
|
||||
RemoveRenameMetricByCondition(condition string)
|
||||
AddRenameMetricByName(from, to string) error
|
||||
RemoveRenameMetricByName(from string)
|
||||
SetNormalizeUnits(settings bool)
|
||||
AddChangeUnitPrefix(condition string, prefix string) error
|
||||
RemoveChangeUnitPrefix(condition string)
|
||||
AddAddTagsByCondition(condition, key, value string) error
|
||||
RemoveAddTagsByCondition(condition string)
|
||||
AddDeleteTagsByCondition(condition, key, value string) error
|
||||
RemoveDeleteTagsByCondition(condition string)
|
||||
AddAddMetaByCondition(condition, key, value string) error
|
||||
RemoveAddMetaByCondition(condition string)
|
||||
AddDeleteMetaByCondition(condition, key, value string) error
|
||||
RemoveDeleteMetaByCondition(condition string)
|
||||
AddMoveTagToMeta(condition, key, value string) error
|
||||
RemoveMoveTagToMeta(condition string)
|
||||
AddMoveTagToFields(condition, key, value string) error
|
||||
RemoveMoveTagToFields(condition string)
|
||||
AddMoveMetaToTags(condition, key, value string) error
|
||||
RemoveMoveMetaToTags(condition string)
|
||||
AddMoveMetaToFields(condition, key, value string) error
|
||||
RemoveMoveMetaToFields(condition string)
|
||||
AddMoveFieldToTags(condition, key, value string) error
|
||||
RemoveMoveFieldToTags(condition string)
|
||||
AddMoveFieldToMeta(condition, key, value string) error
|
||||
RemoveMoveFieldToMeta(condition string)
|
||||
// Read in a JSON configuration
|
||||
FromConfigJSON(config json.RawMessage) error
|
||||
// Processing functions for legacy CCMetric and current CCMessage
|
||||
ProcessMetric(m lp.CCMetric) (lp2.CCMessage, error)
|
||||
ProcessMessage(m lp2.CCMessage) (lp2.CCMessage, error)
|
||||
//EvalToBool(condition string, parameters map[string]interface{}) (bool, error)
|
||||
//EvalToFloat64(condition string, parameters map[string]interface{}) (float64, error)
|
||||
//EvalToString(condition string, parameters map[string]interface{}) (string, error)
|
||||
}
|
||||
|
||||
const (
|
||||
STAGENAME_DROP_BY_NAME string = "drop_by_name"
|
||||
STAGENAME_DROP_BY_TYPE string = "drop_by_type"
|
||||
STAGENAME_DROP_IF string = "drop_if"
|
||||
STAGENAME_ADD_TAG string = "add_tag"
|
||||
STAGENAME_DELETE_TAG string = "delete_tag"
|
||||
STAGENAME_MOVE_TAG_META string = "move_tag_to_meta"
|
||||
STAGENAME_MOVE_TAG_FIELD string = "move_tag_to_fields"
|
||||
STAGENAME_ADD_META string = "add_meta"
|
||||
STAGENAME_DELETE_META string = "delete_meta"
|
||||
STAGENAME_MOVE_META_TAG string = "move_meta_to_tags"
|
||||
STAGENAME_MOVE_META_FIELD string = "move_meta_to_fields"
|
||||
STAGENAME_ADD_FIELD string = "add_field"
|
||||
STAGENAME_DELETE_FIELD string = "delete_field"
|
||||
STAGENAME_MOVE_FIELD_TAG string = "move_field_to_tags"
|
||||
STAGENAME_MOVE_FIELD_META string = "move_field_to_meta"
|
||||
STAGENAME_RENAME_BY_NAME string = "rename"
|
||||
STAGENAME_RENAME_IF string = "rename_if"
|
||||
STAGENAME_CHANGE_UNIT_PREFIX string = "change_unit_prefix"
|
||||
STAGENAME_NORMALIZE_UNIT string = "normalize_unit"
|
||||
)
|
||||
|
||||
var StageNames = []string{
|
||||
STAGENAME_DROP_BY_NAME,
|
||||
STAGENAME_DROP_BY_TYPE,
|
||||
STAGENAME_DROP_IF,
|
||||
STAGENAME_ADD_TAG,
|
||||
STAGENAME_DELETE_TAG,
|
||||
STAGENAME_MOVE_TAG_META,
|
||||
STAGENAME_MOVE_TAG_FIELD,
|
||||
STAGENAME_ADD_META,
|
||||
STAGENAME_DELETE_META,
|
||||
STAGENAME_MOVE_META_TAG,
|
||||
STAGENAME_MOVE_META_FIELD,
|
||||
STAGENAME_ADD_FIELD,
|
||||
STAGENAME_DELETE_FIELD,
|
||||
STAGENAME_MOVE_FIELD_TAG,
|
||||
STAGENAME_MOVE_FIELD_META,
|
||||
STAGENAME_RENAME_BY_NAME,
|
||||
STAGENAME_RENAME_IF,
|
||||
STAGENAME_CHANGE_UNIT_PREFIX,
|
||||
STAGENAME_NORMALIZE_UNIT,
|
||||
}
|
||||
|
||||
var paramMapPool = sync.Pool{
|
||||
New: func() any {
|
||||
return make(map[string]interface{})
|
||||
},
|
||||
}
|
||||
|
||||
func sanitizeExprString(key string) string {
|
||||
return strings.ReplaceAll(key, "type-id", "typeid")
|
||||
}
|
||||
|
||||
func getParamMap(point lp.CCMetric) map[string]interface{} {
|
||||
params := paramMapPool.Get().(map[string]interface{})
|
||||
params["message"] = point
|
||||
params["msg"] = point
|
||||
params["name"] = point.Name()
|
||||
params["timestamp"] = point.Time().Unix()
|
||||
params["time"] = params["timestamp"]
|
||||
|
||||
fields := paramMapPool.Get().(map[string]interface{})
|
||||
for key, value := range point.Fields() {
|
||||
fields[key] = value
|
||||
switch key {
|
||||
case "value":
|
||||
params["messagetype"] = "metric"
|
||||
params["value"] = value
|
||||
params["metric"] = value
|
||||
case "event":
|
||||
params["messagetype"] = "event"
|
||||
params["event"] = value
|
||||
case "control":
|
||||
params["messagetype"] = "control"
|
||||
params["control"] = value
|
||||
case "log":
|
||||
params["messagetype"] = "log"
|
||||
params["log"] = value
|
||||
default:
|
||||
params["messagetype"] = "unknown"
|
||||
}
|
||||
}
|
||||
params["msgtype"] = params["messagetype"]
|
||||
params["fields"] = fields
|
||||
params["field"] = fields
|
||||
tags := paramMapPool.Get().(map[string]interface{})
|
||||
for key, value := range point.Tags() {
|
||||
tags[sanitizeExprString(key)] = value
|
||||
}
|
||||
params["tags"] = tags
|
||||
params["tag"] = tags
|
||||
meta := paramMapPool.Get().(map[string]interface{})
|
||||
for key, value := range point.Meta() {
|
||||
meta[sanitizeExprString(key)] = value
|
||||
}
|
||||
params["meta"] = meta
|
||||
return params
|
||||
}
|
||||
|
||||
var baseenv = map[string]interface{}{
|
||||
"name": "",
|
||||
"messagetype": "unknown",
|
||||
"msgtype": "unknown",
|
||||
"tag": map[string]interface{}{
|
||||
"type": "unknown",
|
||||
"typeid": "0",
|
||||
"stype": "unknown",
|
||||
"stypeid": "0",
|
||||
"hostname": "localhost",
|
||||
"cluster": "nocluster",
|
||||
},
|
||||
"tags": map[string]interface{}{
|
||||
"type": "unknown",
|
||||
"typeid": "0",
|
||||
"stype": "unknown",
|
||||
"stypeid": "0",
|
||||
"hostname": "localhost",
|
||||
"cluster": "nocluster",
|
||||
},
|
||||
"meta": map[string]interface{}{
|
||||
"unit": "invalid",
|
||||
"source": "unknown",
|
||||
},
|
||||
"fields": map[string]interface{}{
|
||||
"value": 0,
|
||||
"event": "",
|
||||
"control": "",
|
||||
"log": "",
|
||||
},
|
||||
"field": map[string]interface{}{
|
||||
"value": 0,
|
||||
"event": "",
|
||||
"control": "",
|
||||
"log": "",
|
||||
},
|
||||
"timestamp": 1234567890,
|
||||
"msg": lp2.EmptyMessage(),
|
||||
"message": lp2.EmptyMessage(),
|
||||
}
|
||||
|
||||
func addBaseEnvWalker(values map[string]interface{}) map[string]interface{} {
|
||||
out := make(map[string]interface{})
|
||||
for k, v := range values {
|
||||
switch value := v.(type) {
|
||||
case int, int32, int64, uint, uint32, uint64, string, float32, float64:
|
||||
out[k] = value
|
||||
case map[string]interface{}:
|
||||
if _, ok := baseenv[k]; !ok {
|
||||
out[k] = addBaseEnvWalker(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) AddBaseEnv(env map[string]interface{}) error {
|
||||
for k, v := range env {
|
||||
switch value := v.(type) {
|
||||
case int, int32, int64, uint, uint32, uint64, string, float32, float64:
|
||||
baseenv[k] = value
|
||||
case map[string]interface{}:
|
||||
if _, ok := baseenv[k]; !ok {
|
||||
baseenv[k] = addBaseEnvWalker(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) init() error {
|
||||
mp.stages = make([]string, 0)
|
||||
mp.mapping = make(map[string]*vm.Program)
|
||||
mp.dropMessages = make(map[string]struct{})
|
||||
mp.dropTypes = make(map[string]struct{})
|
||||
mp.dropMessagesIf = make(map[*vm.Program]struct{})
|
||||
mp.renameMessages = make(map[string]string)
|
||||
mp.renameMessagesIf = make(map[*vm.Program]string)
|
||||
mp.changeUnitPrefix = make(map[*vm.Program]string)
|
||||
mp.addTagsIf = make(map[*vm.Program]messageProcessorTagConfig)
|
||||
mp.addMetaIf = make(map[*vm.Program]messageProcessorTagConfig)
|
||||
mp.addFieldIf = make(map[*vm.Program]messageProcessorTagConfig)
|
||||
mp.deleteTagsIf = make(map[*vm.Program]messageProcessorTagConfig)
|
||||
mp.deleteMetaIf = make(map[*vm.Program]messageProcessorTagConfig)
|
||||
mp.deleteFieldIf = make(map[*vm.Program]messageProcessorTagConfig)
|
||||
mp.moveFieldToMeta = make(map[*vm.Program]messageProcessorTagConfig)
|
||||
mp.moveFieldToTag = make(map[*vm.Program]messageProcessorTagConfig)
|
||||
mp.moveMetaToField = make(map[*vm.Program]messageProcessorTagConfig)
|
||||
mp.moveMetaToTag = make(map[*vm.Program]messageProcessorTagConfig)
|
||||
mp.moveTagToField = make(map[*vm.Program]messageProcessorTagConfig)
|
||||
mp.moveTagToMeta = make(map[*vm.Program]messageProcessorTagConfig)
|
||||
mp.normalizeUnits = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) AddDropMessagesByName(name string) error {
|
||||
mp.mutex.Lock()
|
||||
if _, ok := mp.dropMessages[name]; !ok {
|
||||
mp.dropMessages[name] = struct{}{}
|
||||
}
|
||||
mp.mutex.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) RemoveDropMessagesByName(name string) {
|
||||
mp.mutex.Lock()
|
||||
delete(mp.dropMessages, name)
|
||||
mp.mutex.Unlock()
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) AddDropMessagesByType(typestring string) error {
|
||||
valid := []string{"metric", "event", "control", "log"}
|
||||
isValid := false
|
||||
for _, t := range valid {
|
||||
if t == typestring {
|
||||
isValid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if isValid {
|
||||
mp.mutex.Lock()
|
||||
if _, ok := mp.dropTypes[typestring]; !ok {
|
||||
cclog.ComponentDebug("MessageProcessor", "Adding type", typestring, "for dropping")
|
||||
mp.dropTypes[typestring] = struct{}{}
|
||||
}
|
||||
mp.mutex.Unlock()
|
||||
} else {
|
||||
return fmt.Errorf("invalid message type %s", typestring)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) RemoveDropMessagesByType(typestring string) {
|
||||
mp.mutex.Lock()
|
||||
delete(mp.dropTypes, typestring)
|
||||
mp.mutex.Unlock()
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) addTagConfig(condition, key, value string, config *map[*vm.Program]messageProcessorTagConfig) error {
|
||||
var err error
|
||||
evaluable, err := expr.Compile(sanitizeExprString(condition), expr.Env(baseenv), expr.AsBool())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create condition evaluable of '%s': %v", condition, err.Error())
|
||||
}
|
||||
mp.mutex.Lock()
|
||||
if _, ok := (*config)[evaluable]; !ok {
|
||||
mp.mapping[condition] = evaluable
|
||||
(*config)[evaluable] = messageProcessorTagConfig{
|
||||
Condition: condition,
|
||||
Key: key,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
mp.mutex.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) removeTagConfig(condition string, config *map[*vm.Program]messageProcessorTagConfig) {
|
||||
mp.mutex.Lock()
|
||||
if e, ok := mp.mapping[condition]; ok {
|
||||
delete(mp.mapping, condition)
|
||||
delete(*config, e)
|
||||
}
|
||||
mp.mutex.Unlock()
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) AddAddTagsByCondition(condition, key, value string) error {
|
||||
return mp.addTagConfig(condition, key, value, &mp.addTagsIf)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) RemoveAddTagsByCondition(condition string) {
|
||||
mp.removeTagConfig(condition, &mp.addTagsIf)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) AddDeleteTagsByCondition(condition, key, value string) error {
|
||||
return mp.addTagConfig(condition, key, value, &mp.deleteTagsIf)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) RemoveDeleteTagsByCondition(condition string) {
|
||||
mp.removeTagConfig(condition, &mp.deleteTagsIf)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) AddAddMetaByCondition(condition, key, value string) error {
|
||||
return mp.addTagConfig(condition, key, value, &mp.addMetaIf)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) RemoveAddMetaByCondition(condition string) {
|
||||
mp.removeTagConfig(condition, &mp.addMetaIf)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) AddDeleteMetaByCondition(condition, key, value string) error {
|
||||
return mp.addTagConfig(condition, key, value, &mp.deleteMetaIf)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) RemoveDeleteMetaByCondition(condition string) {
|
||||
mp.removeTagConfig(condition, &mp.deleteMetaIf)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) AddAddFieldByCondition(condition, key, value string) error {
|
||||
return mp.addTagConfig(condition, key, value, &mp.addFieldIf)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) RemoveAddFieldByCondition(condition string) {
|
||||
mp.removeTagConfig(condition, &mp.addFieldIf)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) AddDeleteFieldByCondition(condition, key, value string) error {
|
||||
return mp.addTagConfig(condition, key, value, &mp.deleteFieldIf)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) RemoveDeleteFieldByCondition(condition string) {
|
||||
mp.removeTagConfig(condition, &mp.deleteFieldIf)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) AddDropMessagesByCondition(condition string) error {
|
||||
|
||||
var err error
|
||||
evaluable, err := expr.Compile(sanitizeExprString(condition), expr.Env(baseenv), expr.AsBool())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create condition evaluable of '%s': %v", condition, err.Error())
|
||||
}
|
||||
mp.mutex.Lock()
|
||||
if _, ok := mp.dropMessagesIf[evaluable]; !ok {
|
||||
mp.mapping[condition] = evaluable
|
||||
mp.dropMessagesIf[evaluable] = struct{}{}
|
||||
}
|
||||
mp.mutex.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) RemoveDropMessagesByCondition(condition string) {
|
||||
mp.mutex.Lock()
|
||||
if e, ok := mp.mapping[condition]; ok {
|
||||
delete(mp.mapping, condition)
|
||||
delete(mp.dropMessagesIf, e)
|
||||
}
|
||||
mp.mutex.Unlock()
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) AddRenameMetricByCondition(condition string, name string) error {
|
||||
|
||||
var err error
|
||||
evaluable, err := expr.Compile(sanitizeExprString(condition), expr.Env(baseenv), expr.AsBool())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create condition evaluable of '%s': %v", condition, err.Error())
|
||||
}
|
||||
mp.mutex.Lock()
|
||||
if _, ok := mp.renameMessagesIf[evaluable]; !ok {
|
||||
mp.mapping[condition] = evaluable
|
||||
mp.renameMessagesIf[evaluable] = name
|
||||
} else {
|
||||
mp.renameMessagesIf[evaluable] = name
|
||||
}
|
||||
mp.mutex.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) RemoveRenameMetricByCondition(condition string) {
|
||||
mp.mutex.Lock()
|
||||
if e, ok := mp.mapping[condition]; ok {
|
||||
delete(mp.mapping, condition)
|
||||
delete(mp.renameMessagesIf, e)
|
||||
}
|
||||
mp.mutex.Unlock()
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) SetNormalizeUnits(setting bool) {
|
||||
mp.normalizeUnits = setting
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) AddChangeUnitPrefix(condition string, prefix string) error {
|
||||
|
||||
var err error
|
||||
evaluable, err := expr.Compile(sanitizeExprString(condition), expr.Env(baseenv), expr.AsBool())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create condition evaluable of '%s': %v", condition, err.Error())
|
||||
}
|
||||
mp.mutex.Lock()
|
||||
if _, ok := mp.changeUnitPrefix[evaluable]; !ok {
|
||||
mp.mapping[condition] = evaluable
|
||||
mp.changeUnitPrefix[evaluable] = prefix
|
||||
} else {
|
||||
mp.changeUnitPrefix[evaluable] = prefix
|
||||
}
|
||||
mp.mutex.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) RemoveChangeUnitPrefix(condition string) {
|
||||
mp.mutex.Lock()
|
||||
if e, ok := mp.mapping[condition]; ok {
|
||||
delete(mp.mapping, condition)
|
||||
delete(mp.changeUnitPrefix, e)
|
||||
}
|
||||
mp.mutex.Unlock()
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) AddRenameMetricByName(from, to string) error {
|
||||
mp.mutex.Lock()
|
||||
if _, ok := mp.renameMessages[from]; !ok {
|
||||
mp.renameMessages[from] = to
|
||||
}
|
||||
mp.mutex.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) RemoveRenameMetricByName(from string) {
|
||||
mp.mutex.Lock()
|
||||
delete(mp.renameMessages, from)
|
||||
mp.mutex.Unlock()
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) AddMoveTagToMeta(condition, key, value string) error {
|
||||
return mp.addTagConfig(condition, key, value, &mp.moveTagToMeta)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) RemoveMoveTagToMeta(condition string) {
|
||||
mp.removeTagConfig(condition, &mp.moveTagToMeta)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) AddMoveTagToFields(condition, key, value string) error {
|
||||
return mp.addTagConfig(condition, key, value, &mp.moveTagToField)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) RemoveMoveTagToFields(condition string) {
|
||||
mp.removeTagConfig(condition, &mp.moveTagToField)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) AddMoveMetaToTags(condition, key, value string) error {
|
||||
return mp.addTagConfig(condition, key, value, &mp.moveMetaToTag)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) RemoveMoveMetaToTags(condition string) {
|
||||
mp.removeTagConfig(condition, &mp.moveMetaToTag)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) AddMoveMetaToFields(condition, key, value string) error {
|
||||
return mp.addTagConfig(condition, key, value, &mp.moveMetaToField)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) RemoveMoveMetaToFields(condition string) {
|
||||
mp.removeTagConfig(condition, &mp.moveMetaToField)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) AddMoveFieldToTags(condition, key, value string) error {
|
||||
return mp.addTagConfig(condition, key, value, &mp.moveFieldToTag)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) RemoveMoveFieldToTags(condition string) {
|
||||
mp.removeTagConfig(condition, &mp.moveFieldToTag)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) AddMoveFieldToMeta(condition, key, value string) error {
|
||||
return mp.addTagConfig(condition, key, value, &mp.moveFieldToMeta)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) RemoveMoveFieldToMeta(condition string) {
|
||||
mp.removeTagConfig(condition, &mp.moveFieldToMeta)
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) SetStages(stages []string) error {
|
||||
newstages := make([]string, 0)
|
||||
if len(stages) == 0 {
|
||||
mp.mutex.Lock()
|
||||
mp.stages = newstages
|
||||
mp.mutex.Unlock()
|
||||
return nil
|
||||
}
|
||||
for i, s := range stages {
|
||||
valid := false
|
||||
for _, v := range StageNames {
|
||||
if s == v {
|
||||
valid = true
|
||||
}
|
||||
}
|
||||
if valid {
|
||||
newstages = append(newstages, s)
|
||||
} else {
|
||||
return fmt.Errorf("invalid stage %s at index %d", s, i)
|
||||
}
|
||||
}
|
||||
mp.mutex.Lock()
|
||||
mp.stages = newstages
|
||||
mp.mutex.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) DefaultStages() []string {
|
||||
return StageNames
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) FromConfigJSON(config json.RawMessage) error {
|
||||
var c messageProcessorConfig
|
||||
|
||||
err := json.Unmarshal(config, &c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
|
||||
if len(c.StageOrder) > 0 {
|
||||
err = mp.SetStages(c.StageOrder)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
} else {
|
||||
err = mp.SetStages(mp.DefaultStages())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
for _, m := range c.DropMessages {
|
||||
err = mp.AddDropMessagesByName(m)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
}
|
||||
for _, m := range c.DropByType {
|
||||
err = mp.AddDropMessagesByType(m)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
}
|
||||
for _, m := range c.DropMessagesIf {
|
||||
err = mp.AddDropMessagesByCondition(m)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
}
|
||||
for k, v := range c.RenameMessagesIf {
|
||||
err = mp.AddRenameMetricByCondition(k, v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
}
|
||||
for k, v := range c.RenameMessages {
|
||||
err = mp.AddRenameMetricByName(k, v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
}
|
||||
for k, v := range c.ChangeUnitPrefix {
|
||||
err = mp.AddChangeUnitPrefix(k, v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
}
|
||||
for _, c := range c.AddTagsIf {
|
||||
err = mp.AddAddTagsByCondition(c.Condition, c.Key, c.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
}
|
||||
for _, c := range c.AddMetaIf {
|
||||
err = mp.AddAddMetaByCondition(c.Condition, c.Key, c.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
}
|
||||
for _, c := range c.AddFieldIf {
|
||||
err = mp.AddAddFieldByCondition(c.Condition, c.Key, c.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
}
|
||||
for _, c := range c.DelTagsIf {
|
||||
err = mp.AddDeleteTagsByCondition(c.Condition, c.Key, c.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
}
|
||||
for _, c := range c.DelMetaIf {
|
||||
err = mp.AddDeleteMetaByCondition(c.Condition, c.Key, c.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
}
|
||||
for _, c := range c.DelFieldIf {
|
||||
err = mp.AddDeleteFieldByCondition(c.Condition, c.Key, c.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
}
|
||||
for _, c := range c.MoveTagToMeta {
|
||||
err = mp.AddMoveTagToMeta(c.Condition, c.Key, c.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
}
|
||||
for _, c := range c.MoveTagToField {
|
||||
err = mp.AddMoveTagToFields(c.Condition, c.Key, c.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
}
|
||||
for _, c := range c.MoveMetaToTag {
|
||||
err = mp.AddMoveMetaToTags(c.Condition, c.Key, c.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
}
|
||||
for _, c := range c.MoveMetaToField {
|
||||
err = mp.AddMoveMetaToFields(c.Condition, c.Key, c.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
}
|
||||
for _, c := range c.MoveFieldToTag {
|
||||
err = mp.AddMoveFieldToTags(c.Condition, c.Key, c.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
}
|
||||
for _, c := range c.MoveFieldToMeta {
|
||||
err = mp.AddMoveFieldToMeta(c.Condition, c.Key, c.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
}
|
||||
for _, m := range c.DropByType {
|
||||
err = mp.AddDropMessagesByType(m)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
}
|
||||
if len(c.AddBaseEnv) > 0 {
|
||||
err = mp.AddBaseEnv(c.AddBaseEnv)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process config JSON: %v", err.Error())
|
||||
}
|
||||
}
|
||||
mp.SetNormalizeUnits(c.NormalizeUnits)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) ProcessMetric(metric lp.CCMetric) (lp2.CCMessage, error) {
|
||||
m, err := lp2.NewMessage(
|
||||
metric.Name(),
|
||||
metric.Tags(),
|
||||
metric.Meta(),
|
||||
metric.Fields(),
|
||||
metric.Time(),
|
||||
)
|
||||
if err != nil {
|
||||
return m, fmt.Errorf("failed to parse metric to message: %v", err.Error())
|
||||
}
|
||||
return mp.ProcessMessage(m)
|
||||
|
||||
}
|
||||
|
||||
func (mp *messageProcessor) ProcessMessage(m lp2.CCMessage) (lp2.CCMessage, error) {
|
||||
var err error = nil
|
||||
var out lp2.CCMessage = lp2.FromMessage(m)
|
||||
|
||||
name := out.Name()
|
||||
|
||||
if len(mp.stages) == 0 {
|
||||
mp.SetStages(mp.DefaultStages())
|
||||
}
|
||||
|
||||
mp.mutex.RLock()
|
||||
defer mp.mutex.RUnlock()
|
||||
|
||||
params := getParamMap(out)
|
||||
|
||||
defer func() {
|
||||
params["field"] = nil
|
||||
params["tag"] = nil
|
||||
paramMapPool.Put(params["fields"])
|
||||
paramMapPool.Put(params["tags"])
|
||||
paramMapPool.Put(params["meta"])
|
||||
paramMapPool.Put(params)
|
||||
}()
|
||||
|
||||
for _, s := range mp.stages {
|
||||
switch s {
|
||||
case STAGENAME_DROP_BY_NAME:
|
||||
if len(mp.dropMessages) > 0 {
|
||||
cclog.ComponentDebug("MessageProcessor", "Dropping by message name ", name)
|
||||
if _, ok := mp.dropMessages[name]; ok {
|
||||
cclog.ComponentDebug("MessageProcessor", "Drop")
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
case STAGENAME_DROP_BY_TYPE:
|
||||
if len(mp.dropTypes) > 0 {
|
||||
cclog.ComponentDebug("MessageProcessor", "Dropping by message type")
|
||||
if _, ok := mp.dropTypes[params["messagetype"].(string)]; ok {
|
||||
cclog.ComponentDebug("MessageProcessor", "Drop")
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
case STAGENAME_DROP_IF:
|
||||
if len(mp.dropMessagesIf) > 0 {
|
||||
cclog.ComponentDebug("MessageProcessor", "Dropping by condition")
|
||||
drop, err := dropMessagesIf(¶ms, &mp.dropMessagesIf)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("failed to evaluate: %v", err.Error())
|
||||
}
|
||||
if drop {
|
||||
cclog.ComponentDebug("MessageProcessor", "Drop")
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
case STAGENAME_RENAME_BY_NAME:
|
||||
if len(mp.renameMessages) > 0 {
|
||||
cclog.ComponentDebug("MessageProcessor", "Renaming by name match")
|
||||
if newname, ok := mp.renameMessages[name]; ok {
|
||||
cclog.ComponentDebug("MessageProcessor", "Rename to", newname)
|
||||
out.SetName(newname)
|
||||
cclog.ComponentDebug("MessageProcessor", "Add old name as 'oldname' to meta", name)
|
||||
out.AddMeta("oldname", name)
|
||||
}
|
||||
}
|
||||
case STAGENAME_RENAME_IF:
|
||||
if len(mp.renameMessagesIf) > 0 {
|
||||
cclog.ComponentDebug("MessageProcessor", "Renaming by condition")
|
||||
_, err := renameMessagesIf(out, ¶ms, &mp.renameMessagesIf)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("failed to evaluate: %v", err.Error())
|
||||
}
|
||||
}
|
||||
case STAGENAME_ADD_TAG:
|
||||
if len(mp.addTagsIf) > 0 {
|
||||
cclog.ComponentDebug("MessageProcessor", "Adding tags")
|
||||
_, err = addTagIf(out, ¶ms, &mp.addTagsIf)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("failed to evaluate: %v", err.Error())
|
||||
}
|
||||
}
|
||||
case STAGENAME_DELETE_TAG:
|
||||
if len(mp.deleteTagsIf) > 0 {
|
||||
cclog.ComponentDebug("MessageProcessor", "Delete tags")
|
||||
_, err = deleteTagIf(out, ¶ms, &mp.deleteTagsIf)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("failed to evaluate: %v", err.Error())
|
||||
}
|
||||
}
|
||||
case STAGENAME_ADD_META:
|
||||
if len(mp.addMetaIf) > 0 {
|
||||
cclog.ComponentDebug("MessageProcessor", "Adding meta information")
|
||||
_, err = addMetaIf(out, ¶ms, &mp.addMetaIf)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("failed to evaluate: %v", err.Error())
|
||||
}
|
||||
}
|
||||
case STAGENAME_DELETE_META:
|
||||
if len(mp.deleteMetaIf) > 0 {
|
||||
cclog.ComponentDebug("MessageProcessor", "Delete meta information")
|
||||
_, err = deleteMetaIf(out, ¶ms, &mp.deleteMetaIf)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("failed to evaluate: %v", err.Error())
|
||||
}
|
||||
}
|
||||
case STAGENAME_ADD_FIELD:
|
||||
if len(mp.addFieldIf) > 0 {
|
||||
cclog.ComponentDebug("MessageProcessor", "Adding fields")
|
||||
_, err = addFieldIf(out, ¶ms, &mp.addFieldIf)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("failed to evaluate: %v", err.Error())
|
||||
}
|
||||
}
|
||||
case STAGENAME_DELETE_FIELD:
|
||||
if len(mp.deleteFieldIf) > 0 {
|
||||
cclog.ComponentDebug("MessageProcessor", "Delete fields")
|
||||
_, err = deleteFieldIf(out, ¶ms, &mp.deleteFieldIf)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("failed to evaluate: %v", err.Error())
|
||||
}
|
||||
}
|
||||
case STAGENAME_MOVE_TAG_META:
|
||||
if len(mp.moveTagToMeta) > 0 {
|
||||
cclog.ComponentDebug("MessageProcessor", "Move tag to meta")
|
||||
_, err := moveTagToMeta(out, ¶ms, &mp.moveTagToMeta)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("failed to evaluate: %v", err.Error())
|
||||
}
|
||||
}
|
||||
case STAGENAME_MOVE_TAG_FIELD:
|
||||
if len(mp.moveTagToField) > 0 {
|
||||
cclog.ComponentDebug("MessageProcessor", "Move tag to fields")
|
||||
_, err := moveTagToField(out, ¶ms, &mp.moveTagToField)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("failed to evaluate: %v", err.Error())
|
||||
}
|
||||
}
|
||||
case STAGENAME_MOVE_META_TAG:
|
||||
if len(mp.moveMetaToTag) > 0 {
|
||||
cclog.ComponentDebug("MessageProcessor", "Move meta to tags")
|
||||
_, err := moveMetaToTag(out, ¶ms, &mp.moveMetaToTag)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("failed to evaluate: %v", err.Error())
|
||||
}
|
||||
}
|
||||
case STAGENAME_MOVE_META_FIELD:
|
||||
if len(mp.moveMetaToField) > 0 {
|
||||
cclog.ComponentDebug("MessageProcessor", "Move meta to fields")
|
||||
_, err := moveMetaToField(out, ¶ms, &mp.moveMetaToField)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("failed to evaluate: %v", err.Error())
|
||||
}
|
||||
}
|
||||
case STAGENAME_MOVE_FIELD_META:
|
||||
if len(mp.moveFieldToMeta) > 0 {
|
||||
cclog.ComponentDebug("MessageProcessor", "Move field to meta")
|
||||
_, err := moveFieldToMeta(out, ¶ms, &mp.moveFieldToMeta)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("failed to evaluate: %v", err.Error())
|
||||
}
|
||||
}
|
||||
case STAGENAME_MOVE_FIELD_TAG:
|
||||
if len(mp.moveFieldToTag) > 0 {
|
||||
cclog.ComponentDebug("MessageProcessor", "Move field to tags")
|
||||
_, err := moveFieldToTag(out, ¶ms, &mp.moveFieldToTag)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("failed to evaluate: %v", err.Error())
|
||||
}
|
||||
}
|
||||
case STAGENAME_NORMALIZE_UNIT:
|
||||
if mp.normalizeUnits {
|
||||
cclog.ComponentDebug("MessageProcessor", "Normalize units")
|
||||
if lp2.IsMetric(out) {
|
||||
_, err := normalizeUnits(out)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("failed to evaluate: %v", err.Error())
|
||||
}
|
||||
} else {
|
||||
cclog.ComponentDebug("MessageProcessor", "skipped, no metric")
|
||||
}
|
||||
}
|
||||
|
||||
case STAGENAME_CHANGE_UNIT_PREFIX:
|
||||
if len(mp.changeUnitPrefix) > 0 {
|
||||
cclog.ComponentDebug("MessageProcessor", "Change unit prefix")
|
||||
if lp2.IsMetric(out) {
|
||||
_, err := changeUnitPrefix(out, ¶ms, &mp.changeUnitPrefix)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("failed to evaluate: %v", err.Error())
|
||||
}
|
||||
} else {
|
||||
cclog.ComponentDebug("MessageProcessor", "skipped, no metric")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Get a new instace of a message processor.
|
||||
func NewMessageProcessor() (MessageProcessor, error) {
|
||||
mp := new(messageProcessor)
|
||||
err := mp.init()
|
||||
if err != nil {
|
||||
err := fmt.Errorf("failed to create MessageProcessor: %v", err.Error())
|
||||
cclog.ComponentError("MessageProcessor", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
return mp, nil
|
||||
}
|
265
pkg/messageProcessor/messageProcessorFuncs.go
Normal file
265
pkg/messageProcessor/messageProcessorFuncs.go
Normal file
@ -0,0 +1,265 @@
|
||||
package messageprocessor
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
lp2 "github.com/ClusterCockpit/cc-energy-manager/pkg/cc-message"
|
||||
cclog "github.com/ClusterCockpit/cc-metric-collector/pkg/ccLogger"
|
||||
units "github.com/ClusterCockpit/cc-units"
|
||||
"github.com/expr-lang/expr"
|
||||
"github.com/expr-lang/expr/vm"
|
||||
)
|
||||
|
||||
type MessageLocation int
|
||||
|
||||
const (
|
||||
MESSAGE_LOCATION_TAGS MessageLocation = iota
|
||||
MESSAGE_LOCATION_META
|
||||
MESSAGE_LOCATION_FIELDS
|
||||
)
|
||||
|
||||
// Abstract function to move entries from one location to another
|
||||
func moveInMessage(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig, from, to MessageLocation) (bool, error) {
|
||||
for d, data := range *checks {
|
||||
value, err := expr.Run(d, *params)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to evaluate: %v", err.Error())
|
||||
}
|
||||
cclog.ComponentDebug("MessageProcessor", "Move from", from, "to", to)
|
||||
if value.(bool) {
|
||||
var v string
|
||||
var ok bool = false
|
||||
switch from {
|
||||
case MESSAGE_LOCATION_TAGS:
|
||||
cclog.ComponentDebug("MessageProcessor", "Getting tag key", data.Key)
|
||||
v, ok = message.GetTag(data.Key)
|
||||
case MESSAGE_LOCATION_META:
|
||||
cclog.ComponentDebug("MessageProcessor", "Getting meta key", data.Key)
|
||||
cclog.ComponentDebug("MessageProcessor", message.Meta())
|
||||
v, ok = message.GetMeta(data.Key)
|
||||
case MESSAGE_LOCATION_FIELDS:
|
||||
var x interface{}
|
||||
cclog.ComponentDebug("MessageProcessor", "Getting field key", data.Key)
|
||||
x, ok = message.GetField(data.Key)
|
||||
v = fmt.Sprintf("%v", x)
|
||||
}
|
||||
if ok {
|
||||
switch from {
|
||||
case MESSAGE_LOCATION_TAGS:
|
||||
cclog.ComponentDebug("MessageProcessor", "Removing tag key", data.Key)
|
||||
message.RemoveTag(data.Key)
|
||||
case MESSAGE_LOCATION_META:
|
||||
cclog.ComponentDebug("MessageProcessor", "Removing meta key", data.Key)
|
||||
message.RemoveMeta(data.Key)
|
||||
case MESSAGE_LOCATION_FIELDS:
|
||||
cclog.ComponentDebug("MessageProcessor", "Removing field key", data.Key)
|
||||
message.RemoveField(data.Key)
|
||||
}
|
||||
switch to {
|
||||
case MESSAGE_LOCATION_TAGS:
|
||||
cclog.ComponentDebug("MessageProcessor", "Adding tag", data.Value, "->", v)
|
||||
message.AddTag(data.Value, v)
|
||||
case MESSAGE_LOCATION_META:
|
||||
cclog.ComponentDebug("MessageProcessor", "Adding meta", data.Value, "->", v)
|
||||
message.AddMeta(data.Value, v)
|
||||
case MESSAGE_LOCATION_FIELDS:
|
||||
cclog.ComponentDebug("MessageProcessor", "Adding field", data.Value, "->", v)
|
||||
message.AddField(data.Value, v)
|
||||
}
|
||||
} else {
|
||||
return false, fmt.Errorf("failed to get message entry: %s", data.Key)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func deleteIf(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig, location MessageLocation) (bool, error) {
|
||||
for d, data := range *checks {
|
||||
value, err := expr.Run(d, *params)
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("failed to evaluate: %v", err.Error())
|
||||
}
|
||||
if value.(bool) {
|
||||
switch location {
|
||||
case MESSAGE_LOCATION_FIELDS:
|
||||
switch data.Key {
|
||||
case "value", "event", "log", "control":
|
||||
return false, errors.New("cannot delete protected fields")
|
||||
default:
|
||||
cclog.ComponentDebug("MessageProcessor", "Removing field for", data.Key)
|
||||
message.RemoveField(data.Key)
|
||||
}
|
||||
case MESSAGE_LOCATION_TAGS:
|
||||
cclog.ComponentDebug("MessageProcessor", "Removing tag for", data.Key)
|
||||
message.RemoveTag(data.Key)
|
||||
case MESSAGE_LOCATION_META:
|
||||
cclog.ComponentDebug("MessageProcessor", "Removing meta for", data.Key)
|
||||
message.RemoveMeta(data.Key)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func addIf(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig, location MessageLocation) (bool, error) {
|
||||
for d, data := range *checks {
|
||||
value, err := expr.Run(d, *params)
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("failed to evaluate: %v", err.Error())
|
||||
}
|
||||
if value.(bool) {
|
||||
switch location {
|
||||
case MESSAGE_LOCATION_FIELDS:
|
||||
cclog.ComponentDebug("MessageProcessor", "Adding field", data.Value, "->", data.Value)
|
||||
message.AddField(data.Key, data.Value)
|
||||
case MESSAGE_LOCATION_TAGS:
|
||||
cclog.ComponentDebug("MessageProcessor", "Adding tag", data.Value, "->", data.Value)
|
||||
message.AddTag(data.Key, data.Value)
|
||||
case MESSAGE_LOCATION_META:
|
||||
cclog.ComponentDebug("MessageProcessor", "Adding meta", data.Value, "->", data.Value)
|
||||
message.AddMeta(data.Key, data.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func deleteTagIf(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) {
|
||||
return deleteIf(message, params, checks, MESSAGE_LOCATION_TAGS)
|
||||
}
|
||||
|
||||
func addTagIf(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) {
|
||||
return addIf(message, params, checks, MESSAGE_LOCATION_TAGS)
|
||||
}
|
||||
|
||||
func moveTagToMeta(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) {
|
||||
return moveInMessage(message, params, checks, MESSAGE_LOCATION_TAGS, MESSAGE_LOCATION_META)
|
||||
}
|
||||
|
||||
func moveTagToField(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) {
|
||||
return moveInMessage(message, params, checks, MESSAGE_LOCATION_TAGS, MESSAGE_LOCATION_FIELDS)
|
||||
}
|
||||
|
||||
func deleteMetaIf(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) {
|
||||
return deleteIf(message, params, checks, MESSAGE_LOCATION_META)
|
||||
}
|
||||
|
||||
func addMetaIf(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) {
|
||||
return addIf(message, params, checks, MESSAGE_LOCATION_META)
|
||||
}
|
||||
|
||||
func moveMetaToTag(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) {
|
||||
return moveInMessage(message, params, checks, MESSAGE_LOCATION_META, MESSAGE_LOCATION_TAGS)
|
||||
}
|
||||
|
||||
func moveMetaToField(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) {
|
||||
return moveInMessage(message, params, checks, MESSAGE_LOCATION_META, MESSAGE_LOCATION_FIELDS)
|
||||
}
|
||||
|
||||
func deleteFieldIf(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) {
|
||||
return deleteIf(message, params, checks, MESSAGE_LOCATION_FIELDS)
|
||||
}
|
||||
|
||||
func addFieldIf(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) {
|
||||
return addIf(message, params, checks, MESSAGE_LOCATION_FIELDS)
|
||||
}
|
||||
|
||||
func moveFieldToTag(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) {
|
||||
return moveInMessage(message, params, checks, MESSAGE_LOCATION_FIELDS, MESSAGE_LOCATION_TAGS)
|
||||
}
|
||||
|
||||
func moveFieldToMeta(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) {
|
||||
return moveInMessage(message, params, checks, MESSAGE_LOCATION_FIELDS, MESSAGE_LOCATION_META)
|
||||
}
|
||||
|
||||
func dropMessagesIf(params *map[string]interface{}, checks *map[*vm.Program]struct{}) (bool, error) {
|
||||
for d := range *checks {
|
||||
value, err := expr.Run(d, *params)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to evaluate: %v", err.Error())
|
||||
}
|
||||
if value.(bool) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func normalizeUnits(message lp2.CCMessage) (bool, error) {
|
||||
if in_unit, ok := message.GetMeta("unit"); ok {
|
||||
u := units.NewUnit(in_unit)
|
||||
if u.Valid() {
|
||||
cclog.ComponentDebug("MessageProcessor", "Update unit with", u.Short())
|
||||
message.AddMeta("unit", u.Short())
|
||||
}
|
||||
} else if in_unit, ok := message.GetTag("unit"); ok {
|
||||
u := units.NewUnit(in_unit)
|
||||
if u.Valid() {
|
||||
cclog.ComponentDebug("MessageProcessor", "Update unit with", u.Short())
|
||||
message.AddTag("unit", u.Short())
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func changeUnitPrefix(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]string) (bool, error) {
|
||||
for r, n := range *checks {
|
||||
value, err := expr.Run(r, *params)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to evaluate: %v", err.Error())
|
||||
}
|
||||
if value.(bool) {
|
||||
newPrefix := units.NewPrefix(n)
|
||||
cclog.ComponentDebug("MessageProcessor", "Condition matches, change to prefix", newPrefix.String())
|
||||
if in_unit, ok := message.GetMeta("unit"); ok && newPrefix != units.InvalidPrefix {
|
||||
u := units.NewUnit(in_unit)
|
||||
if u.Valid() {
|
||||
cclog.ComponentDebug("MessageProcessor", "Input unit", u.Short())
|
||||
conv, out_unit := units.GetUnitPrefixFactor(u, newPrefix)
|
||||
if conv != nil && out_unit.Valid() {
|
||||
if val, ok := message.GetField("value"); ok {
|
||||
cclog.ComponentDebug("MessageProcessor", "Update unit with", out_unit.Short())
|
||||
message.AddField("value", conv(val))
|
||||
message.AddMeta("unit", out_unit.Short())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if in_unit, ok := message.GetTag("unit"); ok && newPrefix != units.InvalidPrefix {
|
||||
u := units.NewUnit(in_unit)
|
||||
if u.Valid() {
|
||||
cclog.ComponentDebug("MessageProcessor", "Input unit", u.Short())
|
||||
conv, out_unit := units.GetUnitPrefixFactor(u, newPrefix)
|
||||
if conv != nil && out_unit.Valid() {
|
||||
if val, ok := message.GetField("value"); ok {
|
||||
cclog.ComponentDebug("MessageProcessor", "Update unit with", out_unit.Short())
|
||||
message.AddField("value", conv(val))
|
||||
message.AddTag("unit", out_unit.Short())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func renameMessagesIf(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]string) (bool, error) {
|
||||
for d, n := range *checks {
|
||||
value, err := expr.Run(d, *params)
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("failed to evaluate: %v", err.Error())
|
||||
}
|
||||
if value.(bool) {
|
||||
old := message.Name()
|
||||
cclog.ComponentDebug("MessageProcessor", "Rename to", n)
|
||||
message.SetName(n)
|
||||
cclog.ComponentDebug("MessageProcessor", "Add old name as 'oldname' to meta", old)
|
||||
message.AddMeta("oldname", old)
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
396
pkg/messageProcessor/messageProcessor_test.go
Normal file
396
pkg/messageProcessor/messageProcessor_test.go
Normal file
@ -0,0 +1,396 @@
|
||||
package messageprocessor
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
lp "github.com/ClusterCockpit/cc-energy-manager/pkg/cc-message"
|
||||
cclog "github.com/ClusterCockpit/cc-metric-collector/pkg/ccLogger"
|
||||
)
|
||||
|
||||
func generate_message_lists(num_lists, num_entries int) ([][]lp.CCMessage, error) {
|
||||
mlist := make([][]lp.CCMessage, 0)
|
||||
for j := 0; j < num_lists; j++ {
|
||||
out := make([]lp.CCMessage, 0)
|
||||
for i := 0; i < num_entries; i++ {
|
||||
var x lp.CCMessage
|
||||
var err error = nil
|
||||
switch {
|
||||
case i%4 == 0:
|
||||
x, err = lp.NewEvent("myevent", map[string]string{"type": "socket", "type-id": "0"}, map[string]string{}, "nothing happend", time.Now())
|
||||
case i%4 == 1:
|
||||
x, err = lp.NewMetric("mymetric", map[string]string{"type": "socket", "type-id": "0"}, map[string]string{"unit": "kByte"}, 12.145, time.Now())
|
||||
case i%4 == 2:
|
||||
x, err = lp.NewLog("mylog", map[string]string{"type": "socket", "type-id": "0"}, map[string]string{}, "disk status: OK", time.Now())
|
||||
case i%4 == 3:
|
||||
x, err = lp.NewGetControl("mycontrol", map[string]string{"type": "socket", "type-id": "0"}, map[string]string{}, time.Now())
|
||||
}
|
||||
if err == nil {
|
||||
x.AddTag("hostname", "myhost")
|
||||
out = append(out, x)
|
||||
} else {
|
||||
return nil, errors.New("failed to create message")
|
||||
}
|
||||
}
|
||||
mlist = append(mlist, out)
|
||||
}
|
||||
return mlist, nil
|
||||
}
|
||||
|
||||
func TestNewMessageProcessor(t *testing.T) {
|
||||
_, err := NewMessageProcessor()
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
type Configs struct {
|
||||
name string
|
||||
config json.RawMessage
|
||||
drop bool
|
||||
errors bool
|
||||
pre func(msg lp.CCMessage) error
|
||||
check func(msg lp.CCMessage) error
|
||||
}
|
||||
|
||||
var test_configs = []Configs{
|
||||
{
|
||||
name: "single_dropif_nomatch",
|
||||
config: json.RawMessage(`{"drop_messages_if": [ "name == 'testname' && tags.type == 'socket' && tags.typeid % 2 == 1"]}`),
|
||||
},
|
||||
{
|
||||
name: "drop_by_name",
|
||||
config: json.RawMessage(`{"drop_messages": [ "net_bytes_in"]}`),
|
||||
drop: true,
|
||||
},
|
||||
{
|
||||
name: "drop_by_type_match",
|
||||
config: json.RawMessage(`{"drop_by_message_type": [ "metric"]}`),
|
||||
drop: true,
|
||||
},
|
||||
{
|
||||
name: "drop_by_type_nomatch",
|
||||
config: json.RawMessage(`{"drop_by_message_type": [ "event"]}`),
|
||||
},
|
||||
{
|
||||
name: "single_dropif_match",
|
||||
config: json.RawMessage(`{"drop_messages_if": [ "name == 'net_bytes_in' && tags.type == 'node'"]}`),
|
||||
drop: true,
|
||||
},
|
||||
{
|
||||
name: "double_dropif_match_nomatch",
|
||||
config: json.RawMessage(`{"drop_messages_if": [ "name == 'net_bytes_in' && tags.type == 'node'", "name == 'testname' && tags.type == 'socket' && tags.typeid % 2 == 1"]}`),
|
||||
drop: true,
|
||||
},
|
||||
{
|
||||
name: "rename_simple",
|
||||
config: json.RawMessage(`{"rename_messages": { "net_bytes_in" : "net_bytes_out", "rapl_power": "cpu_power"}}`),
|
||||
check: func(msg lp.CCMessage) error {
|
||||
if msg.Name() != "net_bytes_out" {
|
||||
return errors.New("expected name net_bytes_out but still have net_bytes_in")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rename_match",
|
||||
config: json.RawMessage(`{"rename_messages_if": { "name == 'net_bytes_in'" : "net_bytes_out", "name == 'rapl_power'": "cpu_power"}}`),
|
||||
check: func(msg lp.CCMessage) error {
|
||||
if msg.Name() != "net_bytes_out" {
|
||||
return errors.New("expected name net_bytes_out but still have net_bytes_in")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rename_nomatch",
|
||||
config: json.RawMessage(`{"rename_messages_if": { "name == 'net_bytes_out'" : "net_bytes_in", "name == 'rapl_power'": "cpu_power"}}`),
|
||||
check: func(msg lp.CCMessage) error {
|
||||
if msg.Name() != "net_bytes_in" {
|
||||
return errors.New("expected name net_bytes_in but still have net_bytes_out")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add_tag",
|
||||
config: json.RawMessage(`{"add_tags_if": [{"if": "name == 'net_bytes_in'", "key" : "cluster", "value" : "mycluster"}]}`),
|
||||
check: func(msg lp.CCMessage) error {
|
||||
if !msg.HasTag("cluster") {
|
||||
return errors.New("expected new tag 'cluster' but not present")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "del_tag",
|
||||
config: json.RawMessage(`{"delete_tags_if": [{"if": "name == 'net_bytes_in'", "key" : "type"}]}`),
|
||||
check: func(msg lp.CCMessage) error {
|
||||
if msg.HasTag("type") {
|
||||
return errors.New("expected to have no 'type' but still present")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add_meta",
|
||||
config: json.RawMessage(`{"add_meta_if": [{"if": "name == 'net_bytes_in'", "key" : "source", "value" : "example"}]}`),
|
||||
check: func(msg lp.CCMessage) error {
|
||||
if !msg.HasMeta("source") {
|
||||
return errors.New("expected new tag 'source' but not present")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "del_meta",
|
||||
config: json.RawMessage(`{"delete_meta_if": [{"if": "name == 'net_bytes_in'", "key" : "unit"}]}`),
|
||||
check: func(msg lp.CCMessage) error {
|
||||
if msg.HasMeta("unit") {
|
||||
return errors.New("expected to have no 'unit' but still present")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add_field",
|
||||
config: json.RawMessage(`{"add_fields_if": [{"if": "name == 'net_bytes_in'", "key" : "myfield", "value" : "example"}]}`),
|
||||
check: func(msg lp.CCMessage) error {
|
||||
if !msg.HasField("myfield") {
|
||||
return errors.New("expected new tag 'source' but not present")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "delete_fields_if_protected",
|
||||
config: json.RawMessage(`{"delete_fields_if": [{"if": "name == 'net_bytes_in'", "key" : "value"}]}`),
|
||||
errors: true,
|
||||
check: func(msg lp.CCMessage) error {
|
||||
if !msg.HasField("value") {
|
||||
return errors.New("expected to still have 'value' field because it is a protected field key")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "delete_fields_if_unprotected",
|
||||
config: json.RawMessage(`{"delete_fields_if": [{"if": "name == 'net_bytes_in'", "key" : "testfield"}]}`),
|
||||
check: func(msg lp.CCMessage) error {
|
||||
if msg.HasField("testfield") {
|
||||
return errors.New("expected to still have 'testfield' field but should be deleted")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
pre: func(msg lp.CCMessage) error {
|
||||
msg.AddField("testfield", 4.123)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "single_change_prefix_match",
|
||||
config: json.RawMessage(`{"change_unit_prefix": {"name == 'net_bytes_in' && tags.type == 'node'": "M"}}`),
|
||||
check: func(msg lp.CCMessage) error {
|
||||
if u, ok := msg.GetMeta("unit"); ok {
|
||||
if u != "MB" {
|
||||
return fmt.Errorf("expected unit MB but have %s", u)
|
||||
}
|
||||
} else if u, ok := msg.GetTag("unit"); ok {
|
||||
if u != "MB" {
|
||||
return fmt.Errorf("expected unit MB but have %s", u)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "normalize_units",
|
||||
config: json.RawMessage(`{"normalize_units": true}`),
|
||||
check: func(msg lp.CCMessage) error {
|
||||
if u, ok := msg.GetMeta("unit"); ok {
|
||||
if u != "B" {
|
||||
return fmt.Errorf("expected unit B but have %s", u)
|
||||
}
|
||||
} else if u, ok := msg.GetTag("unit"); ok {
|
||||
if u != "B" {
|
||||
return fmt.Errorf("expected unit B but have %s", u)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "move_tag_to_meta",
|
||||
config: json.RawMessage(`{"move_tag_to_meta_if": [{"if": "name == 'net_bytes_in'", "key" : "type-id", "value": "typeid"}]}`),
|
||||
check: func(msg lp.CCMessage) error {
|
||||
if msg.HasTag("type-id") || !msg.HasMeta("typeid") {
|
||||
return errors.New("moving tag 'type-id' to meta 'typeid' failed")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
pre: func(msg lp.CCMessage) error {
|
||||
msg.AddTag("type-id", "0")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "move_tag_to_field",
|
||||
config: json.RawMessage(`{"move_tag_to_field_if": [{"if": "name == 'net_bytes_in'", "key" : "type-id", "value": "typeid"}]}`),
|
||||
check: func(msg lp.CCMessage) error {
|
||||
if msg.HasTag("type-id") || !msg.HasField("typeid") {
|
||||
return errors.New("moving tag 'type-id' to field 'typeid' failed")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
pre: func(msg lp.CCMessage) error {
|
||||
msg.AddTag("type-id", "0")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "move_meta_to_tag",
|
||||
config: json.RawMessage(`{"move_meta_to_tag_if": [{"if": "name == 'net_bytes_in'", "key" : "unit", "value": "unit"}]}`),
|
||||
check: func(msg lp.CCMessage) error {
|
||||
if msg.HasMeta("unit") || !msg.HasTag("unit") {
|
||||
return errors.New("moving meta 'unit' to tag 'unit' failed")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "move_meta_to_field",
|
||||
config: json.RawMessage(`{"move_meta_to_field_if": [{"if": "name == 'net_bytes_in'", "key" : "unit", "value": "unit"}]}`),
|
||||
check: func(msg lp.CCMessage) error {
|
||||
if msg.HasMeta("unit") || !msg.HasField("unit") {
|
||||
return errors.New("moving meta 'unit' to field 'unit' failed")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "move_field_to_tag",
|
||||
config: json.RawMessage(`{"move_field_to_tag_if": [{"if": "name == 'net_bytes_in'", "key" : "myfield", "value": "field"}]}`),
|
||||
check: func(msg lp.CCMessage) error {
|
||||
if msg.HasField("myfield") || !msg.HasTag("field") {
|
||||
return errors.New("moving meta 'myfield' to tag 'field' failed")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
pre: func(msg lp.CCMessage) error {
|
||||
msg.AddField("myfield", 12)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "move_field_to_meta",
|
||||
config: json.RawMessage(`{"move_field_to_meta_if": [{"if": "name == 'net_bytes_in'", "key" : "myfield", "value": "field"}]}`),
|
||||
check: func(msg lp.CCMessage) error {
|
||||
if msg.HasField("myfield") || !msg.HasMeta("field") {
|
||||
return errors.New("moving meta 'myfield' to meta 'field' failed")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
pre: func(msg lp.CCMessage) error {
|
||||
msg.AddField("myfield", 12)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestConfigList(t *testing.T) {
|
||||
for _, c := range test_configs {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
m, err := lp.NewMetric("net_bytes_in", map[string]string{"type": "node", "type-id": "0"}, map[string]string{"unit": "Byte"}, float64(1024.0), time.Now())
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
return
|
||||
}
|
||||
if c.pre != nil {
|
||||
if err = c.pre(m); err != nil {
|
||||
t.Errorf("error running pre-test function: %v", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
mp, err := NewMessageProcessor()
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
return
|
||||
}
|
||||
err = mp.FromConfigJSON(c.config)
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
return
|
||||
}
|
||||
//t.Log(m.ToLineProtocol(nil))
|
||||
out, err := mp.ProcessMessage(m)
|
||||
if err != nil && !c.errors {
|
||||
cclog.SetDebug()
|
||||
mp.ProcessMessage(m)
|
||||
t.Error(err.Error())
|
||||
return
|
||||
}
|
||||
if out == nil && !c.drop {
|
||||
t.Error("fail, message should NOT be dropped but processor signalled dropping")
|
||||
return
|
||||
} else if out != nil && c.drop {
|
||||
t.Error("fail, message should be dropped but processor signalled NO dropping")
|
||||
return
|
||||
}
|
||||
// {
|
||||
// if c.drop {
|
||||
// t.Error("fail, message should be dropped but processor signalled NO dropping")
|
||||
// } else {
|
||||
// t.Error("fail, message should NOT be dropped but processor signalled dropping")
|
||||
// }
|
||||
// cclog.SetDebug()
|
||||
// mp.ProcessMessage(m)
|
||||
// return
|
||||
// }
|
||||
if c.check != nil {
|
||||
if err := c.check(out); err != nil {
|
||||
t.Errorf("check failed with %v", err.Error())
|
||||
t.Log("Rerun with debugging")
|
||||
cclog.SetDebug()
|
||||
mp.ProcessMessage(m)
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkProcessing(b *testing.B) {
|
||||
|
||||
mlist, err := generate_message_lists(b.N, 1000)
|
||||
if err != nil {
|
||||
b.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
mp, err := NewMessageProcessor()
|
||||
if err != nil {
|
||||
b.Error(err.Error())
|
||||
return
|
||||
}
|
||||
err = mp.FromConfigJSON(json.RawMessage(`{"move_meta_to_tag_if": [{"if" : "name == 'mymetric'", "key":"unit", "value":"unit"}]}`))
|
||||
if err != nil {
|
||||
b.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, m := range mlist[i] {
|
||||
if _, err := mp.ProcessMessage(m); err != nil {
|
||||
b.Errorf("failed processing message '%s': %v", m.ToLineProtocol(nil), err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
b.StopTimer()
|
||||
b.ReportMetric(float64(b.Elapsed())/float64(len(mlist)*b.N), "ns/message")
|
||||
}
|
Loading…
Reference in New Issue
Block a user