diff --git a/infra/blueprint-test/pkg/utils/logger.go b/infra/blueprint-test/pkg/utils/logger.go index c6bfd03ee5f..dfd8655b458 100644 --- a/infra/blueprint-test/pkg/utils/logger.go +++ b/infra/blueprint-test/pkg/utils/logger.go @@ -17,9 +17,13 @@ package utils import ( + "fmt" + "io" + "os" "testing" "github.com/gruntwork-io/terratest/modules/logger" + terraTesting "github.com/gruntwork-io/terratest/modules/testing" ) // GetLoggerFromT returns a logger based on test verbosity @@ -29,3 +33,33 @@ func GetLoggerFromT() *logger.Logger { } return logger.Discard } + +// TestFileLogger is a logger that writes to disk instead of stdout. +// This is useful when you want to redirect verbose logs of long running tests to disk. +type TestFileLogger struct { + pth string + w io.WriteCloser +} + +// NewTestFileLogger returns a TestFileLogger logger that can be used with the WithLogger option. +func NewTestFileLogger(t *testing.T, pth string) (*logger.Logger, func(t *testing.T)) { + f, err := os.OpenFile(pth, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + t.Fatalf("error opening file %s: %v", pth, err) + } + fl := TestFileLogger{ + pth: pth, + w: f, + } + return logger.New(fl), fl.Close +} + +func (fl TestFileLogger) Logf(t terraTesting.TestingT, format string, args ...interface{}) { + logger.DoLog(t, 3, fl.w, fmt.Sprintf(format, args...)) +} + +func (fl TestFileLogger) Close(t *testing.T) { + if err := fl.w.Close(); err != nil { + t.Fatalf("error closing file logger %s: %v", fl.pth, err) + } +} diff --git a/infra/blueprint-test/pkg/utils/logger_test.go b/infra/blueprint-test/pkg/utils/logger_test.go new file mode 100644 index 00000000000..151d27c8996 --- /dev/null +++ b/infra/blueprint-test/pkg/utils/logger_test.go @@ -0,0 +1,53 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package utils + +import ( + "fmt" + "os" + "path" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewTestFileLogger(t *testing.T) { + tests := []struct { + name string + content string + }{ + { + name: "simple", + content: "foo", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert := assert.New(t) + testFile := path.Join(t.TempDir(), fmt.Sprintf("%s.log", tt.name)) + fl, flClose := NewTestFileLogger(t, testFile) + fl.Logf(t, tt.content) + flClose(t) + gotContent, err := os.ReadFile(testFile) + assert.NoError(err) + assert.Contains(string(gotContent), "foo") + // assert we are wrapping logger.DoLog which prints stack/test info + assert.Contains(string(gotContent), fmt.Sprintf("TestNewTestFileLogger/%s", tt.name)) + assert.Contains(string(gotContent), "logger_test.go") + }) + } +}