tfsdklog

package
Version: v0.7.0 Latest Latest
Warning

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

Go to latest
Published: Jul 25, 2022 License: MPL-2.0 Imports: 12 Imported by: 3

Documentation

Overview

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

Plugin authors shouldn't need to use this package; it is meant for authors of the frameworks and SDKs for plugins. Plugin authors should use the tflog package.

This package provides very similar functionality to tflog, except it uses a separate namespace for its logs.

Index

Examples

Constants

This section is empty.

Variables

View Source
var ValidLevels = []string{"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF"}

ValidLevels are the string representations of levels that can be set for loggers.

Functions

func Debug

func Debug(ctx context.Context, msg string, additionalFields ...map[string]interface{})

Debug logs `msg` at the debug level to the logger in `ctx`, with optional `additionalFields` structured key-value fields in the log output. Fields are shallow merged with any defined on the logger, e.g. by the `SetField()` function, and across multiple maps.

Example
// this function calls New with the options it needs to be reliably
// tested. Framework and SDK developers should call New, inject the
// resulting context in their framework, and then pass it around. This
// exampleCtx is a stand-in for a context you have injected a logger
// into and passed to the area of the codebase you need it.
exampleCtx := getExampleContext()

// non-example-setup code begins here
Debug(exampleCtx, "hello, world", map[string]interface{}{
	"foo":    123,
	"colors": []string{"red", "blue", "green"},
})
Output:

{"@level":"debug","@message":"hello, world","@module":"sdk","colors":["red","blue","green"],"foo":123}

func Error

func Error(ctx context.Context, msg string, additionalFields ...map[string]interface{})

Error logs `msg` at the error level to the logger in `ctx`, with optional `additionalFields` structured key-value fields in the log output. Fields are shallow merged with any defined on the logger, e.g. by the `SetField()` function, and across multiple maps.

Example
// this function calls New with the options it needs to be reliably
// tested. Framework and SDK developers should call New, inject the
// resulting context in their framework, and then pass it around. This
// exampleCtx is a stand-in for a context you have injected a logger
// into and passed to the area of the codebase you need it.
exampleCtx := getExampleContext()

// non-example-setup code begins here
Error(exampleCtx, "hello, world", map[string]interface{}{
	"foo":    123,
	"colors": []string{"red", "blue", "green"},
})
Output:

{"@level":"error","@message":"hello, world","@module":"sdk","colors":["red","blue","green"],"foo":123}

func Info

func Info(ctx context.Context, msg string, additionalFields ...map[string]interface{})

Info logs `msg` at the info level to the logger in `ctx`, with optional `additionalFields` structured key-value fields in the log output. Fields are shallow merged with any defined on the logger, e.g. by the `SetField()` function, and across multiple maps.

Example
// this function calls New with the options it needs to be reliably
// tested. Framework and SDK developers should call New, inject the
// resulting context in their framework, and then pass it around. This
// exampleCtx is a stand-in for a context you have injected a logger
// into and passed to the area of the codebase you need it.
exampleCtx := getExampleContext()

// non-example-setup code begins here
Info(exampleCtx, "hello, world", map[string]interface{}{
	"foo":    123,
	"colors": []string{"red", "blue", "green"},
})
Output:

{"@level":"info","@message":"hello, world","@module":"sdk","colors":["red","blue","green"],"foo":123}

func MaskAllFieldValuesRegexes added in v0.7.0

func MaskAllFieldValuesRegexes(ctx context.Context, expressions ...*regexp.Regexp) context.Context

MaskAllFieldValuesRegexes returns a new context.Context that has a modified logger that masks (replaces) with asterisks (`***`) all field value substrings, matching one of the given *regexp.Regexp.

Note that the replacement will happen, only for field values that are of type string.

Each call to this function is additive: the regexp to mask by are added to the existing configuration.

Example:

configuration = `[regexp.MustCompile("(foo|bar)")]`

log1 = `{ msg = "...", fields = { 'k1': '***', 'k2': '***', 'k3': 'baz' }`  -> masked value
log2 = `{ msg = "...", fields = { 'k1': 'boo', 'k2': 'far', 'k3': 'baz' }`  -> as-is value
log2 = `{ msg = "...", fields = { 'k1': '*** *** baz' }`                    -> masked 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
exampleCtx = MaskAllFieldValuesRegexes(exampleCtx, regexp.MustCompile("v1|v2"))

// all messages logged with exampleCtx will now have field values matching
// the above regular expressions, replaced with ***
Trace(exampleCtx, "example log message", map[string]interface{}{
	"k1": "v1 plus some text",
	"k2": "v2 plus more text",
})
Output:

{"@level":"trace","@message":"example log message","@module":"sdk","k1":"*** plus some text","k2":"*** plus more text"}

func MaskAllFieldValuesStrings added in v0.7.0

