Documentation ¶
Overview ¶
Package tflog provides helper functions for writing log output and creating loggers for Terraform plugins.
For most plugin authors, building on an SDK or framework, the SDK or framework will take care of injecting a logger using New.
tflog also allows plugin authors to create subsystem loggers, which are loggers for sufficiently distinct areas of the codebase or concerns. The benefit of using distinct loggers for these concerns is doing so allows plugin authors and practitioners to configure different log levels for each subsystem's log, allowing log output to be turned on or off without recompiling.
Index ¶
- func Debug(ctx context.Context, msg string, args ...interface{})
- func Error(ctx context.Context, msg string, args ...interface{})
- func Info(ctx context.Context, msg string, args ...interface{})
- func New(ctx context.Context, options ...Option) context.Context
- func NewSubsystem(ctx context.Context, subsystem string, options ...Option) context.Context
- func SubsystemDebug(ctx context.Context, subsystem, msg string, args ...interface{})
- func SubsystemError(ctx context.Context, subsystem, msg string, args ...interface{})
- func SubsystemInfo(ctx context.Context, subsystem, msg string, args ...interface{})
- func SubsystemTrace(ctx context.Context, subsystem, msg string, args ...interface{})
- func SubsystemWarn(ctx context.Context, subsystem, msg string, args ...interface{})
- func SubsystemWith(ctx context.Context, subsystem, key string, value interface{}) context.Context
- func Trace(ctx context.Context, msg string, args ...interface{})
- func Warn(ctx context.Context, msg string, args ...interface{})
- func With(ctx context.Context, key string, value interface{}) context.Context
- type Option
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Debug ¶
Debug logs `msg` at the debug level to the logger in `ctx`, with `args` as structured arguments in the log output. `args` is expected to be pairs of key and value.
Example ¶
// virtually no plugin developers will need to worry about // instantiating loggers, as the libraries they're using will take care // of that, but we're not using those libraries in these examples. So // we need to do the injection ourselves. Plugin developers will // basically never need to do this, so the next line can safely be // considered setup for the example and ignored. Instead, use the // context passed in by the framework or library you're using. exampleCtx := getExampleContext() // non-example-setup code begins here Debug(exampleCtx, "hello, world", "foo", 123, "colors", []string{"red", "blue", "green"})
Output: {"@level":"debug","@message":"hello, world","colors":["red","blue","green"],"foo":123}
func Error ¶
Error logs `msg` at the error level to the logger in `ctx`, with `args` as structured arguments in the log output. `args` is expected to be pairs of key and value.
Example ¶
// virtually no plugin developers will need to worry about // instantiating loggers, as the libraries they're using will take care // of that, but we're not using those libraries in these examples. So // we need to do the injection ourselves. Plugin developers will // basically never need to do this, so the next line can safely be // considered setup for the example and ignored. Instead, use the // context passed in by the framework or library you're using. exampleCtx := getExampleContext() // non-example-setup code begins here Error(exampleCtx, "hello, world", "foo", 123, "colors", []string{"red", "blue", "green"})
Output: {"@level":"error","@message":"hello, world","colors":["red","blue","green"],"foo":123}
func Info ¶
Info logs `msg` at the info level to the logger in `ctx`, with `args` as structured arguments in the log output. `args` is expected to be pairs of key and value.
Example ¶
// virtually no plugin developers will need to worry about // instantiating loggers, as the libraries they're using will take care // of that, but we're not using those libraries in these examples. So // we need to do the injection ourselves. Plugin developers will // basically never need to do this, so the next line can safely be // considered setup for the example and ignored. Instead, use the // context passed in by the framework or library you're using. exampleCtx := getExampleContext() // non-example-setup code begins here Info(exampleCtx, "hello, world", "foo", 123, "colors", []string{"red", "blue", "green"})
Output: {"@level":"info","@message":"hello, world","colors":["red","blue","green"],"foo":123}
func New ¶
New returns a new context.Context that contains a logger configured with the passed options.
This function isn't usually needed by plugin developers. Typically, the SDK or framework the plugin is built on will call New and configure a logger before handing off to the plugin code. This function is here for SDK and framework developers to do that setup work. Plugin developers should be able to safely assume that the logger already exists and just start using it.
func NewSubsystem ¶
NewSubsystem returns a new context.Context that contains a subsystem logger configured with the passed options, named after the subsystem argument.
Subsystem loggers allow different areas of a plugin codebase to use different logging levels, giving developers more fine-grained control over what is logging and with what verbosity. They're best utilized for logical concerns that are sometimes helpful to log, but may generate unwanted noise at other times.
The only Options supported for subsystems are the Options for setting the level of the logger.
Example ¶
// virtually no plugin developers will need to worry about // instantiating loggers, as the libraries they're using will take care // of that, but we're not using those libraries in these examples. So // we need to do the injection ourselves. Plugin developers will // basically never need to do this, so the next line can safely be // considered setup for the example and ignored. Instead, use the // context passed in by the framework or library you're using. exampleCtx := getExampleContext() // non-example-setup code begins here // register a new subsystem before using it subCtx := NewSubsystem(exampleCtx, "my-subsystem") // messages logged to the subsystem will carry the subsystem name with // them SubsystemTrace(subCtx, "my-subsystem", "hello, world", "foo", 123)
Output: {"@level":"trace","@message":"hello, world","@module":"my-subsystem","foo":123}
Example (WithLevel) ¶
// virtually no plugin developers will need to worry about // instantiating loggers, as the libraries they're using will take care // of that, but we're not using those libraries in these examples. So // we need to do the injection ourselves. Plugin developers will // basically never need to do this, so the next line can safely be // considered setup for the example and ignored. Instead, use the // context passed in by the framework or library you're using. exampleCtx := getExampleContext() // simulate the user setting a logging level for the subsystem os.Setenv("EXAMPLE_SUBSYSTEM_LEVEL", "WARN") // non-example-setup code begins here // create a context with a logger for a new "my-subsystem" subsystem, // using the WARN level from the "EXAMPLE_SUBSYSTEM_LEVEL" environment // variable subCtx := NewSubsystem(exampleCtx, "my-subsystem", WithLevelFromEnv("EXAMPLE_SUBSYSTEM_LEVEL")) // this won't actually get output, it's not at WARN or higher SubsystemTrace(subCtx, "my-subsystem", "hello, world", "foo", 123) // the parent logger will still output at its configured TRACE level, // though Trace(subCtx, "hello, world", "foo", 123) // and the subsystem logger will output at the WARN level SubsystemWarn(subCtx, "my-subsystem", "hello, world", "foo", 123)
Output: {"@level":"trace","@message":"hello, world","foo":123} {"@level":"warn","@message":"hello, world","@module":"my-subsystem","foo":123}
func SubsystemDebug ¶
SubsystemDebug logs `msg` at the debug level to the subsystem logger specified in `ctx`, with `args` as structured arguments in the log output. `args` is expected to be pairs of key and value.
Example ¶
// virtually no plugin developers will need to worry about // instantiating loggers, as the libraries they're using will take care // of that, but we're not using those libraries in these examples. So // we need to do the injection ourselves. Plugin developers will // basically never need to do this, so the next line can safely be // considered setup for the example and ignored. Instead, use the // context passed in by the framework or library you're using. exampleCtx := getExampleContext() // register a new subsystem before using it exampleCtx = NewSubsystem(exampleCtx, "my-subsystem") // non-example-setup code begins here SubsystemDebug(exampleCtx, "my-subsystem", "hello, world", "foo", 123, "colors", []string{"red", "blue", "green"})
Output: {"@level":"debug","@message":"hello, world","@module":"my-subsystem","colors":["red","blue","green"],"foo":123}
func SubsystemError ¶
SubsystemError logs `msg` at the error level to the subsystem logger specified in `ctx`, with `args` as structured arguments in the log output. `args` is expected to be pairs of key and value.
Example ¶
// virtually no plugin developers will need to worry about // instantiating loggers, as the libraries they're using will take care // of that, but we're not using those libraries in these examples. So // we need to do the injection ourselves. Plugin developers will // basically never need to do this, so the next line can safely be // considered setup for the example and ignored. Instead, use the // context passed in by the framework or library you're using. exampleCtx := getExampleContext() // register a new subsystem before using it exampleCtx = NewSubsystem(exampleCtx, "my-subsystem") // non-example-setup code begins here SubsystemError(exampleCtx, "my-subsystem", "hello, world", "foo", 123, "colors", []string{"red", "blue", "green"})
Output: {"@level":"error","@message":"hello, world","@module":"my-subsystem","colors":["red","blue","green"],"foo":123}
func SubsystemInfo ¶
SubsystemInfo logs `msg` at the info level to the subsystem logger specified in `ctx`, with `args` as structured arguments in the log output. `args` is expected to be pairs of key and value.
Example ¶
// virtually no plugin developers will need to worry about // instantiating loggers, as the libraries they're using will take care // of that, but we're not using those libraries in these examples. So // we need to do the injection ourselves. Plugin developers will // basically never need to do this, so the next line can safely be // considered setup for the example and ignored. Instead, use the // context passed in by the framework or library you're using. exampleCtx := getExampleContext() // register a new subsystem before using it exampleCtx = NewSubsystem(exampleCtx, "my-subsystem") // non-example-setup code begins here SubsystemInfo(exampleCtx, "my-subsystem", "hello, world", "foo", 123, "colors", []string{"red", "blue", "green"})
Output: {"@level":"info","@message":"hello, world","@module":"my-subsystem","colors":["red","blue","green"],"foo":123}
func SubsystemTrace ¶
SubsystemTrace logs `msg` at the trace level to the subsystem logger specified in `ctx`, with `args` as structured arguments in the log output. `args` is expected to be pairs of key and value.
Example ¶
// virtually no plugin developers will need to worry about // instantiating loggers, as the libraries they're using will take care // of that, but we're not using those libraries in these examples. So // we need to do the injection ourselves. Plugin developers will // basically never need to do this, so the next line can safely be // considered setup for the example and ignored. Instead, use the // context passed in by the framework or library you're using. exampleCtx := getExampleContext() // register a new subsystem before using it exampleCtx = NewSubsystem(exampleCtx, "my-subsystem") // non-example-setup code begins here SubsystemTrace(exampleCtx, "my-subsystem", "hello, world", "foo", 123, "colors", []string{"red", "blue", "green"})
Output: {"@level":"trace","@message":"hello, world","@module":"my-subsystem","colors":["red","blue","green"],"foo":123}
func SubsystemWarn ¶
SubsystemWarn logs `msg` at the warn level to the subsystem logger specified in `ctx`, with `args` as structured arguments in the log output. `args` is expected to be pairs of key and value.
Example ¶
// virtually no plugin developers will need to worry about // instantiating loggers, as the libraries they're using will take care // of that, but we're not using those libraries in these examples. So // we need to do the injection ourselves. Plugin developers will // basically never need to do this, so the next line can safely be // considered setup for the example and ignored. Instead, use the // context passed in by the framework or library you're using. exampleCtx := getExampleContext() // register a new subsystem before using it exampleCtx = NewSubsystem(exampleCtx, "my-subsystem") // non-example-setup code begins here SubsystemWarn(exampleCtx, "my-subsystem", "hello, world", "foo", 123, "colors", []string{"red", "blue", "green"})
Output: {"@level":"warn","@message":"hello, world","@module":"my-subsystem","colors":["red","blue","green"],"foo":123}
func SubsystemWith ¶
SubsystemWith returns a new context.Context that has a modified logger for the specified subsystem in it which will include key and value as arguments in all its log output.
Example ¶
// virtually no plugin developers will need to worry about // instantiating loggers, as the libraries they're using will take care // of that, but we're not using those libraries in these examples. So // we need to do the injection ourselves. Plugin developers will // basically never need to do this, so the next line can safely be // considered setup for the example and ignored. Instead, use the // context passed in by the framework or library you're using. exampleCtx := getExampleContext() // register a new subsystem before using it exampleCtx = NewSubsystem(exampleCtx, "my-subsystem") // non-example-setup code begins here // associate a key and value with all lines logged by the sub-logger derivedCtx := SubsystemWith(exampleCtx, "my-subsystem", "foo", 123) // all messages logged with derivedCtx will now have foo=123 // automatically included SubsystemTrace(derivedCtx, "my-subsystem", "example log message")
Output: {"@level":"trace","@message":"example log message","@module":"my-subsystem","foo":123}
func Trace ¶
Trace logs `msg` at the trace level to the logger in `ctx`, with `args` as structured arguments in the log output. `args` is expected to be pairs of key and value.
Example ¶
// virtually no plugin developers will need to worry about // instantiating loggers, as the libraries they're using will take care // of that, but we're not using those libraries in these examples. So // we need to do the injection ourselves. Plugin developers will // basically never need to do this, so the next line can safely be // considered setup for the example and ignored. Instead, use the // context passed in by the framework or library you're using. exampleCtx := getExampleContext() // non-example-setup code begins here Trace(exampleCtx, "hello, world", "foo", 123, "colors", []string{"red", "blue", "green"})
Output: {"@level":"trace","@message":"hello, world","colors":["red","blue","green"],"foo":123}
func Warn ¶
Warn logs `msg` at the warn level to the logger in `ctx`, with `args` as structured arguments in the log output. `args` is expected to be pairs of key and value.
Example ¶
// virtually no plugin developers will need to worry about // instantiating loggers, as the libraries they're using will take care // of that, but we're not using those libraries in these examples. So // we need to do the injection ourselves. Plugin developers will // basically never need to do this, so the next line can safely be // considered setup for the example and ignored. Instead, use the // context passed in by the framework or library you're using. exampleCtx := getExampleContext() // non-example-setup code begins here Warn(exampleCtx, "hello, world", "foo", 123, "colors", []string{"red", "blue", "green"})
Output: {"@level":"warn","@message":"hello, world","colors":["red","blue","green"],"foo":123}
func With ¶
With returns a new context.Context that has a modified logger in it which will include key and value as arguments in all its log output.
Example ¶
// virtually no plugin developers will need to worry about // instantiating loggers, as the libraries they're using will take care // of that, but we're not using those libraries in these examples. So // we need to do the injection ourselves. Plugin developers will // basically never need to do this, so the next line can safely be // considered setup for the example and ignored. Instead, use the // context passed in by the framework or library you're using. exampleCtx := getExampleContext() // non-example-setup code begins here derivedCtx := With(exampleCtx, "foo", 123) // all messages logged with derivedCtx will now have foo=123 // automatically included Trace(derivedCtx, "example log message")
Output: {"@level":"trace","@message":"example log message","foo":123}
Types ¶
type Option ¶
type Option func(loggerOpts) loggerOpts
Option defines a modification to the configuration for a logger.
func WithLevel ¶
func WithLevel(level hclog.Level) Option
WithLevel returns an option that will set the level of the logger.
func WithLevelFromEnv ¶
WithLevelFromEnv returns an option that will set the level of the logger based on the string in an environment variable. The environment variable checked will be `name` and `subsystems`, joined by _ and in all caps.
func WithLogName ¶
WithLogName returns an option that will set the logger name explicitly to `name`. This has no effect when used with NewSubsystem.
func WithStderrFromInit ¶
func WithStderrFromInit() Option
WithStderrFromInit returns an option that tells the logger to write to the os.Stderr that was present when the program started, not the one that is available at runtime. Some versions of Terraform overwrite os.Stderr with an io.Writer that is never read, so any log lines written to it will be lost.
func WithoutLocation ¶
func WithoutLocation() Option
WithoutLocation returns an option that disables including the location of the log line in the log output, which is on by default. This has no effect when used with NewSubsystem.