tflog

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 24, 2021 License: MPL-2.0 Imports: 5 Imported by: 0

README

PkgGoDev

terraform-plugin-log

terraform-plugin-log is a helper module for logging from Terraform providers. It uses RPC-specific loggers to attach context and information to logs, and has multiple loggers to allow filtering of log output, making finding what you're looking for easier. It is a small wrapper on top of go-hclog, adding some conventions and reframing things for Terraform plugin developers.

Go Compatibility

terraform-plugin-log is built in Go, and uses the support policy of Go as its support policy. The two latest major releases of Go are supported by terraform-plugin-log.

Currently, that means Go 1.15 or later must be used when using terraform-plugin-log.

Contributing

See .github/CONTRIBUTING.md

License

Mozilla Public License v2.0

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

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Debug

func Debug(ctx context.Context, msg string, args ...interface{})

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

func Error(ctx context.Context, msg string, args ...interface{})

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

func Info(ctx context.Context, msg string, args ...interface{})

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

func New(ctx context.Context, options ...Option) context.Context

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

func NewSubsystem(ctx context.Context, subsystem string, options ...Option) context.Context

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

func SubsystemDebug(ctx context.Context, subsystem, msg string, args ...interface{})

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

func SubsystemError(ctx context.Context, subsystem, msg string, args ...interface{})

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

func SubsystemInfo(ctx context.Context, subsystem, msg string, args ...interface{})

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

func SubsystemTrace(ctx context.Context, subsystem, msg string, args ...interface{})

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

func SubsystemWarn(ctx context.Context, subsystem, msg string, args ...interface{})

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

func SubsystemWith(ctx context.Context, subsystem, key string, value interface{}) context.Context

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

func Trace(ctx context.Context, msg string, args ...interface{})

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

func Warn(ctx context.Context, msg string, args ...interface{})

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

func With(ctx context.Context, key string, value interface{}) context.Context

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

func WithLevelFromEnv(name string, subsystems ...string) Option

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

func WithLogName(name string) Option

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.

Directories

Path Synopsis
Package tfsdklog provides helper functions for logging from SDKs and frameworks for building Terraform plugins.
Package tfsdklog provides helper functions for logging from SDKs and frameworks for building Terraform plugins.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL