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

     1  package logconfig
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  
     7  	"github.com/eapache/channels"
     8  	"github.com/go-kit/kit/log"
     9  	"github.com/hyperledger/burrow/logging"
    10  	"github.com/hyperledger/burrow/logging/loggers"
    11  	"github.com/hyperledger/burrow/logging/structure"
    12  )
    13  
    14  // This file contains definitions for a configurable output graph for the
    15  // logging system.
    16  
    17  type outputType string
    18  type transformType string
    19  type filterMode string
    20  
    21  const (
    22  	// OutputType
    23  	NoOutput outputType = ""
    24  	Stdout   outputType = "stdout"
    25  	Stderr   outputType = "stderr"
    26  	File     outputType = "file"
    27  
    28  	// TransformType
    29  	NoTransform transformType = ""
    30  	// Filter log lines
    31  	Filter transformType = "filter"
    32  	// Remove key-val pairs from each log line
    33  	Prune transformType = "prune"
    34  	// Add key value pairs to each log line
    35  	Label     transformType = "label"
    36  	Capture   transformType = "capture"
    37  	Sort      transformType = "sort"
    38  	Vectorise transformType = "vectorise"
    39  
    40  	// TODO [Silas]: add 'flush on exit' transform which flushes the buffer of
    41  	// CaptureLogger to its OutputLogger a non-passthrough capture when an exit
    42  	// signal is detected or some other exceptional thing happens
    43  
    44  	NoFilterMode          filterMode = ""
    45  	IncludeWhenAllMatch   filterMode = "include_when_all_match"
    46  	IncludeWhenAnyMatches filterMode = "include_when_any_match"
    47  	ExcludeWhenAllMatch   filterMode = "exclude_when_all_match"
    48  	ExcludeWhenAnyMatches filterMode = "exclude_when_any_match"
    49  )
    50  
    51  // Only include log lines matching the filter so negate the predicate in filter
    52  func (mode filterMode) Include() bool {
    53  	switch mode {
    54  	case IncludeWhenAllMatch, IncludeWhenAnyMatches:
    55  		return true
    56  	default:
    57  		return false
    58  	}
    59  }
    60  
    61  // The predicate should only evaluate true if all the key value predicates match
    62  func (mode filterMode) MatchAll() bool {
    63  	switch mode {
    64  	case IncludeWhenAllMatch, ExcludeWhenAllMatch:
    65  		return true
    66  	default:
    67  		return false
    68  	}
    69  }
    70  
    71  // Exclude log lines that match the predicate
    72  func (mode filterMode) Exclude() bool {
    73  	return !mode.Include()
    74  }
    75  
    76  // The predicate should evaluate true if at least one of the key value predicates matches
    77  func (mode filterMode) MatchAny() bool {
    78  	return !mode.MatchAll()
    79  }
    80  
    81  // Sink configuration types
    82  type (
    83  	// Outputs
    84  	// TODO: reintroduce syslog removed when we dropped log15 dependency
    85  	SyslogConfig struct {
    86  		Url string
    87  		Tag string
    88  	}
    89  
    90  	FileConfig struct {
    91  		Path string
    92  	}
    93  
    94  	OutputConfig struct {
    95  		OutputType    outputType
    96  		Format        string
    97  		*FileConfig   `json:",omitempty" toml:",omitempty"`
    98  		*SyslogConfig `json:",omitempty" toml:",omitempty"`
    99  	}
   100  
   101  	// Transforms
   102  	LabelConfig struct {
   103  		Labels map[string]string
   104  		Prefix bool
   105  	}
   106  
   107  	PruneConfig struct {
   108  		Keys        []string
   109  		IncludeKeys bool
   110  	}
   111  
   112  	CaptureConfig struct {
   113  		Name        string
   114  		BufferCap   int
   115  		Passthrough bool
   116  	}
   117  
   118  	// Generates true if KeyRegex matches a log line key and ValueRegex matches that key's value.
   119  	// If ValueRegex is empty then returns true if any key matches
   120  	// If KeyRegex is empty then returns true if any value matches
   121  	KeyValuePredicateConfig struct {
   122  		KeyRegex   string
   123  		ValueRegex string
   124  	}
   125  
   126  	// Filter types
   127  	FilterConfig struct {
   128  		FilterMode filterMode
   129  		// Predicates to match a log line against using FilterMode
   130  		Predicates []*KeyValuePredicateConfig
   131  	}
   132  
   133  	SortConfig struct {
   134  		// Sort keys-values with keys in this list first
   135  		Keys []string
   136  	}
   137  
   138  	TransformConfig struct {
   139  		TransformType transformType
   140  		LabelConfig   *LabelConfig   `json:",omitempty" toml:",omitempty"`
   141  		PruneConfig   *PruneConfig   `json:",omitempty" toml:",omitempty"`
   142  		CaptureConfig *CaptureConfig `json:",omitempty" toml:",omitempty"`
   143  		FilterConfig  *FilterConfig  `json:",omitempty" toml:",omitempty"`
   144  		SortConfig    *SortConfig    `json:",omitempty" toml:",omitempty"`
   145  	}
   146  
   147  	// Sink
   148  	// A Sink describes a logger that logs to zero or one output and logs to zero or more child sinks.
   149  	// before transmitting its log it applies zero or one transforms to the stream of log lines.
   150  	// by chaining together many Sinks arbitrary transforms to and multi
   151  	SinkConfig struct {
   152  		Transform *TransformConfig `json:",omitempty" toml:",omitempty"`
   153  		Sinks     []*SinkConfig    `json:",omitempty" toml:",omitempty"`
   154  		Output    *OutputConfig    `json:",omitempty" toml:",omitempty"`
   155  	}
   156  )
   157  
   158  // Builders
   159  func Sink() *SinkConfig {
   160  	return &SinkConfig{}
   161  }
   162  
   163  func (sc *SinkConfig) MustLogger() *logging.Logger {
   164  	return sc.LoggingConfig().MustLogger()
   165  }
   166  
   167  func (sc *SinkConfig) Logger() (*logging.Logger, error) {
   168  	return sc.LoggingConfig().Logger()
   169  }
   170  
   171  // Wrap this sink as RootSink of LoggingCOnfig
   172  func (sc *SinkConfig) LoggingConfig() *LoggingConfig {
   173  	lc := New()
   174  	lc.RootSink = sc
   175  	return lc
   176  }
   177  
   178  func (sc *SinkConfig) AddSinks(sinks ...*SinkConfig) *SinkConfig {
   179  	sc.Sinks = append(sc.Sinks, sinks...)
   180  	return sc
   181  }
   182  
   183  func (sc *SinkConfig) SetTransform(transform *TransformConfig) *SinkConfig {
   184  	sc.Transform = transform
   185  	return sc
   186  }
   187  
   188  func (sc *SinkConfig) FilterScope(scope string) *SinkConfig {
   189  	return sc.SetTransform(FilterTransform(IncludeWhenAllMatch, structure.ScopeKey, scope))
   190  }
   191  
   192  func (sc *SinkConfig) Terminal() *SinkConfig {
   193  	return sc.SetOutput(StderrOutput().SetFormat(TerminalFormat))
   194  }
   195  
   196  func (sc *SinkConfig) SetOutput(output *OutputConfig) *SinkConfig {
   197  	sc.Output = output
   198  	return sc
   199  }
   200  
   201  func (outputConfig *OutputConfig) SetFormat(format string) *OutputConfig {
   202  	outputConfig.Format = format
   203  	return outputConfig
   204  }
   205  
   206  func StdoutOutput() *OutputConfig {
   207  	return &OutputConfig{
   208  		OutputType: Stdout,
   209  	}
   210  }
   211  
   212  func StderrOutput() *OutputConfig {
   213  	return &OutputConfig{
   214  		OutputType: Stderr,
   215  	}
   216  }
   217  
   218  func FileOutput(path string) *OutputConfig {
   219  	return &OutputConfig{
   220  		OutputType: File,
   221  		FileConfig: &FileConfig{
   222  			Path: path,
   223  		},
   224  	}
   225  }
   226  
   227  // Transforms
   228  
   229  func CaptureTransform(name string, bufferCap int, passthrough bool) *TransformConfig {
   230  	return &TransformConfig{
   231  		TransformType: Capture,
   232  		CaptureConfig: &CaptureConfig{
   233  			Name:        name,
   234  			BufferCap:   bufferCap,
   235  			Passthrough: passthrough,
   236  		},
   237  	}
   238  }
   239  
   240  func LabelTransform(prefix bool, labelKeyvals ...string) *TransformConfig {
   241  	length := len(labelKeyvals) / 2
   242  	labels := make(map[string]string, length)
   243  	for i := 0; i < 2*length; i += 2 {
   244  		labels[labelKeyvals[i]] = labelKeyvals[i+1]
   245  	}
   246  	return &TransformConfig{
   247  		TransformType: Label,
   248  		LabelConfig: &LabelConfig{
   249  			Prefix: prefix,
   250  			Labels: labels,
   251  		},
   252  	}
   253  }
   254  
   255  func OnlyTransform(keys ...string) *TransformConfig {
   256  	return &TransformConfig{
   257  		TransformType: Prune,
   258  		PruneConfig: &PruneConfig{
   259  			Keys:        keys,
   260  			IncludeKeys: true,
   261  		},
   262  	}
   263  }
   264  
   265  func PruneTransform(keys ...string) *TransformConfig {
   266  	return &TransformConfig{
   267  		TransformType: Prune,
   268  		PruneConfig: &PruneConfig{
   269  			Keys: keys,
   270  		},
   271  	}
   272  }
   273  
   274  func FilterTransform(fmode filterMode, keyValueRegexes ...string) *TransformConfig {
   275  	if len(keyValueRegexes)%2 == 1 {
   276  		keyValueRegexes = append(keyValueRegexes, "")
   277  	}
   278  	length := len(keyValueRegexes) / 2
   279  	predicates := make([]*KeyValuePredicateConfig, length)
   280  	for i := 0; i < length; i++ {
   281  		kv := i * 2
   282  		predicates[i] = &KeyValuePredicateConfig{
   283  			KeyRegex:   keyValueRegexes[kv],
   284  			ValueRegex: keyValueRegexes[kv+1],
   285  		}
   286  	}
   287  	return &TransformConfig{
   288  		TransformType: Filter,
   289  		FilterConfig: &FilterConfig{
   290  			FilterMode: fmode,
   291  			Predicates: predicates,
   292  		},
   293  	}
   294  }
   295  
   296  func (filterConfig *FilterConfig) SetFilterMode(filterMode filterMode) *FilterConfig {
   297  	filterConfig.FilterMode = filterMode
   298  	return filterConfig
   299  }
   300  
   301  func (filterConfig *FilterConfig) AddPredicate(keyRegex, valueRegex string) *FilterConfig {
   302  	filterConfig.Predicates = append(filterConfig.Predicates, &KeyValuePredicateConfig{
   303  		KeyRegex:   keyRegex,
   304  		ValueRegex: valueRegex,
   305  	})
   306  	return filterConfig
   307  }
   308  
   309  func SortTransform(keys ...string) *TransformConfig {
   310  	return &TransformConfig{
   311  		TransformType: Sort,
   312  		SortConfig: &SortConfig{
   313  			Keys: keys,
   314  		},
   315  	}
   316  }
   317  
   318  func VectoriseTransform() *TransformConfig {
   319  	return &TransformConfig{
   320  		TransformType: Vectorise,
   321  	}
   322  }
   323  
   324  // Logger formation
   325  func (sc *SinkConfig) BuildLogger() (log.Logger, map[string]*loggers.CaptureLogger, error) {
   326  	if sc == nil {
   327  		return log.NewNopLogger(), nil, nil
   328  	}
   329  	return BuildLoggerFromSinkConfig(sc, make(map[string]*loggers.CaptureLogger))
   330  }
   331  
   332  func BuildLoggerFromSinkConfig(sinkConfig *SinkConfig, captures map[string]*loggers.CaptureLogger) (log.Logger,
   333  	map[string]*loggers.CaptureLogger, error) {
   334  
   335  	if sinkConfig == nil {
   336  		return log.NewNopLogger(), captures, nil
   337  	}
   338  	numSinks := len(sinkConfig.Sinks)
   339  	outputLoggers := make([]log.Logger, numSinks, numSinks+1)
   340  	// We need a depth-first post-order over the output loggers so we'll keep
   341  	// recurring into children sinks we reach a terminal sink (with no children)
   342  	for i, sc := range sinkConfig.Sinks {
   343  		var err error
   344  		outputLoggers[i], captures, err = BuildLoggerFromSinkConfig(sc, captures)
   345  		if err != nil {
   346  			return nil, nil, err
   347  		}
   348  	}
   349  
   350  	// Grab the outputs after we have terminated any children sinks above
   351  	if sinkConfig.Output != nil && sinkConfig.Output.OutputType != NoOutput {
   352  		l, err := BuildOutputLogger(sinkConfig.Output)
   353  		if err != nil {
   354  			return nil, captures, err
   355  		}
   356  		outputLoggers = append(outputLoggers, l)
   357  	}
   358  
   359  	outputLogger := loggers.NewMultipleOutputLogger(outputLoggers...)
   360  
   361  	if sinkConfig.Transform != nil && sinkConfig.Transform.TransformType != NoTransform {
   362  		return BuildTransformLoggerPassthrough(sinkConfig.Transform, captures, outputLogger)
   363  	}
   364  	return outputLogger, captures, nil
   365  }
   366  
   367  func BuildOutputLogger(outputConfig *OutputConfig) (log.Logger, error) {
   368  	switch outputConfig.OutputType {
   369  	case NoOutput:
   370  		return log.NewNopLogger(), nil
   371  	case File:
   372  		return loggers.NewFileLogger(outputConfig.FileConfig.Path, outputConfig.Format)
   373  	case Stdout:
   374  		return loggers.NewStreamLogger(os.Stdout, outputConfig.Format)
   375  	case Stderr:
   376  		return loggers.NewStreamLogger(os.Stderr, outputConfig.Format)
   377  	default:
   378  		return nil, fmt.Errorf("could not build logger for output: '%s'",
   379  			outputConfig.OutputType)
   380  	}
   381  }
   382  
   383  func BuildTransformLoggerPassthrough(transformConfig *TransformConfig, captures map[string]*loggers.CaptureLogger,
   384  	outputLogger log.Logger) (log.Logger, map[string]*loggers.CaptureLogger, error) {
   385  
   386  	transformThenOutputLogger, captures, err := BuildTransformLogger(transformConfig, captures, outputLogger)
   387  	if err != nil {
   388  		return nil, nil, err
   389  	}
   390  	// send signals through captures so they can be flushed
   391  	if transformConfig.TransformType == Capture {
   392  		return transformThenOutputLogger, captures, nil
   393  	}
   394  	return signalPassthroughLogger(outputLogger, transformThenOutputLogger), captures, nil
   395  }
   396  
   397  func BuildTransformLogger(transformConfig *TransformConfig, captures map[string]*loggers.CaptureLogger,
   398  	outputLogger log.Logger) (log.Logger, map[string]*loggers.CaptureLogger, error) {
   399  
   400  	switch transformConfig.TransformType {
   401  	case NoTransform:
   402  		return outputLogger, captures, nil
   403  	case Label:
   404  		if transformConfig.LabelConfig == nil {
   405  			return nil, nil, fmt.Errorf("label transform specified but no LabelConfig provided")
   406  		}
   407  		keyvals := make([]interface{}, 0, len(transformConfig.LabelConfig.Labels)*2)
   408  		for k, v := range transformConfig.LabelConfig.Labels {
   409  			keyvals = append(keyvals, k, v)
   410  		}
   411  		if transformConfig.LabelConfig.Prefix {
   412  			return log.WithPrefix(outputLogger, keyvals...), captures, nil
   413  		} else {
   414  			return log.With(outputLogger, keyvals...), captures, nil
   415  		}
   416  	case Prune:
   417  		if transformConfig.PruneConfig == nil {
   418  			return nil, nil, fmt.Errorf("prune transform specified but no PruneConfig provided")
   419  		}
   420  		keys := make([]interface{}, len(transformConfig.PruneConfig.Keys))
   421  		for i, k := range transformConfig.PruneConfig.Keys {
   422  			keys[i] = k
   423  		}
   424  		return log.LoggerFunc(func(keyvals ...interface{}) error {
   425  			if transformConfig.PruneConfig.IncludeKeys {
   426  				return outputLogger.Log(structure.OnlyKeys(keyvals, keys...)...)
   427  			} else {
   428  				return outputLogger.Log(structure.RemoveKeys(keyvals, keys...)...)
   429  			}
   430  		}), captures, nil
   431  
   432  	case Capture:
   433  		if transformConfig.CaptureConfig == nil {
   434  			return nil, nil, fmt.Errorf("capture transform specified but no CaptureConfig provided")
   435  		}
   436  		name := transformConfig.CaptureConfig.Name
   437  		if _, ok := captures[name]; ok {
   438  			return nil, captures, fmt.Errorf("could not register new logging capture since name '%s' already "+
   439  				"registered", name)
   440  		}
   441  		// Create a capture logger according to configuration (it may tee the output)
   442  		// or capture it to be flushed later
   443  		captureLogger := loggers.NewCaptureLogger(outputLogger,
   444  			channels.BufferCap(transformConfig.CaptureConfig.BufferCap),
   445  			transformConfig.CaptureConfig.Passthrough)
   446  		// Register the capture
   447  		captures[name] = captureLogger
   448  		// Pass it upstream to be logged to
   449  		return captureLogger, captures, nil
   450  	case Filter:
   451  		if transformConfig.FilterConfig == nil {
   452  			return nil, nil, fmt.Errorf("filter transform specified but no FilterConfig provided")
   453  		}
   454  		predicate, err := BuildFilterPredicate(transformConfig.FilterConfig)
   455  		if err != nil {
   456  			return nil, captures, fmt.Errorf("could not build filter predicate: '%s'", err)
   457  		}
   458  		return loggers.FilterLogger(outputLogger, predicate), captures, nil
   459  	case Sort:
   460  		if transformConfig.SortConfig == nil {
   461  			return nil, nil, fmt.Errorf("sort transform specified but no SortConfig provided")
   462  		}
   463  		return loggers.SortLogger(outputLogger, transformConfig.SortConfig.Keys...), captures, nil
   464  	case Vectorise:
   465  		return loggers.VectorValuedLogger(outputLogger), captures, nil
   466  	default:
   467  		return nil, captures, fmt.Errorf("could not build logger for transform: '%s'", transformConfig.TransformType)
   468  	}
   469  }
   470  
   471  func signalPassthroughLogger(ifSignalLogger log.Logger, otherwiseLogger log.Logger) log.Logger {
   472  	return log.LoggerFunc(func(keyvals ...interface{}) error {
   473  		if structure.Signal(keyvals) != "" {
   474  			return ifSignalLogger.Log(keyvals...)
   475  		}
   476  		return otherwiseLogger.Log(keyvals...)
   477  	})
   478  }