func MaskAllFieldValuesStrings(ctx context.Context, matchingStrings ...string) context.Context

MaskAllFieldValuesStrings returns a new context.Context that has a modified logger that masks (replaces) with asterisks (`***`) all field value substrings, equal to one of the given strings.

Note that the replacement will happen, only for field values that are of type string.

Each call to this function is additive: the regexp to mask by are added to the existing configuration.

Example:

configuration = `[regexp.MustCompile("(foo|bar)")]`

log1 = `{ msg = "...", fields = { 'k1': '***', 'k2': '***', 'k3': 'baz' }`  -> masked value
log2 = `{ msg = "...", fields = { 'k1': 'boo', 'k2': 'far', 'k3': 'baz' }`  -> as-is value
log2 = `{ msg = "...", fields = { 'k1': '*** *** baz' }`                    -> masked 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
exampleCtx = MaskAllFieldValuesStrings(exampleCtx, "v1", "v2")

// all messages logged with exampleCtx will now have field values equal
// the above strings, replaced with ***
Trace(exampleCtx, "example log message", map[string]interface{}{
	"k1": "v1 plus some text",
	"k2": "v2 plus more text",
})
Output:

{"@level":"trace","@message":"example log message","@module":"sdk","k1":"*** plus some text","k2":"*** plus more text"}

func MaskFieldValuesWithFieldKeys added in v0.6.0

func MaskFieldValuesWithFieldKeys(ctx context.Context, keys ...string) context.Context

MaskFieldValuesWithFieldKeys returns a new context.Context that has a modified logger that masks (replaces) with asterisks (`***`) any field value where the key matches one of the given keys.

Each call to this function is additive: the keys to mask by are added to the existing configuration.

Example:

configuration = `['foo', 'baz']`

log1 = `{ msg = "...", fields = { 'foo': '***', 'bar': '...' }`  -> masked value
log2 = `{ msg = "...", fields = { 'bar': '...' }`                -> as-is value
log3 = `{ msg = "...", fields = { 'baz': '***', 'boo': '...' }`  -> masked 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
exampleCtx = MaskFieldValuesWithFieldKeys(exampleCtx, "field1")

// all messages logged with exampleCtx will now have field1=***
Trace(exampleCtx, "example log message", map[string]interface{}{
	"field1": 123,
	"field2": 456,
})
Output:

{"@level":"trace","@message":"example log message","@module":"sdk","field1":"***","field2":456}

func MaskLogRegexes added in v0.7.0

func MaskLogRegexes(ctx context.Context, expressions ...*regexp.Regexp) context.Context

MaskLogRegexes is a shortcut to invoke MaskMessageRegexes and MaskAllFieldValuesRegexes using the same input. Refer to those functions for details.

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
exampleCtx = MaskLogRegexes(exampleCtx, regexp.MustCompile("v1|v2"), regexp.MustCompile("message"))

// all messages logged with exampleCtx will now have message content
// and field values matching the above regular expressions, replaced with ***
Trace(exampleCtx, "example log message", map[string]interface{}{
	"k1": "v1 plus some text",
	"k2": "v2 plus more text",
})
Output:

{"@level":"trace","@message":"example log ***","@module":"sdk","k1":"*** plus some text","k2":"*** plus more text"}

func MaskLogStrings added in v0.7.0

func MaskLogStrings(ctx context.Context, matchingStrings ...string) context.Context

MaskLogStrings is a shortcut to invoke MaskMessageStrings and MaskAllFieldValuesStrings using the same input. Refer to those functions for details.

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
exampleCtx = MaskLogStrings(exampleCtx, "v1", "v2", "message")

// all messages logged with exampleCtx will now have message content
// and field values equal the above strings, replaced with ***
Trace(exampleCtx, "example log message", map[string]interface{}{
	"k1": "v1 plus some text",
	"k2": "v2 plus more text",
})
Output:

{"@level":"trace","@message":"example log ***","@module":"sdk","k1":"*** plus some text","k2":"*** plus more text"}

func MaskMessageRegexes added in v0.6.0

func MaskMessageRegexes(ctx context.Context, expressions ...*regexp.Regexp) context.Context

MaskMessageRegexes returns a new context.Context that has a modified logger that masks (replaces) with asterisks (`***`) all message substrings, matching one of the given *regexp.Regexp.

Each call to this function is additive: the regexp to mask by are added to the existing configuration.

Example:

configuration = `[regexp.MustCompile("(foo|bar)")]`

log1 = `{ msg = "banana apple ***", fields = {...}`     -> masked portion
log2 = `{ msg = "pineapple mango", fields = {...}`      -> as-is
log3 = `{ msg = "pineapple mango ***", fields = {...}`  -> masked portion
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
exampleCtx = MaskMessageRegexes(exampleCtx, regexp.MustCompile(`[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{4}`))

