diff --git a/builtin/logical/database/dbplugin/client.go b/builtin/logical/database/dbplugin/client.go index 95b614b8073d..6df39489fea0 100644 --- a/builtin/logical/database/dbplugin/client.go +++ b/builtin/logical/database/dbplugin/client.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/go-plugin" "github.com/hashicorp/vault/helper/pluginutil" + log "github.com/mgutz/logxi/v1" ) // DatabasePluginClient embeds a databasePluginRPCClient and wraps it's Close @@ -29,13 +30,13 @@ func (dc *DatabasePluginClient) Close() error { // newPluginClient returns a databaseRPCClient with a connection to a running // plugin. The client is wrapped in a DatabasePluginClient object to ensure the // plugin is killed on call of Close(). -func newPluginClient(sys pluginutil.RunnerUtil, pluginRunner *pluginutil.PluginRunner) (Database, error) { +func newPluginClient(sys pluginutil.RunnerUtil, pluginRunner *pluginutil.PluginRunner, logger log.Logger) (Database, error) { // pluginMap is the map of plugins we can dispense. var pluginMap = map[string]plugin.Plugin{ "database": new(DatabasePlugin), } - client, err := pluginRunner.Run(sys, pluginMap, handshakeConfig, []string{}) + client, err := pluginRunner.Run(sys, pluginMap, handshakeConfig, []string{}, logger) if err != nil { return nil, err } diff --git a/builtin/logical/database/dbplugin/plugin.go b/builtin/logical/database/dbplugin/plugin.go index a3bfcb6a3bb4..0becc9f4aa43 100644 --- a/builtin/logical/database/dbplugin/plugin.go +++ b/builtin/logical/database/dbplugin/plugin.go @@ -62,7 +62,7 @@ func PluginFactory(pluginName string, sys pluginutil.LookRunnerUtil, logger log. } else { // create a DatabasePluginClient instance - db, err = newPluginClient(sys, pluginRunner) + db, err = newPluginClient(sys, pluginRunner, logger) if err != nil { return nil, err } diff --git a/builtin/plugin/backend.go b/builtin/plugin/backend.go index 1e311255b708..1b7105d791d8 100644 --- a/builtin/plugin/backend.go +++ b/builtin/plugin/backend.go @@ -30,7 +30,7 @@ func Backend(conf *logical.BackendConfig) (logical.Backend, error) { name := conf.Config["plugin_name"] sys := conf.System - b, err := bplugin.NewBackend(name, sys) + b, err := bplugin.NewBackend(name, sys, conf.Logger) if err != nil { return nil, err } diff --git a/builtin/plugin/backend_test.go b/builtin/plugin/backend_test.go index 8c471b80b778..d9f182cb7854 100644 --- a/builtin/plugin/backend_test.go +++ b/builtin/plugin/backend_test.go @@ -4,12 +4,14 @@ import ( "os" "testing" + "github.com/hashicorp/vault/helper/logformat" "github.com/hashicorp/vault/helper/pluginutil" vaulthttp "github.com/hashicorp/vault/http" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/plugin" "github.com/hashicorp/vault/logical/plugin/mock" "github.com/hashicorp/vault/vault" + log "github.com/mgutz/logxi/v1" ) func TestBackend(t *testing.T) { @@ -71,7 +73,7 @@ func testConfig(t *testing.T) (*logical.BackendConfig, func()) { sys := vault.TestDynamicSystemView(core.Core) config := &logical.BackendConfig{ - Logger: nil, + Logger: logformat.NewVaultLogger(log.LevelTrace), System: sys, Config: map[string]string{ "plugin_name": "mock-plugin", diff --git a/helper/pluginutil/logger.go b/helper/pluginutil/logger.go new file mode 100644 index 000000000000..fff8ff129ed3 --- /dev/null +++ b/helper/pluginutil/logger.go @@ -0,0 +1,158 @@ +package pluginutil + +import ( + "bytes" + "fmt" + stdlog "log" + "strings" + + hclog "github.com/hashicorp/go-hclog" + log "github.com/mgutz/logxi/v1" +) + +// pluginLogFaker is a wrapper on logxi.Logger that +// implements hclog.Logger +type hclogFaker struct { + logger log.Logger + + name string + implied []interface{} +} + +func (f *hclogFaker) buildLog(msg string, args ...interface{}) (string, []interface{}) { + if f.name != "" { + msg = fmt.Sprintf("%s: %s", f.name, msg) + } + args = append(f.implied, args...) + + return msg, args +} + +func (f *hclogFaker) Trace(msg string, args ...interface{}) { + msg, args = f.buildLog(msg, args...) + f.logger.Trace(msg, args...) +} + +func (f *hclogFaker) Debug(msg string, args ...interface{}) { + msg, args = f.buildLog(msg, args...) + f.logger.Debug(msg, args...) +} + +func (f *hclogFaker) Info(msg string, args ...interface{}) { + msg, args = f.buildLog(msg, args...) + f.logger.Info(msg, args...) +} + +func (f *hclogFaker) Warn(msg string, args ...interface{}) { + msg, args = f.buildLog(msg, args...) + f.logger.Warn(msg, args...) +} + +func (f *hclogFaker) Error(msg string, args ...interface{}) { + msg, args = f.buildLog(msg, args...) + f.logger.Error(msg, args...) +} + +func (f *hclogFaker) IsTrace() bool { + return f.logger.IsTrace() +} + +func (f *hclogFaker) IsDebug() bool { + return f.logger.IsDebug() +} + +func (f *hclogFaker) IsInfo() bool { + return f.logger.IsInfo() +} + +func (f *hclogFaker) IsWarn() bool { + return f.logger.IsWarn() +} + +func (f *hclogFaker) IsError() bool { + return !f.logger.IsTrace() && !f.logger.IsDebug() && !f.logger.IsInfo() && !f.IsWarn() +} + +func (f *hclogFaker) With(args ...interface{}) hclog.Logger { + var nf = *f + nf.implied = append(nf.implied, args...) + return f +} + +func (f *hclogFaker) Named(name string) hclog.Logger { + var nf = *f + if nf.name != "" { + nf.name = nf.name + "." + name + } + return &nf +} + +func (f *hclogFaker) ResetNamed(name string) hclog.Logger { + var nf = *f + nf.name = name + return &nf +} + +func (f *hclogFaker) StandardLogger(opts *hclog.StandardLoggerOptions) *stdlog.Logger { + if opts == nil { + opts = &hclog.StandardLoggerOptions{} + } + + return stdlog.New(&stdlogAdapter{f, opts.InferLevels}, "", 0) +} + +// Provides a io.Writer to shim the data out of *log.Logger +// and back into our Logger. This is basically the only way to +// build upon *log.Logger. +type stdlogAdapter struct { + hl hclog.Logger + inferLevels bool +} + +// Take the data, infer the levels if configured, and send it through +// a regular Logger +func (s *stdlogAdapter) Write(data []byte) (int, error) { + str := string(bytes.TrimRight(data, " \t\n")) + + if s.inferLevels { + level, str := s.pickLevel(str) + switch level { + case hclog.Trace: + s.hl.Trace(str) + case hclog.Debug: + s.hl.Debug(str) + case hclog.Info: + s.hl.Info(str) + case hclog.Warn: + s.hl.Warn(str) + case hclog.Error: + s.hl.Error(str) + default: + s.hl.Info(str) + } + } else { + s.hl.Info(str) + } + + return len(data), nil +} + +// Detect, based on conventions, what log level this is +func (s *stdlogAdapter) pickLevel(str string) (hclog.Level, string) { + switch { + case strings.HasPrefix(str, "[DEBUG]"): + return hclog.Debug, strings.TrimSpace(str[7:]) + case strings.HasPrefix(str, "[TRACE]"): + return hclog.Trace, strings.TrimSpace(str[7:]) + case strings.HasPrefix(str, "[INFO]"): + return hclog.Info, strings.TrimSpace(str[6:]) + case strings.HasPrefix(str, "[WARN]"): + return hclog.Warn, strings.TrimSpace(str[7:]) + case strings.HasPrefix(str, "[ERROR]"): + return hclog.Error, strings.TrimSpace(str[7:]) + case strings.HasPrefix(str, "[ERR]"): + return hclog.Error, strings.TrimSpace(str[5:]) + default: + return hclog.Info, str + } +} diff --git a/helper/pluginutil/runner.go b/helper/pluginutil/runner.go index d050e1013f51..e34f070c2b25 100644 --- a/helper/pluginutil/runner.go +++ b/helper/pluginutil/runner.go @@ -10,6 +10,7 @@ import ( plugin "github.com/hashicorp/go-plugin" "github.com/hashicorp/vault/api" "github.com/hashicorp/vault/helper/wrapping" + log "github.com/mgutz/logxi/v1" ) // Looker defines the plugin Lookup function that looks into the plugin catalog @@ -45,7 +46,7 @@ type PluginRunner struct { // Run takes a wrapper instance, and the go-plugin paramaters and executes a // plugin. -func (r *PluginRunner) Run(wrapper RunnerUtil, pluginMap map[string]plugin.Plugin, hs plugin.HandshakeConfig, env []string) (*plugin.Client, error) { +func (r *PluginRunner) Run(wrapper RunnerUtil, pluginMap map[string]plugin.Plugin, hs plugin.HandshakeConfig, env []string, logger log.Logger) (*plugin.Client, error) { // Get a CA TLS Certificate certBytes, key, err := generateCert() if err != nil { @@ -79,12 +80,19 @@ func (r *PluginRunner) Run(wrapper RunnerUtil, pluginMap map[string]plugin.Plugi Hash: sha256.New(), } + // Create logger for the plugin client + clogger := &hclogFaker{ + logger: logger, + } + namedLogger := clogger.ResetNamed("plugin") + client := plugin.NewClient(&plugin.ClientConfig{ HandshakeConfig: hs, Plugins: pluginMap, Cmd: cmd, TLSConfig: clientTLSConfig, SecureConfig: secureConfig, + Logger: namedLogger, }) return client, nil diff --git a/logical/plugin/backend_client.go b/logical/plugin/backend_client.go index 0015b3fc0861..f18564b134a7 100644 --- a/logical/plugin/backend_client.go +++ b/logical/plugin/backend_client.go @@ -11,9 +11,8 @@ import ( // backendPluginClient implements logical.Backend and is the // go-plugin client. type backendPluginClient struct { - broker *plugin.MuxBroker - client *rpc.Client - pluginClient *plugin.Client + broker *plugin.MuxBroker + client *rpc.Client system logical.SystemView logger log.Logger diff --git a/logical/plugin/plugin.go b/logical/plugin/plugin.go index 4cfad90916a2..eeb6e073c3e9 100644 --- a/logical/plugin/plugin.go +++ b/logical/plugin/plugin.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/go-plugin" "github.com/hashicorp/vault/helper/pluginutil" "github.com/hashicorp/vault/logical" + log "github.com/mgutz/logxi/v1" ) // Register these types since we have to serialize and de-serialize tls.ConnectionState @@ -40,7 +41,7 @@ func (b *BackendPluginClient) Cleanup() { // NewBackend will return an instance of an RPC-based client implementation of the backend for // external plugins, or a concrete implementation of the backend if it is a builtin backend. // The backend is returned as a logical.Backend interface. -func NewBackend(pluginName string, sys pluginutil.LookRunnerUtil) (logical.Backend, error) { +func NewBackend(pluginName string, sys pluginutil.LookRunnerUtil, logger log.Logger) (logical.Backend, error) { // Look for plugin in the plugin catalog pluginRunner, err := sys.LookupPlugin(pluginName) if err != nil { @@ -64,7 +65,7 @@ func NewBackend(pluginName string, sys pluginutil.LookRunnerUtil) (logical.Backe } else { // create a backendPluginClient instance - backend, err = newPluginClient(sys, pluginRunner) + backend, err = newPluginClient(sys, pluginRunner, logger) if err != nil { return nil, err } @@ -73,12 +74,12 @@ func NewBackend(pluginName string, sys pluginutil.LookRunnerUtil) (logical.Backe return backend, nil } -func newPluginClient(sys pluginutil.RunnerUtil, pluginRunner *pluginutil.PluginRunner) (logical.Backend, error) { +func newPluginClient(sys pluginutil.RunnerUtil, pluginRunner *pluginutil.PluginRunner, logger log.Logger) (logical.Backend, error) { // pluginMap is the map of plugins we can dispense. pluginMap := map[string]plugin.Plugin{ "backend": &BackendPlugin{}, } - client, err := pluginRunner.Run(sys, pluginMap, handshakeConfig, []string{}) + client, err := pluginRunner.Run(sys, pluginMap, handshakeConfig, []string{}, logger) if err != nil { return nil, err }