From f5284c785deaa4e505161d74b4f1c819f7455496 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Wed, 4 May 2022 12:10:15 -0400 Subject: [PATCH] Introduce tflogtest and tfsdklogtest packages (#62) Reference: https://github.com/hashicorp/terraform-plugin-log/issues/61 --- .changelog/62.txt | 7 +++++ internal/loggertest/json_decode.go | 3 +- tflogtest/doc.go | 3 ++ tflogtest/json_decode.go | 13 ++++++++ tflogtest/json_decode_example_test.go | 38 ++++++++++++++++++++++++ tflogtest/root_logger.go | 21 +++++++++++++ tflogtest/root_logger_example_test.go | 27 +++++++++++++++++ tfsdklogtest/doc.go | 3 ++ tfsdklogtest/json_decode.go | 13 ++++++++ tfsdklogtest/json_decode_example_test.go | 38 ++++++++++++++++++++++++ tfsdklogtest/root_logger.go | 21 +++++++++++++ tfsdklogtest/root_logger_example_test.go | 27 +++++++++++++++++ 12 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 .changelog/62.txt create mode 100644 tflogtest/doc.go create mode 100644 tflogtest/json_decode.go create mode 100644 tflogtest/json_decode_example_test.go create mode 100644 tflogtest/root_logger.go create mode 100644 tflogtest/root_logger_example_test.go create mode 100644 tfsdklogtest/doc.go create mode 100644 tfsdklogtest/json_decode.go create mode 100644 tfsdklogtest/json_decode_example_test.go create mode 100644 tfsdklogtest/root_logger.go create mode 100644 tfsdklogtest/root_logger_example_test.go diff --git a/.changelog/62.txt b/.changelog/62.txt new file mode 100644 index 0000000..07e255f --- /dev/null +++ b/.changelog/62.txt @@ -0,0 +1,7 @@ +```release-note:feature +Added `tflogtest` package, which provides functionality for unit testing of provider logging +``` + +```release-note:feature +Added `tfsdklogtest` package, which provides functionality for unit testing of SDK logging +``` diff --git a/internal/loggertest/json_decode.go b/internal/loggertest/json_decode.go index 8de7b21..463e56a 100644 --- a/internal/loggertest/json_decode.go +++ b/internal/loggertest/json_decode.go @@ -8,11 +8,12 @@ import ( func MultilineJSONDecode(data io.Reader) ([]map[string]interface{}, error) { var result []map[string]interface{} - var entry map[string]interface{} dec := json.NewDecoder(data) for { + var entry map[string]interface{} + err := dec.Decode(&entry) if err == io.EOF { diff --git a/tflogtest/doc.go b/tflogtest/doc.go new file mode 100644 index 0000000..cc6ed63 --- /dev/null +++ b/tflogtest/doc.go @@ -0,0 +1,3 @@ +// Package tflogtest provides functionality for unit testing of provider +// logging. +package tflogtest diff --git a/tflogtest/json_decode.go b/tflogtest/json_decode.go new file mode 100644 index 0000000..dd49d96 --- /dev/null +++ b/tflogtest/json_decode.go @@ -0,0 +1,13 @@ +package tflogtest + +import ( + "io" + + "github.com/hashicorp/terraform-plugin-log/internal/loggertest" +) + +// MultilineJSONDecode supports decoding the output of a JSON logger into a +// slice of maps, with each element representing a log entry. +func MultilineJSONDecode(data io.Reader) ([]map[string]interface{}, error) { + return loggertest.MultilineJSONDecode(data) +} diff --git a/tflogtest/json_decode_example_test.go b/tflogtest/json_decode_example_test.go new file mode 100644 index 0000000..2fd48ab --- /dev/null +++ b/tflogtest/json_decode_example_test.go @@ -0,0 +1,38 @@ +package tflogtest + +import ( + "bytes" + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +func ExampleMultilineJSONDecode() { + var output bytes.Buffer + + ctx := RootLogger(context.Background(), &output) + + // Root provider logger is now available for usage, such as writing + // entries, calling With(), or calling NewSubsystem(). + tflog.Trace(ctx, "entry 1") + tflog.Trace(ctx, "entry 2") + + entries, err := MultilineJSONDecode(&output) + + if err != nil { + // Typical unit testing would call t.Fatalf() here. + fmt.Printf("unable to read multiple line JSON: %s", err) + } + + // Entries can be checked via go-cmp's cmp.Diff() or other testing methods. + // This example outputs them to stdout in an explicitly formatted string, + // which would not be expected in typical unit testing. + for _, entry := range entries { + fmt.Printf("@message: %s\n", entry["@message"]) + } + + // Output: + // @message: entry 1 + // @message: entry 2 +} diff --git a/tflogtest/root_logger.go b/tflogtest/root_logger.go new file mode 100644 index 0000000..f2842ee --- /dev/null +++ b/tflogtest/root_logger.go @@ -0,0 +1,21 @@ +package tflogtest + +import ( + "context" + "io" + + "github.com/hashicorp/terraform-plugin-log/internal/loggertest" +) + +// RootLogger returns a context containing a provider root logger suitable for +// unit testing that is: +// +// - Written to the given io.Writer, such as a bytes.Buffer. +// - Written with JSON output, that can be decoded with MultilineJSONDecode. +// - Log level set to TRACE. +// - Without location/caller information in log entries. +// - Without timestamps in log entries. +// +func RootLogger(ctx context.Context, output io.Writer) context.Context { + return loggertest.ProviderRoot(ctx, output) +} diff --git a/tflogtest/root_logger_example_test.go b/tflogtest/root_logger_example_test.go new file mode 100644 index 0000000..720269b --- /dev/null +++ b/tflogtest/root_logger_example_test.go @@ -0,0 +1,27 @@ +package tflogtest + +import ( + "bytes" + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +func ExampleRootLogger() { + var output bytes.Buffer + + ctx := RootLogger(context.Background(), &output) + + // Root provider logger is now available for usage, such as writing + // entries, calling With(), or calling NewSubsystem(). + tflog.Trace(ctx, "hello, world", map[string]interface{}{ + "foo": 123, + "colors": []string{"red", "blue", "green"}, + }) + + fmt.Println(output.String()) + + // Output: + // {"@level":"trace","@message":"hello, world","@module":"provider","colors":["red","blue","green"],"foo":123} +} diff --git a/tfsdklogtest/doc.go b/tfsdklogtest/doc.go new file mode 100644 index 0000000..8660d06 --- /dev/null +++ b/tfsdklogtest/doc.go @@ -0,0 +1,3 @@ +// Package tfsdklogtest provides functionality for unit testing of SDK +// logging. Provider developers should use the tflogtest package. +package tfsdklogtest diff --git a/tfsdklogtest/json_decode.go b/tfsdklogtest/json_decode.go new file mode 100644 index 0000000..5f7c503 --- /dev/null +++ b/tfsdklogtest/json_decode.go @@ -0,0 +1,13 @@ +package tfsdklogtest + +import ( + "io" + + "github.com/hashicorp/terraform-plugin-log/internal/loggertest" +) + +// MultilineJSONDecode supports decoding the output of a JSON logger into a +// slice of maps, with each element representing a log entry. +func MultilineJSONDecode(data io.Reader) ([]map[string]interface{}, error) { + return loggertest.MultilineJSONDecode(data) +} diff --git a/tfsdklogtest/json_decode_example_test.go b/tfsdklogtest/json_decode_example_test.go new file mode 100644 index 0000000..de9845b --- /dev/null +++ b/tfsdklogtest/json_decode_example_test.go @@ -0,0 +1,38 @@ +package tfsdklogtest + +import ( + "bytes" + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-log/tfsdklog" +) + +func ExampleMultilineJSONDecode() { + var output bytes.Buffer + + ctx := RootLogger(context.Background(), &output) + + // Root SDK logger is now available for usage, such as writing entries, + // calling With(), or calling NewSubsystem(). + tfsdklog.Trace(ctx, "entry 1") + tfsdklog.Trace(ctx, "entry 2") + + entries, err := MultilineJSONDecode(&output) + + if err != nil { + // Typical unit testing would call t.Fatalf() here. + fmt.Printf("unable to read multiple line JSON: %s", err) + } + + // Entries can be checked via go-cmp's cmp.Diff() or other testing methods. + // This example outputs them to stdout in an explicitly formatted string, + // which would not be expected in typical unit testing. + for _, entry := range entries { + fmt.Printf("@message: %s\n", entry["@message"]) + } + + // Output: + // @message: entry 1 + // @message: entry 2 +} diff --git a/tfsdklogtest/root_logger.go b/tfsdklogtest/root_logger.go new file mode 100644 index 0000000..c13512a --- /dev/null +++ b/tfsdklogtest/root_logger.go @@ -0,0 +1,21 @@ +package tfsdklogtest + +import ( + "context" + "io" + + "github.com/hashicorp/terraform-plugin-log/internal/loggertest" +) + +// RootLogger returns a context containing a SDK root logger suitable for unit +// testing that is: +// +// - Written to the given io.Writer, such as a bytes.Buffer. +// - Written with JSON output, that can be decoded with MultilineJSONDecode. +// - Log level set to TRACE. +// - Without location/caller information in log entries. +// - Without timestamps in log entries. +// +func RootLogger(ctx context.Context, output io.Writer) context.Context { + return loggertest.SDKRoot(ctx, output) +} diff --git a/tfsdklogtest/root_logger_example_test.go b/tfsdklogtest/root_logger_example_test.go new file mode 100644 index 0000000..c7cf2d6 --- /dev/null +++ b/tfsdklogtest/root_logger_example_test.go @@ -0,0 +1,27 @@ +package tfsdklogtest + +import ( + "bytes" + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-log/tfsdklog" +) + +func ExampleRootLogger() { + var output bytes.Buffer + + ctx := RootLogger(context.Background(), &output) + + // Root SDK logger is now available for usage, such as writing entries, + // calling With(), or calling NewSubsystem(). + tfsdklog.Trace(ctx, "hello, world", map[string]interface{}{ + "foo": 123, + "colors": []string{"red", "blue", "green"}, + }) + + fmt.Println(output.String()) + + // Output: + // {"@level":"trace","@message":"hello, world","@module":"sdk","colors":["red","blue","green"],"foo":123} +}