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

     1  package presets
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"strconv"
     7  
     8  	"github.com/hyperledger/burrow/logging/logconfig"
     9  	"github.com/hyperledger/burrow/logging/loggers"
    10  	"github.com/hyperledger/burrow/logging/structure"
    11  )
    12  
    13  // Function to generate part of a tree of Sinks (e.g. append a single child node, or an entire subtree).
    14  // Each Instruction takes a (sub)root, which it may modify, and appends further child sinks below that root.
    15  // Returning the new subroot from which to apply any further Presets.
    16  // When chained together in a pre-order instructions can be composed to form an entire Sink tree.
    17  type Instruction struct {
    18  	name  string
    19  	desc  string
    20  	nargs int
    21  	// The builder for the Instruction is a function that may modify the stack or ops string. Typically
    22  	// by mutating the sink at the top of the stack and may move the cursor or by pushing child sinks
    23  	// to the stack. The builder may also return a modified ops slice whereby it may insert Instruction calls
    24  	// acting as a macro or consume ops as arguments.
    25  	builder func(stack []*logconfig.SinkConfig, args []string) ([]*logconfig.SinkConfig, error)
    26  }
    27  
    28  func (i Instruction) Name() string {
    29  	return i.name
    30  }
    31  
    32  func (i Instruction) Description() string {
    33  	return i.desc
    34  }
    35  
    36  const (
    37  	Top        = "top"
    38  	Up         = "up"
    39  	Down       = "down"
    40  	Info       = "info"
    41  	Trace      = "trace"
    42  	Minimal    = "minimal"
    43  	Opcodes    = "opcodes"
    44  	IncludeAny = "include-any"
    45  	Stderr     = "stderr"
    46  	Stdout     = "stdout"
    47  	Terminal   = "terminal"
    48  	JSON       = "json"
    49  	Capture    = "capture"
    50  	File       = "file"
    51  )
    52  
    53  var instructions = []Instruction{
    54  	{
    55  		name: Top,
    56  		desc: "Ascend the sink tree to the root and insert a new child logger",
    57  		builder: func(stack []*logconfig.SinkConfig, args []string) ([]*logconfig.SinkConfig, error) {
    58  			return push(stack[:1], logconfig.Sink()), nil
    59  		},
    60  	},
    61  	{
    62  		name: Up,
    63  		desc: "Ascend the sink tree by travelling up the stack to the previous sink recorded on the stack",
    64  		builder: func(stack []*logconfig.SinkConfig, args []string) ([]*logconfig.SinkConfig, error) {
    65  			return pop(stack), nil
    66  		},
    67  	},
    68  	{
    69  		name: Down,
    70  		desc: "Descend the sink tree by inserting a sink as a child to the current sink and adding it to the stack",
    71  		builder: func(stack []*logconfig.SinkConfig, args []string) ([]*logconfig.SinkConfig, error) {
    72  			return push(stack, logconfig.Sink()), nil
    73  		},
    74  	},
    75  	{
    76  		name: Minimal,
    77  		desc: "A generally less chatty log output, follow with output options",
    78  		builder: func(stack []*logconfig.SinkConfig, args []string) ([]*logconfig.SinkConfig, error) {
    79  			return push(stack,
    80  				logconfig.Sink().SetTransform(logconfig.PruneTransform(structure.TraceKey, structure.RunId)),
    81  				logconfig.Sink().SetTransform(logconfig.FilterTransform(logconfig.IncludeWhenAllMatch,
    82  					structure.ChannelKey, structure.InfoChannelName)),
    83  				logconfig.Sink().SetTransform(logconfig.FilterTransform(logconfig.ExcludeWhenAnyMatches,
    84  					structure.ComponentKey, "Tendermint",
    85  					"module", "p2p",
    86  					"module", "mempool"))), nil
    87  		},
    88  	},
    89  	{
    90  		name: Opcodes,
    91  		desc: "Capture opcodes exclusively (when VMOptions includes DebugOpccodes in main config)",
    92  		builder: func(stack []*logconfig.SinkConfig, args []string) ([]*logconfig.SinkConfig, error) {
    93  			return push(stack,
    94  				logconfig.Sink().
    95  					SetTransform(logconfig.FilterTransform(logconfig.IncludeWhenAllMatch, "tag", "DebugOpcodes")).
    96  					SetOutput(logconfig.StdoutOutput().SetFormat("{{.message}}"))), nil
    97  		},
    98  	},
    99  	{
   100  		name: IncludeAny,
   101  		desc: "Establish an 'include when any predicate matches' filter transform at this this sink",
   102  		builder: func(stack []*logconfig.SinkConfig, args []string) ([]*logconfig.SinkConfig, error) {
   103  			sink := peek(stack)
   104  			ensureFilter(sink)
   105  			sink.Transform.FilterConfig.FilterMode = logconfig.IncludeWhenAnyMatches
   106  			return stack, nil
   107  		},
   108  	},
   109  	{
   110  		name: Info,
   111  		desc: "Add a filter predicate to match the Info logging channel",
   112  		builder: func(stack []*logconfig.SinkConfig, args []string) ([]*logconfig.SinkConfig, error) {
   113  			sink := peek(stack)
   114  			ensureFilter(sink)
   115  			sink.Transform.FilterConfig.AddPredicate(structure.ChannelKey, structure.InfoChannelName)
   116  			return stack, nil
   117  		},
   118  	},
   119  	{
   120  		name: Trace,
   121  		desc: "Add a filter predicate to match the Info logging channel",
   122  		builder: func(stack []*logconfig.SinkConfig, args []string) ([]*logconfig.SinkConfig, error) {
   123  			sink := peek(stack)
   124  			ensureFilter(sink)
   125  			sink.Transform.FilterConfig.AddPredicate(structure.ChannelKey, structure.InfoChannelName)
   126  			return stack, nil
   127  		},
   128  	},
   129  	{
   130  		name: Stdout,
   131  		desc: "Use Stdout output for this sink",
   132  		builder: func(stack []*logconfig.SinkConfig, args []string) ([]*logconfig.SinkConfig, error) {
   133  			sink := peek(stack)
   134  			ensureOutput(sink)
   135  			sink.Output.OutputType = logconfig.Stdout
   136  			return stack, nil
   137  		},
   138  	},
   139  	{
   140  		name: Stderr,
   141  		desc: "Use Stderr output for this sink",
   142  		builder: func(stack []*logconfig.SinkConfig, args []string) ([]*logconfig.SinkConfig, error) {
   143  			sink := peek(stack)
   144  			ensureOutput(sink)
   145  			sink.Output.OutputType = logconfig.Stderr
   146  			return stack, nil
   147  		},
   148  	},
   149  	{
   150  		name: Terminal,
   151  		desc: "Use the the terminal output format for this sink",
   152  		builder: func(stack []*logconfig.SinkConfig, args []string) ([]*logconfig.SinkConfig, error) {
   153  			sink := peek(stack)
   154  			ensureOutput(sink)
   155  			sink.Output.Format = loggers.TerminalFormat
   156  			return stack, nil
   157  		},
   158  	},
   159  	{
   160  		name: JSON,
   161  		desc: "Use the the terminal output format for this sink",
   162  		builder: func(stack []*logconfig.SinkConfig, args []string) ([]*logconfig.SinkConfig, error) {
   163  			sink := peek(stack)
   164  			ensureOutput(sink)
   165  			sink.Output.Format = loggers.JSONFormat
   166  			return stack, nil
   167  		},
   168  	},
   169  	{
   170  		name:  Capture,
   171  		desc:  "Insert a capture sink that will only flush on the Sync signal (on shutdown or via SIGHUP)",
   172  		nargs: 2,
   173  		builder: func(stack []*logconfig.SinkConfig, args []string) ([]*logconfig.SinkConfig, error) {
   174  			name := args[0]
   175  			bufferCap, err := strconv.ParseInt(args[1], 10, 32)
   176  			if err != nil {
   177  				return nil, fmt.Errorf("could not parse int32 from capture bufferCap argument '%s': %v", args[1],
   178  					err)
   179  			}
   180  			return push(stack, logconfig.Sink().SetTransform(logconfig.CaptureTransform(name, int(bufferCap), false))), nil
   181  		},
   182  	},
   183  	{
   184  		name:  File,
   185  		desc:  "Save logs to file with single argument file path",
   186  		nargs: 1,
   187  		builder: func(stack []*logconfig.SinkConfig, args []string) ([]*logconfig.SinkConfig, error) {
   188  			sink := peek(stack)
   189  			ensureOutput(sink)
   190  			sink.Output.OutputType = logconfig.File
   191  			sink.Output.FileConfig = &logconfig.FileConfig{
   192  				Path: args[0],
   193  			}
   194  			return stack, nil
   195  		},
   196  	},
   197  }
   198  
   199  var instructionsMap map[string]Instruction
   200  
   201  func init() {
   202  	instructionsMap = make(map[string]Instruction, len(instructions))
   203  	for _, p := range instructions {
   204  		instructionsMap[p.name] = p
   205  	}
   206  }
   207  
   208  func Instructons() []Instruction {
   209  	ins := make([]Instruction, len(instructions))
   210  	copy(ins, instructions)
   211  	return ins
   212  }
   213  
   214  func Describe(name string) string {
   215  	preset, ok := instructionsMap[name]
   216  	if !ok {
   217  		return fmt.Sprintf("No logging preset named '%s'", name)
   218  	}
   219  	return preset.desc
   220  }
   221  
   222  func BuildSinkConfig(ops ...string) (*logconfig.SinkConfig, error) {
   223  	stack := []*logconfig.SinkConfig{logconfig.Sink()}
   224  	var err error
   225  	pos := 0
   226  	for len(ops) > 0 {
   227  		// Keep applying instructions until their are no ops left
   228  		instruction, ok := instructionsMap[ops[0]]
   229  		if !ok {
   230  			return nil, fmt.Errorf("could not find logging preset '%s'", ops[0])
   231  		}
   232  		// pop instruction name
   233  		ops = ops[1:]
   234  		if len(ops) < instruction.nargs {
   235  			return nil, fmt.Errorf("did not have enough arguments for instruction %s at position %v "+
   236  				"(requires %v arguments)", instruction.name, pos, instruction.nargs)
   237  		}
   238  		stack, err = instruction.builder(stack, ops[:instruction.nargs])
   239  		if err != nil {
   240  			return nil, err
   241  		}
   242  		// pop instruction args
   243  		ops = ops[instruction.nargs:]
   244  		pos++
   245  	}
   246  	return stack[0], nil
   247  }
   248  
   249  func ensureFilter(sinkConfig *logconfig.SinkConfig) {
   250  	if sinkConfig.Transform == nil {
   251  		sinkConfig.Transform = &logconfig.TransformConfig{}
   252  	}
   253  	if sinkConfig.Transform.FilterConfig == nil {
   254  		sinkConfig.Transform.FilterConfig = &logconfig.FilterConfig{}
   255  	}
   256  	sinkConfig.Transform.TransformType = logconfig.Filter
   257  }
   258  
   259  func ensureOutput(sinkConfig *logconfig.SinkConfig) {
   260  	if sinkConfig.Output == nil {
   261  		sinkConfig.Output = &logconfig.OutputConfig{}
   262  	}
   263  }
   264  
   265  // Push a path sequence of sinks onto the stack
   266  func push(stack []*logconfig.SinkConfig, sinkConfigs ...*logconfig.SinkConfig) []*logconfig.SinkConfig {
   267  	for _, sinkConfig := range sinkConfigs {
   268  		peek(stack).AddSinks(sinkConfig)
   269  		stack = append(stack, sinkConfig)
   270  	}
   271  	return stack
   272  }
   273  
   274  func pop(stack []*logconfig.SinkConfig) []*logconfig.SinkConfig {
   275  	return stack[:len(stack)-1]
   276  }
   277  
   278  func peek(stack []*logconfig.SinkConfig) *logconfig.SinkConfig {
   279  	return stack[len(stack)-1]
   280  }