// all messages logged with exampleCtx will now have strings matching the
// regular expression replaced with ***
Trace(exampleCtx, "example log message with 1234-1234-1234-1234 masked")
Output:

{"@level":"trace","@message":"example log message with *** masked","@module":"sdk"}

func MaskMessageStrings added in v0.6.0

func MaskMessageStrings(ctx context.Context, matchingStrings ...string) context.Context

MaskMessageStrings returns a new context.Context that has a modified logger that masks (replace) with asterisks (`***`) all message substrings, equal to one of the given strings.

Each call to this function is additive: the string to mask by are added to the existing configuration.

Example:

configuration = `['foo', 'bar']`

log1 = `{ msg = "banana apple ***", fields = { 'k1': 'foo, bar, baz' }`  -> masked portion
log2 = `{ msg = "pineapple mango", fields = {...}`                       -> as-is
log3 = `{ msg = "pineapple mango ***", fields = {...}`                   -> masked portion
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
exampleCtx = MaskMessageStrings(exampleCtx, "my-sensitive-data")

// all messages logged with exampleCtx will now have my-sensitive-data
// replaced with ***
Trace(exampleCtx, "example log message with my-sensitive-data masked")
Output:

{"@level":"trace","@message":"example log message with *** masked","@module":"sdk"}

func NewRootProviderLogger

func NewRootProviderLogger(ctx context.Context, options ...logging.Option) context.Context

NewRootProviderLogger returns a new context.Context that contains a provider logger configured with the passed options.

func NewRootSDKLogger

func NewRootSDKLogger(ctx context.Context, options ...logging.Option) context.Context

NewRootSDKLogger returns a new context.Context that contains an SDK logger configured with the passed options.

func NewSubsystem

