-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Starting to address #6. This is just the start of the work, but it gives us something to build on. Basically, what we're trying to do is build log sink functionality into terraform-plugin-log. Normally, Terraform acts as a log sink, gathering up logs from core and from all the plugins, filtering them appropriately, and combining them into a single log output stream, and deciding where to send that stream. However, when running acceptance tests, the plugin is actually the longest-running process, and so this model breaks down. Instead, we need to have the plugin act as the sink, gathering the logs from core and the plugin and other plugins, combining them into a single stream, filtering the stream, and deciding where to output the stream. Rather than implement this functionality multiple times in different SDKs, it makes more sense to add it to the tfsdklog package and just call it from the test frameworks. There are a few parts to this. First, we need a new sink logger that all other loggers are derived from. It should read environment variables to determine what level of output is desired, what format of output is desired, and where to send that output. Second, we should make all our loggers be sub-loggers of that sink when the sink is set. Third, we should make sure our sub-loggers can only log at levels equal to or higher than the level of this new sink logger when it's set. And finally, we should provide functionality to process hclog JSON output (like from terraform when TF_LOG=json) and route it through this sink logger, so we can combine the log output of the CLI and other providers with the log output native to the process. This commit falls short in a few places: * sub-loggers aren't limited to equal-or-higher levels from the sink logger yet. * none of this has been tested, even manually. It builds? * we need to check that all the environment variables we need to be honoring are getting honored for the provider under test, the CLI, and the providers the CLI launches. This depends on #10.
- Loading branch information
1 parent
a8e5adf
commit a35183b
Showing
6 changed files
with
324 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package tfsdklog | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"os" | ||
"strings" | ||
|
||
"github.com/hashicorp/go-hclog" | ||
) | ||
|
||
const envLogCLI = "TF_LOG_CLI" | ||
|
||
func newCLILogger(ctx context.Context, commandID string) hclog.Logger { | ||
sink := getSink(ctx) | ||
if sink == nil { | ||
return nil | ||
} | ||
l := sink.Named("terraform") | ||
envLevel := strings.ToUpper(os.Getenv(envLog)) | ||
if envLevel != "" { | ||
if isValidLogLevel(envLevel) { | ||
l.SetLevel(hclog.LevelFromString(envLevel)) | ||
} else { | ||
fmt.Fprintf(os.Stderr, "[WARN] Invalid log level for %s: %q. Defaulting to %s level. Valid levels are: %+v", | ||
envLogCLI, envLevel, envLog, ValidLevels) | ||
} | ||
} | ||
l = l.With("command_id", commandID) | ||
return l | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package tfsdklog | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
"os" | ||
"strings" | ||
"syscall" | ||
|
||
"github.com/hashicorp/go-hclog" | ||
"github.com/hashicorp/terraform-plugin-log/internal/logging" | ||
) | ||
|
||
const ( | ||
envLog = "TF_LOG" | ||
envLogFile = "TF_LOG_PATH" | ||
) | ||
|
||
var sink hclog.Logger | ||
|
||
func init() { | ||
sink = newSink() | ||
} | ||
|
||
// ValidLevels are the string representations of levels that can be set for | ||
// loggers. | ||
var ValidLevels = []string{"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF"} | ||
|
||
func getSink(ctx context.Context) hclog.Logger { | ||
logger := ctx.Value(logging.SinkKey) | ||
if logger == nil { | ||
return nil | ||
} | ||
return logger.(hclog.Logger) | ||
} | ||
|
||
// RegisterSink 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. | ||
// | ||
// RegisterSink should only ever be called by test frameworks, providers should | ||
// never call it. | ||
// | ||
// RegisterSink must be called prior to any loggers being setup or instantiated. | ||
func RegisterSink(ctx context.Context) context.Context { | ||
return context.WithValue(ctx, logging.SinkKey, sink) | ||
} | ||
|
||
func newSink() hclog.Logger { | ||
logOutput := io.Writer(os.Stderr) | ||
var json bool | ||
var logLevel hclog.Level | ||
|
||
// if TF_LOG_PATH is set, output logs there | ||
if logPath := os.Getenv(envLogFile); logPath != "" { | ||
f, err := os.OpenFile(logPath, syscall.O_CREAT|syscall.O_RDWR|syscall.O_APPEND, 0666) | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "Error opening log file: %v\n", err) | ||
} else { | ||
logOutput = f | ||
} | ||
} | ||
|
||
// if TF_LOG is set, set the level | ||
envLevel := strings.ToUpper(os.Getenv(envLog)) | ||
if envLevel == "" { | ||
logLevel = hclog.Off | ||
} | ||
if envLevel == "JSON" { | ||
logLevel = hclog.Trace | ||
json = true | ||
} else if isValidLogLevel(envLevel) { | ||
logLevel = hclog.LevelFromString(envLevel) | ||
} else { | ||
fmt.Fprintf(os.Stderr, "[WARN] Invalid log level: %q. Defaulting to level: OFF. Valid levels are: %+v", | ||
envLevel, ValidLevels) | ||
} | ||
|
||
return hclog.New(&hclog.LoggerOptions{ | ||
Level: logLevel, | ||
Output: logOutput, | ||
IndependentLevels: true, | ||
JSONFormat: json, | ||
}) | ||
} | ||
|
||
func isValidLogLevel(level string) bool { | ||
for _, l := range ValidLevels { | ||
if level == string(l) { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
} |
Oops, something went wrong.