mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 02:28:09 +00:00 
			
		
		
		
	Add endpoints to provide ability to modify logging verbosity (#16111)
* add func to set level for specific logger * add endpoints to modify log level * initialize base logger with IndependentLevels * test to ensure other loggers remain unchanged * add DELETE loggers endpoints to revert back to config * add API docs page * add changelog entry * remove extraneous line * add log level field to Core struct * add godoc for getLogLevel * add some loggers to c.allLoggers
This commit is contained in:
		
							
								
								
									
										3
									
								
								changelog/16111.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								changelog/16111.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | ```release-note:improvement | ||||||
|  | core: Add `sys/loggers` and `sys/loggers/:name` endpoints to provide ability to modify logging verbosity | ||||||
|  | ``` | ||||||
| @@ -462,6 +462,7 @@ func (c *ServerCommand) runRecoveryMode() int { | |||||||
| 	c.logger = hclog.NewInterceptLogger(&hclog.LoggerOptions{ | 	c.logger = hclog.NewInterceptLogger(&hclog.LoggerOptions{ | ||||||
| 		Output:            c.gatedWriter, | 		Output:            c.gatedWriter, | ||||||
| 		Level:             level, | 		Level:             level, | ||||||
|  | 		IndependentLevels: true, | ||||||
| 		// Note that if logFormat is either unspecified or standard, then | 		// Note that if logFormat is either unspecified or standard, then | ||||||
| 		// the resulting logger's format will be standard. | 		// the resulting logger's format will be standard. | ||||||
| 		JSONFormat: logFormat == logging.JSONFormat, | 		JSONFormat: logFormat == logging.JSONFormat, | ||||||
| @@ -592,6 +593,7 @@ func (c *ServerCommand) runRecoveryMode() int { | |||||||
| 		Physical:     backend, | 		Physical:     backend, | ||||||
| 		StorageType:  config.Storage.Type, | 		StorageType:  config.Storage.Type, | ||||||
| 		Seal:         barrierSeal, | 		Seal:         barrierSeal, | ||||||
|  | 		LogLevel:     logLevelString, | ||||||
| 		Logger:       c.logger, | 		Logger:       c.logger, | ||||||
| 		DisableMlock: config.DisableMlock, | 		DisableMlock: config.DisableMlock, | ||||||
| 		RecoveryMode: c.flagRecovery, | 		RecoveryMode: c.flagRecovery, | ||||||
| @@ -1119,11 +1121,13 @@ func (c *ServerCommand) Run(args []string) int { | |||||||
| 			Mutex:             &sync.Mutex{}, | 			Mutex:             &sync.Mutex{}, | ||||||
| 			Output:            c.gatedWriter, | 			Output:            c.gatedWriter, | ||||||
| 			Level:             hclog.Trace, | 			Level:             hclog.Trace, | ||||||
|  | 			IndependentLevels: true, | ||||||
| 		}) | 		}) | ||||||
| 	} else { | 	} else { | ||||||
| 		c.logger = hclog.NewInterceptLogger(&hclog.LoggerOptions{ | 		c.logger = hclog.NewInterceptLogger(&hclog.LoggerOptions{ | ||||||
| 			Output:            c.gatedWriter, | 			Output:            c.gatedWriter, | ||||||
| 			Level:             level, | 			Level:             level, | ||||||
|  | 			IndependentLevels: true, | ||||||
| 			// Note that if logFormat is either unspecified or standard, then | 			// Note that if logFormat is either unspecified or standard, then | ||||||
| 			// the resulting logger's format will be standard. | 			// the resulting logger's format will be standard. | ||||||
| 			JSONFormat: logFormat == logging.JSONFormat, | 			JSONFormat: logFormat == logging.JSONFormat, | ||||||
|   | |||||||
| @@ -43,6 +43,7 @@ func NewVaultLogger(level log.Level) log.Logger { | |||||||
| func NewVaultLoggerWithWriter(w io.Writer, level log.Level) log.Logger { | func NewVaultLoggerWithWriter(w io.Writer, level log.Level) log.Logger { | ||||||
| 	opts := &log.LoggerOptions{ | 	opts := &log.LoggerOptions{ | ||||||
| 		Level:             level, | 		Level:             level, | ||||||
|  | 		IndependentLevels: true, | ||||||
| 		Output:            w, | 		Output:            w, | ||||||
| 		JSONFormat:        ParseEnvLogFormat() == JSONFormat, | 		JSONFormat:        ParseEnvLogFormat() == JSONFormat, | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -318,14 +318,19 @@ func (c *Core) startClusterListener(ctx context.Context) error { | |||||||
| 	networkLayer := c.clusterNetworkLayer | 	networkLayer := c.clusterNetworkLayer | ||||||
|  |  | ||||||
| 	if networkLayer == nil { | 	if networkLayer == nil { | ||||||
| 		networkLayer = cluster.NewTCPLayer(c.clusterListenerAddrs, c.logger.Named("cluster-listener.tcp")) | 		tcpLogger := c.logger.Named("cluster-listener.tcp") | ||||||
|  | 		networkLayer = cluster.NewTCPLayer(c.clusterListenerAddrs, tcpLogger) | ||||||
|  | 		c.AddLogger(tcpLogger) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	listenerLogger := c.logger.Named("cluster-listener") | ||||||
| 	c.clusterListener.Store(cluster.NewListener(networkLayer, | 	c.clusterListener.Store(cluster.NewListener(networkLayer, | ||||||
| 		c.clusterCipherSuites, | 		c.clusterCipherSuites, | ||||||
| 		c.logger.Named("cluster-listener"), | 		listenerLogger, | ||||||
| 		5*c.clusterHeartbeatInterval)) | 		5*c.clusterHeartbeatInterval)) | ||||||
|  |  | ||||||
|  | 	c.AddLogger(listenerLogger) | ||||||
|  |  | ||||||
| 	err := c.getClusterListener().Run(ctx) | 	err := c.getClusterListener().Run(ctx) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
|   | |||||||
| @@ -408,6 +408,9 @@ type Core struct { | |||||||
| 	baseLogger log.Logger | 	baseLogger log.Logger | ||||||
| 	logger     log.Logger | 	logger     log.Logger | ||||||
|  |  | ||||||
|  | 	// log level provided by config, CLI flag, or env | ||||||
|  | 	logLevel string | ||||||
|  |  | ||||||
| 	// Disables the trace display for Sentinel checks | 	// Disables the trace display for Sentinel checks | ||||||
| 	sentinelTraceDisabled bool | 	sentinelTraceDisabled bool | ||||||
|  |  | ||||||
| @@ -665,6 +668,8 @@ type CoreConfig struct { | |||||||
|  |  | ||||||
| 	SecureRandomReader io.Reader | 	SecureRandomReader io.Reader | ||||||
|  |  | ||||||
|  | 	LogLevel string | ||||||
|  |  | ||||||
| 	Logger log.Logger | 	Logger log.Logger | ||||||
|  |  | ||||||
| 	// Disables the trace display for Sentinel checks | 	// Disables the trace display for Sentinel checks | ||||||
| @@ -848,6 +853,7 @@ func CreateCore(conf *CoreConfig) (*Core, error) { | |||||||
| 		standbyStopCh:        new(atomic.Value), | 		standbyStopCh:        new(atomic.Value), | ||||||
| 		baseLogger:           conf.Logger, | 		baseLogger:           conf.Logger, | ||||||
| 		logger:               conf.Logger.Named("core"), | 		logger:               conf.Logger.Named("core"), | ||||||
|  | 		logLevel:             conf.LogLevel, | ||||||
|  |  | ||||||
| 		defaultLeaseTTL:                conf.DefaultLeaseTTL, | 		defaultLeaseTTL:                conf.DefaultLeaseTTL, | ||||||
| 		maxLeaseTTL:                    conf.MaxLeaseTTL, | 		maxLeaseTTL:                    conf.MaxLeaseTTL, | ||||||
| @@ -1034,6 +1040,10 @@ func NewCore(conf *CoreConfig) (*Core, error) { | |||||||
|  |  | ||||||
| 	c.loginMFABackend = NewLoginMFABackend(c, conf.Logger) | 	c.loginMFABackend = NewLoginMFABackend(c, conf.Logger) | ||||||
|  |  | ||||||
|  | 	if c.loginMFABackend.mfaLogger != nil { | ||||||
|  | 		c.AddLogger(c.loginMFABackend.mfaLogger) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	logicalBackends := make(map[string]logical.Factory) | 	logicalBackends := make(map[string]logical.Factory) | ||||||
| 	for k, f := range conf.LogicalBackends { | 	for k, f := range conf.LogicalBackends { | ||||||
| 		logicalBackends[k] = f | 		logicalBackends[k] = f | ||||||
| @@ -2816,6 +2826,19 @@ func (c *Core) SetLogLevel(level log.Level) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (c *Core) SetLogLevelByName(name string, level log.Level) error { | ||||||
|  | 	c.allLoggersLock.RLock() | ||||||
|  | 	defer c.allLoggersLock.RUnlock() | ||||||
|  | 	for _, logger := range c.allLoggers { | ||||||
|  | 		if logger.Name() == name { | ||||||
|  | 			logger.SetLevel(level) | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return fmt.Errorf("logger %q does not exist", name) | ||||||
|  | } | ||||||
|  |  | ||||||
| // SetConfig sets core's config object to the newly provided config. | // SetConfig sets core's config object to the newly provided config. | ||||||
| func (c *Core) SetConfig(conf *server.Config) { | func (c *Core) SetConfig(conf *server.Config) { | ||||||
| 	c.rawConfig.Store(conf) | 	c.rawConfig.Store(conf) | ||||||
|   | |||||||
| @@ -313,9 +313,12 @@ func getNumExpirationWorkers(c *Core, l log.Logger) int { | |||||||
| // NewExpirationManager creates a new ExpirationManager that is backed | // NewExpirationManager creates a new ExpirationManager that is backed | ||||||
| // using a given view, and uses the provided router for revocation. | // using a given view, and uses the provided router for revocation. | ||||||
| func NewExpirationManager(c *Core, view *BarrierView, e ExpireLeaseStrategy, logger log.Logger) *ExpirationManager { | func NewExpirationManager(c *Core, view *BarrierView, e ExpireLeaseStrategy, logger log.Logger) *ExpirationManager { | ||||||
| 	jobManager := fairshare.NewJobManager("expire", getNumExpirationWorkers(c, logger), logger.Named("job-manager"), c.metricSink) | 	managerLogger := logger.Named("job-manager") | ||||||
|  | 	jobManager := fairshare.NewJobManager("expire", getNumExpirationWorkers(c, logger), managerLogger, c.metricSink) | ||||||
| 	jobManager.Start() | 	jobManager.Start() | ||||||
|  |  | ||||||
|  | 	c.AddLogger(managerLogger) | ||||||
|  |  | ||||||
| 	exp := &ExpirationManager{ | 	exp := &ExpirationManager{ | ||||||
| 		core:        c, | 		core:        c, | ||||||
| 		router:      c.router, | 		router:      c.router, | ||||||
| @@ -1268,7 +1271,8 @@ func (m *ExpirationManager) Renew(ctx context.Context, leaseID string, increment | |||||||
| // RenewToken is used to renew a token which does not need to | // RenewToken is used to renew a token which does not need to | ||||||
| // invoke a logical backend. | // invoke a logical backend. | ||||||
| func (m *ExpirationManager) RenewToken(ctx context.Context, req *logical.Request, te *logical.TokenEntry, | func (m *ExpirationManager) RenewToken(ctx context.Context, req *logical.Request, te *logical.TokenEntry, | ||||||
| 	increment time.Duration) (*logical.Response, error) { | 	increment time.Duration, | ||||||
|  | ) (*logical.Response, error) { | ||||||
| 	defer metrics.MeasureSince([]string{"expire", "renew-token"}, time.Now()) | 	defer metrics.MeasureSince([]string{"expire", "renew-token"}, time.Now()) | ||||||
|  |  | ||||||
| 	tokenNS, err := NamespaceByID(ctx, te.NamespaceID, m.core) | 	tokenNS, err := NamespaceByID(ctx, te.NamespaceID, m.core) | ||||||
|   | |||||||
| @@ -4436,6 +4436,112 @@ func (b *SystemBackend) handleVersionHistoryList(ctx context.Context, req *logic | |||||||
| 	return logical.ListResponseWithInfo(respKeys, respKeyInfo), nil | 	return logical.ListResponseWithInfo(respKeys, respKeyInfo), nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // getLogLevel returns the hclog.Level that corresponds with the provided level string. | ||||||
|  | // This differs hclog.LevelFromString in that it supports additional level strings so | ||||||
|  | // that in remains consistent with the handling found in the "vault server" command. | ||||||
|  | func getLogLevel(logLevel string) (log.Level, error) { | ||||||
|  | 	var level log.Level | ||||||
|  |  | ||||||
|  | 	switch logLevel { | ||||||
|  | 	case "trace": | ||||||
|  | 		level = log.Trace | ||||||
|  | 	case "debug": | ||||||
|  | 		level = log.Debug | ||||||
|  | 	case "notice", "info", "": | ||||||
|  | 		level = log.Info | ||||||
|  | 	case "warn", "warning": | ||||||
|  | 		level = log.Warn | ||||||
|  | 	case "err", "error": | ||||||
|  | 		level = log.Error | ||||||
|  | 	default: | ||||||
|  | 		return level, fmt.Errorf("unrecognized log level %q", logLevel) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return level, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *SystemBackend) handleLoggersWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { | ||||||
|  | 	logLevelRaw, ok := d.GetOk("level") | ||||||
|  |  | ||||||
|  | 	if !ok { | ||||||
|  | 		return logical.ErrorResponse("level is required"), nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	logLevel := logLevelRaw.(string) | ||||||
|  | 	if logLevel == "" { | ||||||
|  | 		return logical.ErrorResponse("level is empty"), nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	level, err := getLogLevel(logLevel) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return logical.ErrorResponse(fmt.Sprintf("invalid level provided: %s", err.Error())), nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	b.Core.SetLogLevel(level) | ||||||
|  |  | ||||||
|  | 	return nil, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *SystemBackend) handleLoggersDelete(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { | ||||||
|  | 	level, err := getLogLevel(b.Core.logLevel) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return logical.ErrorResponse(fmt.Sprintf("log level from config is invalid: %s", err.Error())), nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	b.Core.SetLogLevel(level) | ||||||
|  |  | ||||||
|  | 	return nil, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *SystemBackend) handleLoggersByNameWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { | ||||||
|  | 	nameRaw, nameOk := d.GetOk("name") | ||||||
|  | 	if !nameOk { | ||||||
|  | 		return logical.ErrorResponse("name is required"), nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	logLevelRaw, logLevelOk := d.GetOk("level") | ||||||
|  |  | ||||||
|  | 	if !logLevelOk { | ||||||
|  | 		return logical.ErrorResponse("level is required"), nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	logLevel := logLevelRaw.(string) | ||||||
|  | 	if logLevel == "" { | ||||||
|  | 		return logical.ErrorResponse("level is empty"), nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	level, err := getLogLevel(logLevel) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return logical.ErrorResponse(fmt.Sprintf("invalid level provided: %s", err.Error())), nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = b.Core.SetLogLevelByName(nameRaw.(string), level) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return logical.ErrorResponse(fmt.Sprintf("invalid params: %s", err.Error())), nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *SystemBackend) handleLoggersByNameDelete(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { | ||||||
|  | 	nameRaw, ok := d.GetOk("name") | ||||||
|  | 	if !ok { | ||||||
|  | 		return logical.ErrorResponse("name is required"), nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	level, err := getLogLevel(b.Core.logLevel) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return logical.ErrorResponse(fmt.Sprintf("log level from config is invalid: %s", err.Error())), nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = b.Core.SetLogLevelByName(nameRaw.(string), level) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return logical.ErrorResponse(fmt.Sprintf("invalid params: %s", err.Error())), nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil, nil | ||||||
|  | } | ||||||
|  |  | ||||||
| func sanitizePath(path string) string { | func sanitizePath(path string) string { | ||||||
| 	if !strings.HasSuffix(path, "/") { | 	if !strings.HasSuffix(path, "/") { | ||||||
| 		path += "/" | 		path += "/" | ||||||
|   | |||||||
| @@ -288,6 +288,50 @@ func (b *SystemBackend) configPaths() []*framework.Path { | |||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
|  | 		{ | ||||||
|  | 			Pattern: "loggers$", | ||||||
|  | 			Fields: map[string]*framework.FieldSchema{ | ||||||
|  | 				"level": { | ||||||
|  | 					Type: framework.TypeString, | ||||||
|  | 					Description: "Log verbosity level. Supported values (in order of detail) are " + | ||||||
|  | 						"\"trace\", \"debug\", \"info\", \"warn\", and \"error\".", | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			Operations: map[logical.Operation]framework.OperationHandler{ | ||||||
|  | 				logical.UpdateOperation: &framework.PathOperation{ | ||||||
|  | 					Callback: b.handleLoggersWrite, | ||||||
|  | 					Summary:  "Modify the log level for all existing loggers.", | ||||||
|  | 				}, | ||||||
|  | 				logical.DeleteOperation: &framework.PathOperation{ | ||||||
|  | 					Callback: b.handleLoggersDelete, | ||||||
|  | 					Summary:  "Revert the all loggers to use log level provided in config.", | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			Pattern: "loggers/" + framework.MatchAllRegex("name"), | ||||||
|  | 			Fields: map[string]*framework.FieldSchema{ | ||||||
|  | 				"name": { | ||||||
|  | 					Type:        framework.TypeString, | ||||||
|  | 					Description: "The name of the logger to be modified.", | ||||||
|  | 				}, | ||||||
|  | 				"level": { | ||||||
|  | 					Type: framework.TypeString, | ||||||
|  | 					Description: "Log verbosity level. Supported values (in order of detail) are " + | ||||||
|  | 						"\"trace\", \"debug\", \"info\", \"warn\", and \"error\".", | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			Operations: map[logical.Operation]framework.OperationHandler{ | ||||||
|  | 				logical.UpdateOperation: &framework.PathOperation{ | ||||||
|  | 					Callback: b.handleLoggersByNameWrite, | ||||||
|  | 					Summary:  "Modify the log level of a single logger.", | ||||||
|  | 				}, | ||||||
|  | 				logical.DeleteOperation: &framework.PathOperation{ | ||||||
|  | 					Callback: b.handleLoggersByNameDelete, | ||||||
|  | 					Summary:  "Revert a single logger to use log level provided in config.", | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4556,3 +4556,274 @@ func TestProcessLimit(t *testing.T) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func validateLevel(level string, logger hclog.Logger) bool { | ||||||
|  | 	switch level { | ||||||
|  | 	case "trace": | ||||||
|  | 		return logger.IsTrace() | ||||||
|  | 	case "debug": | ||||||
|  | 		return logger.IsDebug() | ||||||
|  | 	case "notice", "info", "": | ||||||
|  | 		return logger.IsInfo() | ||||||
|  | 	case "warn", "warning": | ||||||
|  | 		return logger.IsWarn() | ||||||
|  | 	case "err", "error": | ||||||
|  | 		return logger.IsError() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestSystemBackend_Loggers(t *testing.T) { | ||||||
|  | 	testCases := []struct { | ||||||
|  | 		level       string | ||||||
|  | 		expectError bool | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			"trace", | ||||||
|  | 			false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"debug", | ||||||
|  | 			false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"notice", | ||||||
|  | 			false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"info", | ||||||
|  | 			false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"warn", | ||||||
|  | 			false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"warning", | ||||||
|  | 			false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"err", | ||||||
|  | 			false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"error", | ||||||
|  | 			false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"", | ||||||
|  | 			true, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"invalid", | ||||||
|  | 			true, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, tc := range testCases { | ||||||
|  | 		tc := tc | ||||||
|  |  | ||||||
|  | 		t.Run(fmt.Sprintf("all-loggers-%s", tc.level), func(t *testing.T) { | ||||||
|  | 			t.Parallel() | ||||||
|  |  | ||||||
|  | 			core, b, _ := testCoreSystemBackend(t) | ||||||
|  | 			config := core.GetCoreConfigInternal() | ||||||
|  |  | ||||||
|  | 			req := &logical.Request{ | ||||||
|  | 				Path:      "loggers", | ||||||
|  | 				Operation: logical.UpdateOperation, | ||||||
|  | 				Data: map[string]interface{}{ | ||||||
|  | 					"level": tc.level, | ||||||
|  | 				}, | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			resp, err := b.HandleRequest(namespace.RootContext(nil), req) | ||||||
|  | 			respIsError := resp != nil && resp.IsError() | ||||||
|  |  | ||||||
|  | 			if err != nil || (!tc.expectError && respIsError) { | ||||||
|  | 				t.Fatalf("unexpected error, err: %v, resp: %#v", err, resp) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if tc.expectError && !respIsError { | ||||||
|  | 				t.Fatalf("expected response error, resp: %#v", resp) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if !tc.expectError { | ||||||
|  | 				for _, logger := range core.allLoggers { | ||||||
|  | 					if !validateLevel(tc.level, logger) { | ||||||
|  | 						t.Fatalf("expected logger %q to be %q", logger.Name(), tc.level) | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			req = &logical.Request{ | ||||||
|  | 				Path:      fmt.Sprintf("loggers"), | ||||||
|  | 				Operation: logical.DeleteOperation, | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			resp, err = b.HandleRequest(namespace.RootContext(nil), req) | ||||||
|  | 			if err != nil || (resp != nil && resp.IsError()) { | ||||||
|  | 				t.Fatalf("unexpected error, err: %v, resp: %#v", err, resp) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			for _, logger := range core.allLoggers { | ||||||
|  | 				if !validateLevel(config.LogLevel, logger) { | ||||||
|  | 					t.Errorf("expected level of logger %q to match original config", logger.Name()) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestSystemBackend_LoggersByName(t *testing.T) { | ||||||
|  | 	testCases := []struct { | ||||||
|  | 		logger            string | ||||||
|  | 		level             string | ||||||
|  | 		expectWriteError  bool | ||||||
|  | 		expectDeleteError bool | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			"core", | ||||||
|  | 			"trace", | ||||||
|  | 			false, | ||||||
|  | 			false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"token", | ||||||
|  | 			"debug", | ||||||
|  | 			false, | ||||||
|  | 			false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"audit", | ||||||
|  | 			"notice", | ||||||
|  | 			false, | ||||||
|  | 			false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"expiration", | ||||||
|  | 			"info", | ||||||
|  | 			false, | ||||||
|  | 			false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"policy", | ||||||
|  | 			"warn", | ||||||
|  | 			false, | ||||||
|  | 			false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"activity", | ||||||
|  | 			"warning", | ||||||
|  | 			false, | ||||||
|  | 			false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"identity", | ||||||
|  | 			"err", | ||||||
|  | 			false, | ||||||
|  | 			false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"rollback", | ||||||
|  | 			"error", | ||||||
|  | 			false, | ||||||
|  | 			false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"system", | ||||||
|  | 			"", | ||||||
|  | 			true, | ||||||
|  | 			false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"quotas", | ||||||
|  | 			"invalid", | ||||||
|  | 			true, | ||||||
|  | 			false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"", | ||||||
|  | 			"info", | ||||||
|  | 			true, | ||||||
|  | 			true, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"does_not_exist", | ||||||
|  | 			"error", | ||||||
|  | 			true, | ||||||
|  | 			true, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, tc := range testCases { | ||||||
|  | 		tc := tc | ||||||
|  |  | ||||||
|  | 		t.Run(fmt.Sprintf("loggers-by-name-%s", tc.logger), func(t *testing.T) { | ||||||
|  | 			t.Parallel() | ||||||
|  |  | ||||||
|  | 			core, b, _ := testCoreSystemBackend(t) | ||||||
|  | 			config := core.GetCoreConfigInternal() | ||||||
|  |  | ||||||
|  | 			req := &logical.Request{ | ||||||
|  | 				Path:      fmt.Sprintf("loggers/%s", tc.logger), | ||||||
|  | 				Operation: logical.UpdateOperation, | ||||||
|  | 				Data: map[string]interface{}{ | ||||||
|  | 					"name":  tc.logger, | ||||||
|  | 					"level": tc.level, | ||||||
|  | 				}, | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			resp, err := b.HandleRequest(namespace.RootContext(nil), req) | ||||||
|  | 			respIsError := resp != nil && resp.IsError() | ||||||
|  |  | ||||||
|  | 			if err != nil || (!tc.expectWriteError && respIsError) { | ||||||
|  | 				t.Fatalf("unexpected error, err: %v, resp: %#v", err, resp) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if tc.expectWriteError && !respIsError { | ||||||
|  | 				t.Fatalf("expected response error, resp: %#v", resp) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if !tc.expectWriteError { | ||||||
|  | 				for _, logger := range core.allLoggers { | ||||||
|  | 					if logger.Name() != tc.logger && !validateLevel(config.LogLevel, logger) { | ||||||
|  | 						t.Errorf("expected level of logger %q to be unchanged", logger.Name()) | ||||||
|  | 					} | ||||||
|  |  | ||||||
|  | 					if !validateLevel(tc.level, logger) { | ||||||
|  | 						t.Fatalf("expected logger %q to be %q", logger.Name(), tc.level) | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			req = &logical.Request{ | ||||||
|  | 				Path:      fmt.Sprintf("loggers/%s", tc.logger), | ||||||
|  | 				Operation: logical.DeleteOperation, | ||||||
|  | 				Data: map[string]interface{}{ | ||||||
|  | 					"name": tc.logger, | ||||||
|  | 				}, | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			resp, err = b.HandleRequest(namespace.RootContext(nil), req) | ||||||
|  | 			respIsError = resp != nil && resp.IsError() | ||||||
|  |  | ||||||
|  | 			if err != nil || (!tc.expectDeleteError && respIsError) { | ||||||
|  | 				t.Fatalf("unexpected error, err: %v, resp: %#v", err, resp) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if tc.expectDeleteError && !respIsError { | ||||||
|  | 				t.Fatalf("expected response error, resp: %#v", resp) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if !tc.expectDeleteError { | ||||||
|  | 				for _, logger := range core.allLoggers { | ||||||
|  | 					if !validateLevel(config.LogLevel, logger) { | ||||||
|  | 						t.Errorf("expected level of logger %q to match original config", logger.Name()) | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -220,6 +220,7 @@ func (c *Core) startPeriodicRaftTLSRotate(ctx context.Context) error { | |||||||
|  |  | ||||||
| 	c.raftTLSRotationStopCh = make(chan struct{}) | 	c.raftTLSRotationStopCh = make(chan struct{}) | ||||||
| 	logger := c.logger.Named("raft") | 	logger := c.logger.Named("raft") | ||||||
|  | 	c.AddLogger(logger) | ||||||
|  |  | ||||||
| 	if c.isRaftHAOnly() { | 	if c.isRaftHAOnly() { | ||||||
| 		return c.raftTLSRotateDirect(ctx, logger, c.raftTLSRotationStopCh) | 		return c.raftTLSRotateDirect(ctx, logger, c.raftTLSRotationStopCh) | ||||||
|   | |||||||
| @@ -1198,10 +1198,12 @@ func NewTestLogger(t testing.T) *TestLogger { | |||||||
| 	// the sink to stop logging during cluster cleanup. | 	// the sink to stop logging during cluster cleanup. | ||||||
| 	logger := log.NewInterceptLogger(&log.LoggerOptions{ | 	logger := log.NewInterceptLogger(&log.LoggerOptions{ | ||||||
| 		Output:            ioutil.Discard, | 		Output:            ioutil.Discard, | ||||||
|  | 		IndependentLevels: true, | ||||||
| 	}) | 	}) | ||||||
| 	sink := log.NewSinkAdapter(&log.LoggerOptions{ | 	sink := log.NewSinkAdapter(&log.LoggerOptions{ | ||||||
| 		Output:            output, | 		Output:            output, | ||||||
| 		Level:             log.Trace, | 		Level:             log.Trace, | ||||||
|  | 		IndependentLevels: true, | ||||||
| 	}) | 	}) | ||||||
| 	logger.RegisterSink(sink) | 	logger.RegisterSink(sink) | ||||||
| 	return &TestLogger{ | 	return &TestLogger{ | ||||||
|   | |||||||
							
								
								
									
										102
									
								
								website/content/api-docs/system/loggers.mdx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								website/content/api-docs/system/loggers.mdx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | |||||||
|  | --- | ||||||
|  | layout: api | ||||||
|  | page_title: /sys/loggers - HTTP API | ||||||
|  | description: The `/sys/loggers` endpoint is used modify the verbosity level of logging. | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | # `/sys/loggers` | ||||||
|  |  | ||||||
|  | The `/sys/loggers` endpoint is used modify the verbosity level of logging. | ||||||
|  |  | ||||||
|  | ## Modify verbosity level of all loggers | ||||||
|  |  | ||||||
|  | | Method  | Path           | | ||||||
|  | | :------ | :------------- | | ||||||
|  | | `POST`  | `/sys/loggers` | | ||||||
|  |  | ||||||
|  | ### Parameters | ||||||
|  |  | ||||||
|  | - `level` `(string: <required>)` – Specifies the log verbosity level to be set for all loggers. | ||||||
|  | Supported values (in order of detail) are `"trace"`, `"debug"`, `"info"`, `"warn"`, and `"error"`. | ||||||
|  |  | ||||||
|  | ### Sample Payload | ||||||
|  |  | ||||||
|  | ```json | ||||||
|  | { | ||||||
|  |   "level": "debug", | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### Sample Request | ||||||
|  |  | ||||||
|  | ```shell-session | ||||||
|  | $ curl \ | ||||||
|  |     --header "X-Vault-Token: ..." \ | ||||||
|  |     --request POST \ | ||||||
|  |     --data @payload.json \ | ||||||
|  |     http://127.0.0.1:8200/v1/sys/loggers | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## Modify verbosity level of a single logger | ||||||
|  |  | ||||||
|  | | Method  | Path                 | | ||||||
|  | | :------ | :------------------- | | ||||||
|  | | `POST`  | `/sys/loggers/:name` | | ||||||
|  |  | ||||||
|  | ### Parameters | ||||||
|  |  | ||||||
|  | - `name` `(string: <required>)` – Specifies the logger to be modified (e.g. `audit`, `core`, `expiration`). | ||||||
|  | - `level` `(string: <required>)` – Specifies the log verbosity level to be set for the provided logger. | ||||||
|  | Supported values (in order of detail) are `"trace"`, `"debug"`, `"info"`, `"warn"`, and `"error"`. | ||||||
|  |  | ||||||
|  | ### Sample Payload | ||||||
|  |  | ||||||
|  | ```json | ||||||
|  | { | ||||||
|  |   "level": "debug", | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### Sample Request | ||||||
|  |  | ||||||
|  | ```shell-session | ||||||
|  | $ curl \ | ||||||
|  |     --header "X-Vault-Token: ..." \ | ||||||
|  |     --request POST \ | ||||||
|  |     --data @payload.json \ | ||||||
|  |     http://127.0.0.1:8200/v1/sys/loggers/core | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## Revert verbosity of all loggers to configured level | ||||||
|  |  | ||||||
|  | | Method    | Path           | | ||||||
|  | | :-------- | :------------- | | ||||||
|  | | `DELETE`  | `/sys/loggers` | | ||||||
|  |  | ||||||
|  | ### Sample Request | ||||||
|  |  | ||||||
|  | ```shell-session | ||||||
|  | $ curl \ | ||||||
|  |     --header "X-Vault-Token: ..." \ | ||||||
|  |     --request DELETE \ | ||||||
|  |     http://127.0.0.1:8200/v1/sys/loggers | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## Revert verbosity of a single logger to configured level | ||||||
|  |  | ||||||
|  | | Method    | Path                 | | ||||||
|  | | :-------- | :------------------- | | ||||||
|  | | `DELETE`  | `/sys/loggers/:name` | | ||||||
|  |  | ||||||
|  | ### Parameters | ||||||
|  |  | ||||||
|  | - `name` `(string: <required>)` – Specifies the logger to be modified (e.g. `audit`, `core`, `expiration`). | ||||||
|  |  | ||||||
|  | ### Sample Request | ||||||
|  |  | ||||||
|  | ```shell-session | ||||||
|  | $ curl \ | ||||||
|  |     --header "X-Vault-Token: ..." \ | ||||||
|  |     --request DELETE \ | ||||||
|  |     http://127.0.0.1:8200/v1/sys/loggers/core | ||||||
|  | ``` | ||||||
| @@ -482,6 +482,10 @@ | |||||||
|         "title": "<code>/sys/license/status</code>", |         "title": "<code>/sys/license/status</code>", | ||||||
|         "path": "system/license" |         "path": "system/license" | ||||||
|       }, |       }, | ||||||
|  |       { | ||||||
|  |         "title": "<code>/sys/loggers</code>", | ||||||
|  |         "path": "system/loggers" | ||||||
|  |       }, | ||||||
|       { |       { | ||||||
|         "title": "<code>/sys/managed-keys <sup>ENT</sup></code>", |         "title": "<code>/sys/managed-keys <sup>ENT</sup></code>", | ||||||
|         "path": "system/managed-keys" |         "path": "system/managed-keys" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Chris Capurso
					Chris Capurso