github.com/decred/politeia@v1.4.0/politeiawww/cmd/politeiavoter/log.go (about)

     1  // Copyright (c) 2017-2019 The Decred developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"path/filepath"
    11  
    12  	"github.com/decred/slog"
    13  	"github.com/jrick/logrotate/rotator"
    14  )
    15  
    16  // logWriter implements an io.Writer that outputs to both standard output and
    17  // the write-end pipe of an initialized log rotator.
    18  type logWriter struct{}
    19  
    20  func (logWriter) Write(p []byte) (n int, err error) {
    21  	os.Stdout.Write(p)
    22  	return logRotator.Write(p)
    23  }
    24  
    25  // Loggers per subsystem.  A single backend logger is created and all subsytem
    26  // loggers created from it will write to the backend.  When adding new
    27  // subsystems, add the subsystem logger variable here and to the
    28  // subsystemLoggers map.
    29  //
    30  // Loggers can not be used before the log rotator has been initialized with a
    31  // log file.  This must be performed early during application startup by calling
    32  // initLogRotator.
    33  var (
    34  	// backendLog is the logging backend used to create all subsystem loggers.
    35  	// The backend must not be used before the log rotator has been initialized,
    36  	// or data races and/or nil pointer dereferences will occur.
    37  	backendLog = slog.NewBackend(logWriter{})
    38  
    39  	// logRotator is one of the logging outputs.  It should be closed on
    40  	// application shutdown.
    41  	logRotator *rotator.Rotator
    42  
    43  	log = backendLog.Logger("POLV")
    44  )
    45  
    46  // subsystemLoggers maps each subsystem identifier to its associated logger.
    47  var subsystemLoggers = map[string]slog.Logger{
    48  	"POLV": log,
    49  }
    50  
    51  // initLogRotator initializes the logging rotater to write logs to logFile and
    52  // create roll files in the same directory.  It must be called before the
    53  // package-global log rotater variables are used.
    54  func initLogRotator(logFile string) {
    55  	logDir, _ := filepath.Split(logFile)
    56  	err := os.MkdirAll(logDir, 0700)
    57  	if err != nil {
    58  		fmt.Fprintf(os.Stderr, "failed to create log directory: %v\n", err)
    59  		os.Exit(1)
    60  	}
    61  	r, err := rotator.New(logFile, 10*1024, false, 3)
    62  	if err != nil {
    63  		fmt.Fprintf(os.Stderr, "failed to create file rotator: %v\n", err)
    64  		os.Exit(1)
    65  	}
    66  
    67  	logRotator = r
    68  }
    69  
    70  // setLogLevel sets the logging level for provided subsystem.  Invalid
    71  // subsystems are ignored.  Uninitialized subsystems are dynamically created as
    72  // needed.
    73  func setLogLevel(subsystemID string, logLevel string) {
    74  	// Ignore invalid subsystems.
    75  	logger, ok := subsystemLoggers[subsystemID]
    76  	if !ok {
    77  		return
    78  	}
    79  
    80  	// Defaults to info if the log level is invalid.
    81  	level, _ := slog.LevelFromString(logLevel)
    82  	logger.SetLevel(level)
    83  }
    84  
    85  // setLogLevels sets the log level for all subsystem loggers to the passed
    86  // level.  It also dynamically creates the subsystem loggers as needed, so it
    87  // can be used to initialize the logging system.
    88  func setLogLevels(logLevel string) {
    89  	// Configure all sub-systems with the new logging level.  Dynamically
    90  	// create loggers as needed.
    91  	for subsystemID := range subsystemLoggers {
    92  		setLogLevel(subsystemID, logLevel)
    93  	}
    94  }
    95  
    96  // LogClosure is a closure that can be printed with %v to be used to
    97  // generate expensive-to-create data for a detailed log level and avoid doing
    98  // the work if the data isn't printed.
    99  type logClosure func() string
   100  
   101  func (c logClosure) String() string {
   102  	return c()
   103  }