func NewSubsystem(ctx context.Context, subsystem string, options ...logging.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 and additional location offset of the logger.

Example
// this function calls new with the options it needs to be reliably
// tested. framework and sdk developers should call new, inject the
// resulting context in their framework, and then pass it around. this
// examplectx is a stand-in for a context you have injected a logger
// into and passed to the area of the codebase you need it.
exampleCtx := getExampleContext()

// non-example-setup code begins here
subCtx := NewSubsystem(exampleCtx, "my-subsystem")

// messages logged to the subsystem will carry the subsystem name with
// them
SubsystemTrace(subCtx, "my-subsystem", "hello, world", map[string]interface{}{"foo": 123})
Output:

{"@level":"trace","@message":"hello, world","@module":"sdk.my-subsystem","foo":123}
Example (WithLevel)
// this function calls new with the options it needs to be reliably
// tested. framework and sdk developers should call new, inject the
// resulting context in their framework, and then pass it around. this
// examplectx is a stand-in for a context you have injected a logger
// into and passed to the area of the codebase you need it.
exampleCtx := getExampleContext()
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", map[string]interface{}{"foo": 123})

// the parent logger will still output at its configured TRACE level,
// though
Trace(subCtx, "hello, world", map[string]interface{}{"foo": 123})

// and the subsystem logger will output at the WARN level
SubsystemWarn(subCtx, "my-subsystem", "hello, world", map[string]interface{}{"foo": 123})
Output:

{"@level":"trace","@message":"hello, world","@module":"sdk","foo":123}
{"@level":"warn","@message":"hello, world","@module":"sdk.my-subsystem","foo":123}

func OmitLogWithFieldKeys added in v0.6.0

func OmitLogWithFieldKeys(ctx context.Context, keys ...string) context.Context

OmitLogWithFieldKeys returns a new context.Context that has a modified logger that will omit to write any log when any of the given keys is found within its fields.

Each call to this function is additive: the keys to omit by are added to the existing configuration.

Example:

configuration = `['foo', 'baz']`

log1 = `{ msg = "...", fields = { 'foo': '...', 'bar': '...' }`  -> omitted
log2 = `{ msg = "...", fields = { 'bar': '...' }`                -> printed
log3 = `{ msg = "...", fields = { 'baz': '...', 'boo': '...' }`  -> omitted
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
exampleCtx = OmitLogWithFieldKeys(exampleCtx, "field1")

// all messages logged with exampleCtx and using field1 will be omitted
Trace(exampleCtx, "example log message", map[string]interface{}{
	"field1": 123,
	"field2": 456,
})
Output:

func OmitLogWithMessageRegexes added in v0.6.0

func OmitLogWithMessageRegexes(ctx context.Context, expressions ...*regexp.Regexp) context.Context

OmitLogWithMessageRegexes returns a new context.Context that has a modified logger that will omit to write any log that has a message matching any of the given *regexp.Regexp.

Each call to this function is additive: the regexp to omit by are added to the existing configuration.

Example:

configuration = `[regexp.MustCompile("(foo|bar)")]`

log1 = `{ msg = "banana apple foo", fields = {...}`     -> omitted
log2 = `{ msg = "pineapple mango", fields = {...}`      -> printed
log3 = `{ msg = "pineapple mango bar", fields = {...}`  -> omitted
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
exampleCtx = OmitLogWithMessageRegexes(exampleCtx, regexp.MustCompile(`[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{4}`))

// all messages logged with exampleCtx will be omitted if they have a
// string matching the regular expression
Trace(exampleCtx, "example log message with 1234-1234-1234-1234 masked")
Output:

func OmitLogWithMessageStrings added in v0.6.0

func OmitLogWithMessageStrings(ctx context.Context, matchingStrings ...string) context.Context

OmitLogWithMessageStrings returns a new context.Context that has a modified logger that will omit to write any log that matches any of the given string.

Each call to this function is additive: the string to omit by are added to the existing configuration.

Example:

configuration = `['foo', 'bar']`

log1 = `{ msg = "banana apple foo", fields = {...}`     -> omitted
log2 = `{ msg = "pineapple mango", fields = {...}`      -> printed
log3 = `{ msg = "pineapple mango bar", fields = {...}`  -> omitted
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
exampleCtx = OmitLogWithMessageStrings(exampleCtx, "my-sensitive-data")

// all messages logged with exampleCtx will now have my-sensitive-data
// entries omitted
Trace(exampleCtx, "example log message with my-sensitive-data masked")
Output:

func RegisterTestSink

func RegisterTestSink(ctx context.Context, t testing.T) context.Context

RegisterTestSink sets up a logging sink, for use with test frameworks and other cases where plugin logs don't get routed through Terraform. This applies the same filtering and file output behaviors that Terraform does.

RegisterTestSink should only ever be called by test frameworks, providers should never call it.

RegisterTestSink must be called prior to any loggers being setup or instantiated.

func SetField added in v0.6.0

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

SetField returns a new context.Context that has a modified logger in it which will include key and value as fields in all its log output.

In case of the same key is used multiple times (i.e. key collision), the last one set is the one that gets persisted and then outputted with the logs.

Example
// this function calls New with the options it needs to be reliably
// tested. Framework and SDK developers should call New, inject the
// resulting context in their framework, and then pass it around. This
// exampleCtx is a stand-in for a context you have injected a logger
// into and passed to the area of the codebase you need it.
exampleCtx := getExampleContext()

// non-example-setup code begins here
derivedCtx := SetField(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","@module":"sdk","foo":123}

func SubsystemDebug

func SubsystemDebug(ctx context.Context, subsystem, msg string, additionalFields ...map[string]interface{})

SubsystemDebug logs `msg` at the debug level to the subsystem logger specified in `ctx`, with optional `additionalFields` structured key-value fields in the log output. Fields are shallow merged with any defined on the subsystem logger, e.g. by the `SubsystemSetField()` function, and across multiple maps.

Example
// this function calls new with the options it needs to be reliably
// tested. framework and sdk developers should call new, inject the
// resulting context in their framework, and then pass it around. this
// examplectx is a stand-in for a context you have injected a logger
// into and passed to the area of the codebase you need it.
exampleCtx := getExampleContext()
exampleCtx = NewSubsystem(exampleCtx, "my-subsystem")

// non-example-setup code begins here
SubsystemDebug(exampleCtx, "my-subsystem", "hello, world", map[string]interface{}{
	"foo":    123,
	"colors": []string{"red", "blue", "green"},
})
Output:

{"@level":"debug","@message":"hello, world","@module":"sdk.my-subsystem","colors":["red","blue","green"],"foo":123}

func SubsystemError

func SubsystemError(ctx context.Context, subsystem, msg string, additionalFields ...map[string]interface{})

SubsystemError logs `msg` at the error level to the subsystem logger specified in `ctx`, with optional `additionalFields` structured key-value fields in the log output. Fields are shallow merged with any defined on the subsystem logger, e.g. by the `SubsystemSetField()` function, and across multiple maps.

Example
// this function calls new with the options it needs to be reliably
// tested. framework and sdk developers should call new, inject the
// resulting context in their framework, and then pass it around. this
// examplectx is a stand-in for a context you have injected a logger
// into and passed to the area of the codebase you need it.
exampleCtx := getExampleContext()
exampleCtx = NewSubsystem(exampleCtx, "my-subsystem")

// non-example-setup code begins here
SubsystemError(exampleCtx, "my-subsystem", "hello, world", map[string]interface{}{
	"foo":    123,
	"colors": []string{"red", "blue", "green"},
})
Output:

{"@level":"error","@message":"hello, world","@module":"sdk.my-subsystem","colors":["red","blue","green"],"foo":123}

func SubsystemInfo

func SubsystemInfo(ctx context.Context, subsystem, msg string, additionalFields ...map[string]interface{})

SubsystemInfo logs `msg` at the info level to the subsystem logger specified in `ctx`, with optional `additionalFields` structured key-value fields in the log output. Fields are shallow merged with any defined on the subsystem logger, e.g. by the `SubsystemSetField()` function, and across multiple maps.

Example
// this function calls new with the options it needs to be reliably
// tested. framework and sdk developers should call new, inject the
// resulting context in their framework, and then pass it around. this
// examplectx is a stand-in for a context you have injected a logger
// into and passed to the area of the codebase you need it.
exampleCtx := getExampleContext()
exampleCtx = NewSubsystem(exampleCtx, "my-subsystem")

// non-example-setup code begins here
SubsystemInfo(exampleCtx, "my-subsystem", "hello, world", map[string]interface{}{
	"foo":    123,
	"colors": []string{"red", "blue", "green"},
})
Output:

{"@level":"info","@message":"hello, world","@module":"sdk.my-subsystem","colors":["red","blue","green"],"foo":123}

func SubsystemMaskAllFieldValuesRegexes added in v0.7.0

func SubsystemMaskAllFieldValuesRegexes(ctx context.Context, subsystem string, expressions ...*regexp.Regexp) context.Context

SubsystemMaskAllFieldValuesRegexes returns a new context.Context that has a modified logger that masks (replaces) with asterisks (`***`) all field value substrings, matching one of the given *regexp.Regexp.

Each call to this function is additive: the regexp to mask by are added to the existing configuration.

Example:

configuration = `[regexp.MustCompile("(foo|bar)")]`

log1 = `{ msg = "...", fields = { 'k1': '***', 'k2': '***', 'k3': 'baz' }`  -> masked value
log2 = `{ msg = "...", fields = { 'k1': 'boo', 'k2': 'far', 'k3': 'baz' }`  -> as-is value
log2 = `{ msg = "...", fields = { 'k1': '*** *** baz' }`                    -> masked 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
exampleCtx = NewSubsystem(exampleCtx, "my-subsystem")
exampleCtx = SubsystemMaskAllFieldValuesRegexes(exampleCtx, "my-subsystem", regexp.MustCompile("v1|v2"))

// all messages logged with exampleCtx will now have field values matching
// the above regular expressions, replaced with ***
SubsystemTrace(exampleCtx, "my-subsystem", "example log message", map[string]interface{}{
	"k1": "v1 plus some text",
	"k2": "v2 plus more text",
})
Output:

{"@level":"trace","@message":"example log message","@module":"sdk.my-subsystem","k1":"*** plus some text","k2":"*** plus more text"}

func SubsystemMaskAllFieldValuesStrings added in v0.7.0

func SubsystemMaskAllFieldValuesStrings(ctx context.Context, subsystem string, matchingStrings ...string) context.Context

SubsystemMaskAllFieldValuesStrings returns a new context.Context that has a modified logger that masks (replaces) with asterisks (`***`) all field value substrings, equal to one of the given strings.

Each call to this function is additive: the regexp to mask by are added to the existing configuration.

Example:

configuration = `[regexp.MustCompile("(foo|bar)")]`

log1 = `{ msg = "...", fields = { 'k1': '***', 'k2': '***', 'k3': 'baz' }`  -> masked value
log2 = `{ msg = "...", fields = { 'k1': 'boo', 'k2': 'far', 'k3': 'baz' }`  -> as-is value
log2 = `{ msg = "...", fields = { 'k1': '*** *** baz' }`                    -> masked 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
exampleCtx = NewSubsystem(exampleCtx, "my-subsystem")
exampleCtx = SubsystemMaskAllFieldValuesStrings(exampleCtx, "my-subsystem", "v1", "v2")

// all messages logged with exampleCtx will now have field values equal
// the above strings, replaced with ***
SubsystemTrace(exampleCtx, "my-subsystem", "example log message", map[string]interface{}{
	"k1": "v1 plus some text",
	"k2": "v2 plus more text",
})
Output:

{"@level":"trace","@message":"example log message","@module":"sdk.my-subsystem","k1":"*** plus some text","k2":"*** plus more text"}

func SubsystemMaskFieldValuesWithFieldKeys added in v0.6.0

func SubsystemMaskFieldValuesWithFieldKeys(ctx context.Context, subsystem string, keys ...string) context.Context

SubsystemMaskFieldValuesWithFieldKeys returns a new context.Context that has a modified logger that masks (replaces) with asterisks (`***`) any field value where the key matches one of the given keys.

Each call to this function is additive: the keys to mask by are added to the existing configuration.

Example:

configuration = `['foo', 'baz']`

log1 = `{ msg = "...", fields = { 'foo': '***', 'bar': '...' }`  -> masked value
log2 = `{ msg = "...", fields = { 'bar': '...' }`                -> as-is value
log3 = `{ msg = "...", fields = { 'baz': '***', 'boo': '...' }`  -> masked 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
exampleCtx = NewSubsystem(exampleCtx, "my-subsystem")
exampleCtx = SubsystemMaskFieldValuesWithFieldKeys(exampleCtx, "my-subsystem", "field1")

// all messages logged with exampleCtx will now have field1=***
SubsystemTrace(exampleCtx, "my-subsystem", "example log message", map[string]interface{}{
	"field1": 123,
	"field2": 456,
})
Output:

{"@level":"trace","@message":"example log message","@module":"sdk.my-subsystem","field1":"***","field2":456}

func SubsystemMaskLogRegexes added in v0.7.0

func SubsystemMaskLogRegexes(ctx context.Context, subsystem string, expressions ...*regexp.Regexp) context.Context

SubsystemMaskLogRegexes is a shortcut to invoke SubsystemMaskMessageRegexes and SubsystemMaskAllFieldValuesRegexes using the same input. Refer to those functions for details.

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
exampleCtx = NewSubsystem(exampleCtx, "my-subsystem")
exampleCtx = SubsystemMaskLogRegexes(exampleCtx, "my-subsystem", regexp.MustCompile("v1|v2"), regexp.MustCompile("message"))

// all messages logged with exampleCtx will now have message content
// and field values matching the above regular expressions, replaced with ***
SubsystemTrace(exampleCtx, "my-subsystem", "example log message", map[string]interface{}{
	"k1": "v1 plus some text",
	"k2": "v2 plus more text",
})
Output:

{"@level":"trace","@message":"example log ***","@module":"sdk.my-subsystem","k1":"*** plus some text","k2":"*** plus more text"}

func SubsystemMaskLogStrings added in v0.7.0

func SubsystemMaskLogStrings(ctx context.Context, subsystem string, matchingStrings ...string) context.Context

SubsystemMaskLogStrings is a shortcut to invoke SubsystemMaskMessageStrings and SubsystemMaskAllFieldValuesStrings using the same input. Refer to those functions for details.

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
exampleCtx = NewSubsystem(exampleCtx, "my-subsystem")
exampleCtx = SubsystemMaskLogStrings(exampleCtx, "my-subsystem", "v1", "v2", "message")

// all messages logged with exampleCtx will now have message content
// and field values equal the above strings, replaced with ***
SubsystemTrace(exampleCtx, "my-subsystem", "example log message", map[string]interface{}{
	"k1": "v1 plus some text",
	"k2": "v2 plus more text",
})
Output:

{"@level":"trace","@message":"example log ***","@module":"sdk.my-subsystem","k1":"*** plus some text","k2":"*** plus more text"}

func SubsystemMaskMessageRegexes added in v0.6.0

func SubsystemMaskMessageRegexes(ctx context.Context, subsystem string, expressions ...*regexp.Regexp) context.Context

SubsystemMaskMessageRegexes returns a new context.Context that has a modified logger that masks (replaces) with asterisks (`***`) all message substrings, matching one of the given *regexp.Regexp.

Each call to this function is additive: the regexp to mask by are added to the existing configuration.

Example:

configuration = `[regexp.MustCompile("(foo|bar)")]`

log1 = `{ msg = "banana apple ***", fields = {...}`     -> masked portion
log2 = `{ msg = "pineapple mango", fields = {...}`      -> as-is
log3 = `{ msg = "pineapple mango ***", fields = {...}`  -> masked portion
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
exampleCtx = NewSubsystem(exampleCtx, "my-subsystem")
exampleCtx = SubsystemMaskMessageRegexes(exampleCtx, "my-subsystem", regexp.MustCompile(`[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{4}`))

// all messages logged with exampleCtx will now have strings matching the
// regular expression replaced with ***
SubsystemTrace(exampleCtx, "my-subsystem", "example log message with 1234-1234-1234-1234 masked")
Output:

{"@level":"trace","@message":"example log message with *** masked","@module":"sdk.my-subsystem"}

func SubsystemMaskMessageStrings added in v0.6.0

func SubsystemMaskMessageStrings(ctx context.Context, subsystem string, matchingStrings ...string) context.Context

SubsystemMaskMessageStrings returns a new context.Context that has a modified logger that masks (replace) with asterisks (`***`) all message substrings, equal to one of the given strings.

Each call to this function is additive: the string to mask by are added to the existing configuration.

Example:

configuration = `['foo', 'bar']`

log1 = `{ msg = "banana apple ***", fields = { 'k1': 'foo, bar, baz' }`  -> masked portion
log2 = `{ msg = "pineapple mango", fields = {...}`                       -> as-is
log3 = `{ msg = "pineapple mango ***", fields = {...}`                   -> masked portion
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
exampleCtx = NewSubsystem(exampleCtx, "my-subsystem")
exampleCtx = SubsystemMaskMessageStrings(exampleCtx, "my-subsystem", "my-sensitive-data")

// all messages logged with exampleCtx will now have my-sensitive-data
// replaced with ***
SubsystemTrace(exampleCtx, "my-subsystem", "example log message with my-sensitive-data masked")
Output:

{"@level":"trace","@message":"example log message with *** masked","@module":"sdk.my-subsystem"}

func SubsystemOmitLogWithFieldKeys added in v0.6.0

func SubsystemOmitLogWithFieldKeys(ctx context.Context, subsystem string, keys ...string) context.Context

SubsystemOmitLogWithFieldKeys returns a new context.Context that has a modified logger that will omit to write any log when any of the given keys is found within its fields.

Each call to this function is additive: the keys to omit by are added to the existing configuration.

Example:

configuration = `['foo', 'baz']`

log1 = `{ msg = "...", fields = { 'foo': '...', 'bar': '...' }`  -> omitted
log2 = `{ msg = "...", fields = { 'bar': '...' }`                -> printed
log3 = `{ msg = "...", fields = { 'baz': '...', 'boo': '...' }`  -> omitted
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
exampleCtx = NewSubsystem(exampleCtx, "my-subsystem")
exampleCtx = SubsystemOmitLogWithFieldKeys(exampleCtx, "my-subsystem", "field1")

// all messages logged with exampleCtx and using field1 will be omitted
SubsystemTrace(exampleCtx, "my-subsystem", "example log message", map[string]interface{}{
	"field1": 123,
	"field2": 456,
})
Output:

func SubsystemOmitLogWithMessageRegexes added in v0.6.0

func SubsystemOmitLogWithMessageRegexes(ctx context.Context, subsystem string, expressions ...*regexp.Regexp) context.Context

SubsystemOmitLogWithMessageRegexes returns a new context.Context that has a modified logger that will omit to write any log that has a message matching any of the given *regexp.Regexp.

Each call to this function is additive: the regexp to omit by are added to the existing configuration.

Example:

configuration = `[regexp.MustCompile("(foo|bar)")]`

log1 = `{ msg = "banana apple foo", fields = {...}`     -> omitted
log2 = `{ msg = "pineapple mango", fields = {...}`      -> printed
log3 = `{ msg = "pineapple mango bar", fields = {...}`  -> omitted
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
exampleCtx = NewSubsystem(exampleCtx, "my-subsystem")
exampleCtx = SubsystemOmitLogWithMessageRegexes(exampleCtx, "my-subsystem", regexp.MustCompile(`[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{4}`))

// all messages logged with exampleCtx will be omitted if they have a
// string matching the regular expression
SubsystemTrace(exampleCtx, "my-subsystem", "example log message with 1234-1234-1234-1234 masked")
Output:

func SubsystemOmitLogWithMessageStrings added in v0.6.0

func SubsystemOmitLogWithMessageStrings(ctx context.Context, subsystem string, matchingStrings ...string) context.Context

SubsystemOmitLogWithMessageStrings returns a new context.Context that has a modified logger that will omit to write any log that matches any of the given string.

Each call to this function is additive: the string to omit by are added to the existing configuration.

Example:

configuration = `['foo', 'bar']`

log1 = `{ msg = "banana apple foo", fields = {...}`     -> omitted
log2 = `{ msg = "pineapple mango", fields = {...}`      -> printed
log3 = `{ msg = "pineapple mango bar", fields = {...}`  -> omitted
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
exampleCtx = NewSubsystem(exampleCtx, "my-subsystem")
exampleCtx = SubsystemOmitLogWithMessageStrings(exampleCtx, "my-subsystem", "my-sensitive-data")

// all messages logged with exampleCtx will now have my-sensitive-data
// entries omitted
SubsystemTrace(exampleCtx, "my-subsystem", "example log message with my-sensitive-data masked")
Output:

func SubsystemSetField added in v0.6.0

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

SubsystemSetField returns a new context.Context that has a modified logger for the specified subsystem in it which will include key and value as fields in all its log output.

In case of the same key is used multiple times (i.e. key collision), the last one set is the one that gets persisted and then outputted with the logs.

Example
// this function calls new with the options it needs to be reliably
// tested. framework and sdk developers should call new, inject the
// resulting context in their framework, and then pass it around. this
// examplectx is a stand-in for a context you have injected a logger
// into and passed to the area of the codebase you need it.
exampleCtx := getExampleContext()
exampleCtx = NewSubsystem(exampleCtx, "my-subsystem")

// non-example-setup code begins here
derivedCtx := SubsystemSetField(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":"sdk.my-subsystem","foo":123}

func SubsystemTrace

func SubsystemTrace(ctx context.Context, subsystem, msg string, additionalFields ...map[string]interface{})

SubsystemTrace logs `msg` at the trace level to the subsystem logger specified in `ctx`, with optional `additionalFields` structured key-value fields in the log output. Fields are shallow merged with any defined on the subsystem logger, e.g. by the `SubsystemSetField()` function, and across multiple maps.

Example
// this function calls new with the options it needs to be reliably
// tested. framework and sdk developers should call new, inject the
// resulting context in their framework, and then pass it around. this
// examplectx is a stand-in for a context you have injected a logger
// into and passed to the area of the codebase you need it.
exampleCtx := getExampleContext()
exampleCtx = NewSubsystem(exampleCtx, "my-subsystem")

// non-example-setup code begins here
SubsystemTrace(exampleCtx, "my-subsystem", "hello, world", map[string]interface{}{
	"foo":    123,
	"colors": []string{"red", "blue", "green"},
})
Output:

{"@level":"trace","@message":"hello, world","@module":"sdk.my-subsystem","colors":["red","blue","green"],"foo":123}

func SubsystemWarn

func SubsystemWarn(ctx context.Context, subsystem, msg string, additionalFields ...map[string]interface{})

SubsystemWarn logs `msg` at the warn level to the subsystem logger specified in `ctx`, with optional `additionalFields` structured key-value fields in the log output. Fields are shallow merged with any defined on the subsystem logger, e.g. by the `SubsystemSetField()` function, and across multiple maps.

Example
// this function calls new with the options it needs to be reliably
// tested. framework and sdk developers should call new, inject the
// resulting context in their framework, and then pass it around. this
// examplectx is a stand-in for a context you have injected a logger
// into and passed to the area of the codebase you need it.
exampleCtx := getExampleContext()
exampleCtx = NewSubsystem(exampleCtx, "my-subsystem")

// non-example-setup code begins here
SubsystemWarn(exampleCtx, "my-subsystem", "hello, world", map[string]interface{}{
	"foo":    123,
	"colors": []string{"red", "blue", "green"},
})
Output:

{"@level":"warn","@message":"hello, world","@module":"sdk.my-subsystem","colors":["red","blue","green"],"foo":123}

func Trace

func Trace(ctx context.Context, msg string, additionalFields ...map[string]interface{})

Trace logs `msg` at the trace level to the logger in `ctx`, with optional `additionalFields` structured key-value fields in the log output. Fields are shallow merged with any defined on the logger, e.g. by the `SetField()` function, and across multiple maps.

Example
// this function calls New with the options it needs to be reliably
// tested. Framework and SDK developers should call New, inject the
// resulting context in their framework, and then pass it around. This
// exampleCtx is a stand-in for a context you have injected a logger
// into and passed to the area of the codebase you need it.
exampleCtx := getExampleContext()

// non-example-setup code begins here
Trace(exampleCtx, "hello, world", map[string]interface{}{
	"foo":    123,
	"colors": []string{"red", "blue", "green"},
})
Output:

{"@level":"trace","@message":"hello, world","@module":"sdk","colors":["red","blue","green"],"foo":123}

func Warn

func Warn(ctx context.Context, msg string, additionalFields ...map[string]interface{})

Warn logs `msg` at the warn level to the logger in `ctx`, with optional `additionalFields` structured key-value fields in the log output. Fields are shallow merged with any defined on the logger, e.g. by the `SetField()` function, and across multiple maps.

Example
// this function calls New with the options it needs to be reliably
// tested. Framework and SDK developers should call New, inject the
// resulting context in their framework, and then pass it around. This
// exampleCtx is a stand-in for a context you have injected a logger
// into and passed to the area of the codebase you need it.
exampleCtx := getExampleContext()

// non-example-setup code begins here
Warn(exampleCtx, "hello, world", map[string]interface{}{
	"foo":    123,
	"colors": []string{"red", "blue", "green"},
})
Output:

{"@level":"warn","@message":"hello, world","@module":"sdk","colors":["red","blue","green"],"foo":123}

func WithAdditionalLocationOffset added in v0.3.0

func WithAdditionalLocationOffset(additionalLocationOffset int) logging.Option

WithAdditionalLocationOffset returns an option that allowing implementations to fix location information when implementing helper functions. The default offset of 1 is automatically added to the provided value to account for the tfsdklog logging functions.

func WithLevel

func WithLevel(level hclog.Level) logging.Option

WithLevel returns an option that will set the level of the logger.

func WithLevelFromEnv

func WithLevelFromEnv(name string, subsystems ...string) logging.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) logging.Option

WithLogName returns an option that will set the logger name explicitly to `name`. This has no effect when used with NewSubsystem.

func WithRootFields added in v0.4.0

func WithRootFields() logging.Option

WithRootFields enables the copying of root logger fields to a new subsystem logger during creation.

func WithStderrFromInit

func WithStderrFromInit() logging.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() logging.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.

Types

type Options

type Options []logging.Option

Options is a collection of logging options, useful for collecting arguments to NewSubsystem, NewRootSDKLogger, and NewRootProviderLogger before calling them.

Jump to

Keyboard shortcuts

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