github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/logging/logconfig/config.go (about)

     1  package logconfig
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  
     8  	"github.com/eapache/channels"
     9  	"github.com/go-kit/kit/log"
    10  	"github.com/hyperledger/burrow/logging"
    11  
    12  	"github.com/BurntSushi/toml"
    13  	"github.com/hyperledger/burrow/logging/loggers"
    14  )
    15  
    16  type LoggingConfig struct {
    17  	RootSink *SinkConfig `toml:",omitempty"`
    18  	// Trace debug is very noisy - mostly from Tendermint
    19  	Trace bool
    20  	// Send to a channel - will not affect progress if logging graph is intensive but output will lag and some logs
    21  	// may be missed in shutdown
    22  	NonBlocking bool
    23  }
    24  
    25  // For encoding a top-level '[logging]' TOML table
    26  type LoggingConfigWrapper struct {
    27  	Logging *LoggingConfig `toml:",omitempty"`
    28  }
    29  
    30  func DefaultNodeLoggingConfig() *LoggingConfig {
    31  	// Output only Burrow messages on stdout
    32  	return &LoggingConfig{
    33  		RootSink: Sink().
    34  			SetOutput(StdoutOutput().SetFormat(loggers.JSONFormat)),
    35  	}
    36  }
    37  
    38  // Provide a defeault logging config
    39  func New() *LoggingConfig {
    40  	return &LoggingConfig{
    41  		NonBlocking: false,
    42  		RootSink:    Sink().SetOutput(StderrOutput().SetFormat(JSONFormat)),
    43  	}
    44  }
    45  
    46  func (lc *LoggingConfig) WithTrace() *LoggingConfig {
    47  	lc.Trace = true
    48  	return lc
    49  }
    50  
    51  func (lc *LoggingConfig) None() *LoggingConfig {
    52  	lc.RootSink = nil
    53  	return lc
    54  }
    55  
    56  func (lc *LoggingConfig) Root(configure func(sink *SinkConfig) *SinkConfig) *LoggingConfig {
    57  	lc.RootSink = configure(Sink())
    58  	return lc
    59  }
    60  
    61  // Returns the TOML for a top-level logging config wrapped with [logging]
    62  func (lc *LoggingConfig) RootTOMLString() string {
    63  	return TOMLString(LoggingConfigWrapper{lc})
    64  }
    65  
    66  func (lc *LoggingConfig) TOMLString() string {
    67  	return TOMLString(lc)
    68  }
    69  
    70  func (lc *LoggingConfig) RootJSONString() string {
    71  	return JSONString(LoggingConfigWrapper{lc})
    72  }
    73  
    74  func (lc *LoggingConfig) JSONString() string {
    75  	return JSONString(lc)
    76  }
    77  
    78  func (lc *LoggingConfig) MustLogger() *logging.Logger {
    79  	logger, err := lc.Logger()
    80  	if err != nil {
    81  		panic(err)
    82  	}
    83  	return logger
    84  }
    85  
    86  // Obtain a logger from this LoggingConfig
    87  func (lc *LoggingConfig) Logger() (*logging.Logger, error) {
    88  	outputLogger, errCh, err := newLogger(lc)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  	logger := logging.NewLogger(outputLogger)
    93  	if !lc.Trace {
    94  		logger.Trace = log.NewNopLogger()
    95  	}
    96  	go func() {
    97  		err := <-errCh.Out()
    98  		if err != nil {
    99  			fmt.Printf("Logging error: %v", err)
   100  		}
   101  	}()
   102  	return logger, nil
   103  }
   104  
   105  // Hot swap logging config by replacing output loggers built from this LoggingConfig
   106  func (lc *LoggingConfig) UpdateLogger(logger *logging.Logger) (channels.Channel, error) {
   107  	outputLogger, errCh, err := newLogger(lc)
   108  	if err != nil {
   109  		return channels.NewDeadChannel(), err
   110  	}
   111  	logger.SwapOutput(outputLogger)
   112  	return errCh, nil
   113  }
   114  
   115  // Helpers
   116  func newLogger(loggingConfig *LoggingConfig) (log.Logger, channels.Channel, error) {
   117  	outputLogger, _, err := loggingConfig.RootSink.BuildLogger()
   118  	if err != nil {
   119  		return nil, nil, err
   120  	}
   121  	var errCh channels.Channel = channels.NewDeadChannel()
   122  	var logger log.Logger = loggers.NewBurrowFormatLogger(outputLogger)
   123  	if loggingConfig.NonBlocking {
   124  		logger, errCh = loggers.NonBlockingLogger(logger)
   125  		return logger, errCh, nil
   126  	}
   127  	return logger, errCh, err
   128  }
   129  
   130  func TOMLString(v interface{}) string {
   131  	buf := new(bytes.Buffer)
   132  	encoder := toml.NewEncoder(buf)
   133  	err := encoder.Encode(v)
   134  	if err != nil {
   135  		// Seems like a reasonable compromise to make the string function clean
   136  		return fmt.Sprintf("Error encoding TOML: %s", err)
   137  	}
   138  	return buf.String()
   139  }
   140  
   141  func JSONString(v interface{}) string {
   142  	bs, err := json.MarshalIndent(v, "", "\t")
   143  	if err != nil {
   144  		return fmt.Sprintf("Error encoding JSON: %s", err)
   145  	}
   146  	return string(bs)
   147  }