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 }