consul/logging/logger.go

129 lines
3.5 KiB
Go
Raw Normal View History

// Copyright (c) HashiCorp, Inc.
[COMPLIANCE] License changes (#18443) * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at <Blog URL>, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
2023-08-11 13:12:13 +00:00
// SPDX-License-Identifier: BUSL-1.1
package logging
import (
"fmt"
"io"
"path/filepath"
"time"
"github.com/hashicorp/go-hclog"
gsyslog "github.com/hashicorp/go-syslog"
)
// Config is used to set up logging.
type Config struct {
// LogLevel is the minimum level to be logged.
LogLevel string
// LogJSON controls outputing logs in a JSON format.
LogJSON bool
// Name is the name the returned logger will use to prefix log lines.
Name string
// EnableSyslog controls forwarding to syslog.
EnableSyslog bool
// SyslogFacility is the destination for syslog forwarding.
SyslogFacility string
2022-01-06 21:07:09 +00:00
// LogFilePath is the path to write the logs to the user specified file.
LogFilePath string
2022-01-06 21:07:09 +00:00
// LogRotateDuration is the user specified time to rotate logs
LogRotateDuration time.Duration
2022-01-06 21:07:09 +00:00
// LogRotateBytes is the user specified byte limit to rotate logs
LogRotateBytes int
2022-01-06 21:07:09 +00:00
// LogRotateMaxFiles is the maximum number of past archived log files to keep
LogRotateMaxFiles int
}
// defaultRotateDuration is the default time taken by the agent to rotate logs
const defaultRotateDuration = 24 * time.Hour
type LogSetupErrorFn func(string)
2022-01-06 21:07:09 +00:00
// noErrorWriter is a wrapper to suppress errors when writing to w.
type noErrorWriter struct {
w io.Writer
}
func (w noErrorWriter) Write(p []byte) (n int, err error) {
_, _ = w.w.Write(p)
// We purposely return n == len(p) as if write was successful
return len(p), nil
}
// Setup logging from Config, and return an hclog Logger.
//
// Logs may be written to out, and optionally to syslog, and a file.
func Setup(config Config, out io.Writer) (hclog.InterceptLogger, error) {
if !ValidateLogLevel(config.LogLevel) {
return nil, fmt.Errorf("Invalid log level: %s. Valid log levels are: %v",
config.LogLevel,
allowedLogLevels)
}
2022-01-06 21:07:09 +00:00
// If out is os.Stdout and Consul is being run as a Windows Service, writes will
// fail silently, which may inadvertently prevent writes to other writers.
// noErrorWriter is used as a wrapper to suppress any errors when writing to out.
writers := []io.Writer{noErrorWriter{w: out}}
if config.EnableSyslog {
retries := 12
delay := 5 * time.Second
for i := 0; i <= retries; i++ {
syslog, err := gsyslog.NewLogger(gsyslog.LOG_NOTICE, config.SyslogFacility, "consul")
if err == nil {
writers = append(writers, &SyslogWrapper{l: syslog})
break
}
if i == retries {
timeout := time.Duration(retries) * delay
return nil, fmt.Errorf("Syslog setup did not succeed within timeout (%s).", timeout.String())
}
time.Sleep(delay)
}
}
// Create a file logger if the user has specified the path to the log file
if config.LogFilePath != "" {
dir, fileName := filepath.Split(config.LogFilePath)
if fileName == "" {
fileName = "consul.log"
}
if config.LogRotateDuration == 0 {
config.LogRotateDuration = defaultRotateDuration
}
logFile := &LogFile{
fileName: fileName,
logPath: dir,
duration: config.LogRotateDuration,
MaxBytes: config.LogRotateBytes,
MaxFiles: config.LogRotateMaxFiles,
}
if err := logFile.pruneFiles(); err != nil {
return nil, fmt.Errorf("Failed to prune log files: %w", err)
}
if err := logFile.openNew(); err != nil {
return nil, fmt.Errorf("Failed to setup logging: %w", err)
}
writers = append(writers, logFile)
}
logger := hclog.NewInterceptLogger(&hclog.LoggerOptions{
Level: LevelFromString(config.LogLevel),
Name: config.Name,
Output: io.MultiWriter(writers...),
JSONFormat: config.LogJSON,
})
return logger, nil
}