Support logging in starlark (#8408)

This commit is contained in:
Nicolas Filotto 2020-11-16 21:22:40 +01:00 committed by GitHub
parent dc782805da
commit 0c15569174
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 101 additions and 2 deletions

View File

@ -96,6 +96,7 @@ While Starlark is similar to Python, there are important differences to note:
The ability to load external scripts other than your own is pretty limited. The following libraries are available for loading:
* json: `load("json.star", "json")` provides the following functions: `json.encode()`, `json.decode()`, `json.indent()`. See [json.star](/plugins/processors/starlark/testdata/json.star) for an example.
* log: `load("logging.star", "log")` provides the following functions: `log.debug()`, `log.info()`, `log.warn()`, `log.error()`. See [logging.star](/plugins/processors/starlark/testdata/logging.star) for an example.
If you would like to see support for something else here, please open an issue.
@ -185,6 +186,7 @@ def failing(metric):
- [rename](/plugins/processors/starlark/testdata/rename.star) - Rename tags or fields using a name mapping.
- [scale](/plugins/processors/starlark/testdata/scale.star) - Multiply any field by a number
- [value filter](/plugins/processors/starlark/testdata/value_filter.star) - remove a metric based on a field value.
- [logging](/plugins/processors/starlark/testdata/logging.star) - Log messages with the logger of Telegraf
[All examples](/plugins/processors/starlark/testdata) are in the testdata folder.

View File

@ -0,0 +1,47 @@
package starlark
import (
"errors"
"fmt"
"github.com/influxdata/telegraf"
"go.starlark.net/starlark"
"go.starlark.net/starlarkstruct"
)
// Builds a module that defines all the supported logging functions which will log using the provided logger
func LogModule(logger telegraf.Logger) *starlarkstruct.Module {
var logFunc = func(t *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
return log(t, b, args, kwargs, logger)
}
return &starlarkstruct.Module{
Name: "log",
Members: starlark.StringDict{
"debug": starlark.NewBuiltin("log.debug", logFunc),
"info": starlark.NewBuiltin("log.info", logFunc),
"warn": starlark.NewBuiltin("log.warn", logFunc),
"error": starlark.NewBuiltin("log.error", logFunc),
},
}
}
// Logs the provided message according to the level chosen
func log(t *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple, logger telegraf.Logger) (starlark.Value, error) {
var msg starlark.String
if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 1, &msg); err != nil {
return starlark.None, fmt.Errorf("%s: %v", b.Name(), err)
}
switch b.Name() {
case "log.debug":
logger.Debug(string(msg))
case "log.info":
logger.Info(string(msg))
case "log.warn":
logger.Warn(string(msg))
case "log.error":
logger.Error(string(msg))
default:
return nil, errors.New("method " + b.Name() + " is unknown")
}
return starlark.None, nil
}

View File

@ -52,7 +52,9 @@ func (s *Starlark) Init() error {
s.thread = &starlark.Thread{
Print: func(_ *starlark.Thread, msg string) { s.Log.Debug(msg) },
Load: loadFunc,
Load: func(thread *starlark.Thread, module string) (starlark.StringDict, error) {
return loadFunc(thread, module, s.Log)
},
}
builtins := starlark.StringDict{}
@ -217,12 +219,16 @@ func init() {
})
}
func loadFunc(thread *starlark.Thread, module string) (starlark.StringDict, error) {
func loadFunc(thread *starlark.Thread, module string, logger telegraf.Logger) (starlark.StringDict, error) {
switch module {
case "json.star":
return starlark.StringDict{
"json": starlarkjson.Module,
}, nil
case "logging.star":
return starlark.StringDict{
"log": LogModule(logger),
}, nil
default:
return nil, errors.New("module " + module + " is not available")
}

View File

@ -2535,6 +2535,31 @@ func TestScript(t *testing.T) {
),
},
},
{
name: "logging",
plugin: &Starlark{
Script: "testdata/logging.star",
Log: testutil.Logger{},
},
input: []telegraf.Metric{
testutil.MustMetric("log",
map[string]string{},
map[string]interface{}{
"debug": "a debug message",
},
time.Unix(0, 0),
),
},
expected: []telegraf.Metric{
testutil.MustMetric("log",
map[string]string{},
map[string]interface{}{
"debug": "a debug message",
},
time.Unix(0, 0),
),
},
},
}
for _, tt := range tests {

View File

@ -0,0 +1,19 @@
# Example of the way to log a message with all the supported levels
# using the logger of Telegraf.
#
# Example Input:
# log debug="a debug message" 1465839830100400201
#
# Example Output:
# log debug="a debug message" 1465839830100400201
load("logging.star", "log")
# loads log.debug(), log.info(), log.warn(), log.error()
def apply(metric):
log.debug("debug: {}".format(metric.fields["debug"]))
log.info("an info message")
log.warn("a warning message")
log.error("an error message")
return metric