github.com/kjdelisle/consul@v1.4.5/logger/logger.go (about)

     1  package logger
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"path/filepath"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/hashicorp/go-syslog"
    11  	"github.com/hashicorp/logutils"
    12  	"github.com/mitchellh/cli"
    13  )
    14  
    15  // Config is used to set up logging.
    16  type Config struct {
    17  	// LogLevel is the minimum level to be logged.
    18  	LogLevel string
    19  
    20  	// EnableSyslog controls forwarding to syslog.
    21  	EnableSyslog bool
    22  
    23  	// SyslogFacility is the destination for syslog forwarding.
    24  	SyslogFacility string
    25  
    26  	//LogFilePath is the path to write the logs to the user specified file.
    27  	LogFilePath string
    28  
    29  	//LogRotateDuration is the user specified time to rotate logs
    30  	LogRotateDuration time.Duration
    31  
    32  	//LogRotateBytes is the user specified byte limit to rotate logs
    33  	LogRotateBytes int
    34  }
    35  
    36  const (
    37  	// defaultRotateDuration is the default time taken by the agent to rotate logs
    38  	defaultRotateDuration = 24 * time.Hour
    39  )
    40  
    41  var (
    42  	logRotateDuration time.Duration
    43  	logRotateBytes    int
    44  )
    45  
    46  // Setup is used to perform setup of several logging objects:
    47  //
    48  // * A LevelFilter is used to perform filtering by log level.
    49  // * A GatedWriter is used to buffer logs until startup UI operations are
    50  //   complete. After this is flushed then logs flow directly to output
    51  //   destinations.
    52  // * A LogWriter provides a mean to temporarily hook logs, such as for running
    53  //   a command like "consul monitor".
    54  // * An io.Writer is provided as the sink for all logs to flow to.
    55  //
    56  // The provided ui object will get any log messages related to setting up
    57  // logging itself, and will also be hooked up to the gated logger. The final bool
    58  // parameter indicates if logging was set up successfully.
    59  func Setup(config *Config, ui cli.Ui) (*logutils.LevelFilter, *GatedWriter, *LogWriter, io.Writer, bool) {
    60  	// The gated writer buffers logs at startup and holds until it's flushed.
    61  	logGate := &GatedWriter{
    62  		Writer: &cli.UiWriter{Ui: ui},
    63  	}
    64  
    65  	// Set up the level filter.
    66  	logFilter := LevelFilter()
    67  	logFilter.MinLevel = logutils.LogLevel(strings.ToUpper(config.LogLevel))
    68  	logFilter.Writer = logGate
    69  	if !ValidateLevelFilter(logFilter.MinLevel, logFilter) {
    70  		ui.Error(fmt.Sprintf(
    71  			"Invalid log level: %s. Valid log levels are: %v",
    72  			logFilter.MinLevel, logFilter.Levels))
    73  		return nil, nil, nil, nil, false
    74  	}
    75  
    76  	// Set up syslog if it's enabled.
    77  	var syslog io.Writer
    78  	if config.EnableSyslog {
    79  		retries := 12
    80  		delay := 5 * time.Second
    81  		for i := 0; i <= retries; i++ {
    82  			l, err := gsyslog.NewLogger(gsyslog.LOG_NOTICE, config.SyslogFacility, "consul")
    83  			if err == nil {
    84  				syslog = &SyslogWrapper{l, logFilter}
    85  				break
    86  			}
    87  
    88  			ui.Error(fmt.Sprintf("Syslog setup error: %v", err))
    89  			if i == retries {
    90  				timeout := time.Duration(retries) * delay
    91  				ui.Error(fmt.Sprintf("Syslog setup did not succeed within timeout (%s).", timeout.String()))
    92  				return nil, nil, nil, nil, false
    93  			}
    94  
    95  			ui.Error(fmt.Sprintf("Retrying syslog setup in %s...", delay.String()))
    96  			time.Sleep(delay)
    97  		}
    98  	}
    99  	// Create a log writer, and wrap a logOutput around it
   100  	logWriter := NewLogWriter(512)
   101  	writers := []io.Writer{logFilter, logWriter}
   102  
   103  	var logOutput io.Writer
   104  	if syslog != nil {
   105  		writers = append(writers, syslog)
   106  	}
   107  
   108  	// Create a file logger if the user has specified the path to the log file
   109  	if config.LogFilePath != "" {
   110  		dir, fileName := filepath.Split(config.LogFilePath)
   111  		// If a path is provided but has no fileName a default is provided.
   112  		if fileName == "" {
   113  			fileName = "consul.log"
   114  		}
   115  		// Try to enter the user specified log rotation duration first
   116  		if config.LogRotateDuration != 0 {
   117  			logRotateDuration = config.LogRotateDuration
   118  		} else {
   119  			// Default to 24 hrs if no rotation period is specified
   120  			logRotateDuration = defaultRotateDuration
   121  		}
   122  		// User specified byte limit for log rotation if one is provided
   123  		if config.LogRotateBytes != 0 {
   124  			logRotateBytes = config.LogRotateBytes
   125  		}
   126  		logFile := &LogFile{fileName: fileName, logPath: dir, duration: logRotateDuration, MaxBytes: logRotateBytes}
   127  		writers = append(writers, logFile)
   128  	}
   129  
   130  	logOutput = io.MultiWriter(writers...)
   131  	return logFilter, logGate, logWriter, logOutput, true
   132  }