| OLD | NEW |
| 1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 package main | 5 package main |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "bytes" | 8 "bytes" |
| 9 "flag" | 9 "flag" |
| 10 "fmt" | 10 "fmt" |
| 11 "net/http" | 11 "net/http" |
| 12 "os" | 12 "os" |
| 13 "os/signal" | 13 "os/signal" |
| 14 "runtime/pprof" | |
| 15 "sort" | 14 "sort" |
| 16 "strings" | 15 "strings" |
| 17 "time" | 16 "time" |
| 18 | 17 |
| 19 "github.com/maruel/subcommands" | 18 "github.com/maruel/subcommands" |
| 20 "golang.org/x/net/context" | 19 "golang.org/x/net/context" |
| 21 | 20 |
| 22 "github.com/luci/luci-go/client/authcli" | 21 "github.com/luci/luci-go/client/authcli" |
| 23 "github.com/luci/luci-go/common/auth" | 22 "github.com/luci/luci-go/common/auth" |
| 24 "github.com/luci/luci-go/common/cli" | 23 "github.com/luci/luci-go/common/cli" |
| 25 "github.com/luci/luci-go/common/clock/clockflag" | 24 "github.com/luci/luci-go/common/clock/clockflag" |
| 26 "github.com/luci/luci-go/common/config" | 25 "github.com/luci/luci-go/common/config" |
| 27 "github.com/luci/luci-go/common/errors" | 26 "github.com/luci/luci-go/common/errors" |
| 28 "github.com/luci/luci-go/common/flag/multiflag" | 27 "github.com/luci/luci-go/common/flag/multiflag" |
| 29 log "github.com/luci/luci-go/common/logging" | 28 log "github.com/luci/luci-go/common/logging" |
| 30 "github.com/luci/luci-go/common/logging/gologger" | 29 "github.com/luci/luci-go/common/logging/gologger" |
| 31 "github.com/luci/luci-go/common/runtime/paniccatcher" | 30 "github.com/luci/luci-go/common/runtime/paniccatcher" |
| 31 "github.com/luci/luci-go/common/runtime/profiling" |
| 32 grpcLogging "github.com/luci/luci-go/grpc/logging" | 32 grpcLogging "github.com/luci/luci-go/grpc/logging" |
| 33 "github.com/luci/luci-go/logdog/client/butler" | 33 "github.com/luci/luci-go/logdog/client/butler" |
| 34 "github.com/luci/luci-go/logdog/client/butler/output" | 34 "github.com/luci/luci-go/logdog/client/butler/output" |
| 35 "github.com/luci/luci-go/logdog/common/types" | 35 "github.com/luci/luci-go/logdog/common/types" |
| 36 ) | 36 ) |
| 37 | 37 |
| 38 const ( | 38 const ( |
| 39 // flagErrorReturnCode is returned when there is an error with the Butle
r's | 39 // flagErrorReturnCode is returned when there is an error with the Butle
r's |
| 40 // command-line configuration. | 40 // command-line configuration. |
| 41 configErrorReturnCode = 2 | 41 configErrorReturnCode = 2 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 59 project config.ProjectName | 59 project config.ProjectName |
| 60 prefix types.StreamName | 60 prefix types.StreamName |
| 61 outputWorkers int | 61 outputWorkers int |
| 62 outputConfig outputConfigFlag | 62 outputConfig outputConfigFlag |
| 63 | 63 |
| 64 authFlags authcli.Flags | 64 authFlags authcli.Flags |
| 65 | 65 |
| 66 maxBufferAge clockflag.Duration | 66 maxBufferAge clockflag.Duration |
| 67 noBufferLogs bool | 67 noBufferLogs bool |
| 68 | 68 |
| 69 » cpuProfile string | 69 » prof profiling.Profiler |
| 70 | 70 |
| 71 client *http.Client | 71 client *http.Client |
| 72 | 72 |
| 73 // ncCtx is a context that will not be cancelled when cancelFunc is call
ed. | 73 // ncCtx is a context that will not be cancelled when cancelFunc is call
ed. |
| 74 ncCtx context.Context | 74 ncCtx context.Context |
| 75 cancelFunc func() | 75 cancelFunc func() |
| 76 } | 76 } |
| 77 | 77 |
| 78 func (a *application) addFlags(fs *flag.FlagSet) { | 78 func (a *application) addFlags(fs *flag.FlagSet) { |
| 79 a.outputConfig.Output = os.Stdout | 79 a.outputConfig.Output = os.Stdout |
| 80 a.outputConfig.Description = "Select and configure message output adapte
r." | 80 a.outputConfig.Description = "Select and configure message output adapte
r." |
| 81 a.outputConfig.Options = []multiflag.Option{ | 81 a.outputConfig.Options = []multiflag.Option{ |
| 82 multiflag.HelpOption(&a.outputConfig.MultiFlag), | 82 multiflag.HelpOption(&a.outputConfig.MultiFlag), |
| 83 } | 83 } |
| 84 | 84 |
| 85 // Add registered conditional (build tag) options. | 85 // Add registered conditional (build tag) options. |
| 86 for _, f := range getOutputFactories() { | 86 for _, f := range getOutputFactories() { |
| 87 a.outputConfig.AddFactory(f) | 87 a.outputConfig.AddFactory(f) |
| 88 } | 88 } |
| 89 | 89 |
| 90 a.maxBufferAge = clockflag.Duration(butler.DefaultMaxBufferAge) | 90 a.maxBufferAge = clockflag.Duration(butler.DefaultMaxBufferAge) |
| 91 | 91 |
| 92 fs.Var(&a.project, "project", | 92 fs.Var(&a.project, "project", |
| 93 "The log prefix's project name (required).") | 93 "The log prefix's project name (required).") |
| 94 fs.Var(&a.prefix, "prefix", | 94 fs.Var(&a.prefix, "prefix", |
| 95 "Prefix to apply to all stream names.") | 95 "Prefix to apply to all stream names.") |
| 96 fs.Var(&a.outputConfig, "output", | 96 fs.Var(&a.outputConfig, "output", |
| 97 "The output name and configuration. Specify 'help' for more info
rmation.") | 97 "The output name and configuration. Specify 'help' for more info
rmation.") |
| 98 fs.StringVar(&a.cpuProfile, | |
| 99 "cpuprofile", "", "If specified, enables CPU profiling and profi
les to the specified path.") | |
| 100 fs.IntVar(&a.outputWorkers, "output-workers", butler.DefaultOutputWorker
s, | 98 fs.IntVar(&a.outputWorkers, "output-workers", butler.DefaultOutputWorker
s, |
| 101 "The maximum number of parallel output dispatches.") | 99 "The maximum number of parallel output dispatches.") |
| 102 fs.Var(&a.maxBufferAge, "output-max-buffer-age", | 100 fs.Var(&a.maxBufferAge, "output-max-buffer-age", |
| 103 "Send buffered messages if they've been held for longer than thi
s period.") | 101 "Send buffered messages if they've been held for longer than thi
s period.") |
| 104 fs.BoolVar(&a.noBufferLogs, "output-no-buffer", false, | 102 fs.BoolVar(&a.noBufferLogs, "output-no-buffer", false, |
| 105 "If true, dispatch logs immediately. Setting this flag simplifie
s output at the expense "+ | 103 "If true, dispatch logs immediately. Setting this flag simplifie
s output at the expense "+ |
| 106 "of wire-format efficiency.") | 104 "of wire-format efficiency.") |
| 107 } | 105 } |
| 108 | 106 |
| 109 func (a *application) authenticator(ctx context.Context) (*auth.Authenticator, e
rror) { | 107 func (a *application) authenticator(ctx context.Context) (*auth.Authenticator, e
rror) { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 120 if factory == nil { | 118 if factory == nil { |
| 121 return nil, errors.New("main: No output is configured") | 119 return nil, errors.New("main: No output is configured") |
| 122 } | 120 } |
| 123 return factory, nil | 121 return factory, nil |
| 124 } | 122 } |
| 125 | 123 |
| 126 // runWithButler is an execution harness that adds application-level management | 124 // runWithButler is an execution harness that adds application-level management |
| 127 // to a Butler run. | 125 // to a Butler run. |
| 128 func (a *application) runWithButler(out output.Output, runFunc func(*butler.Butl
er) error) error { | 126 func (a *application) runWithButler(out output.Output, runFunc func(*butler.Butl
er) error) error { |
| 129 | 127 |
| 130 » // Enable CPU profiling if specified | 128 » // Start our Profiler. |
| 131 » if a.cpuProfile != "" { | 129 » a.prof.Logger = log.Get(a) |
| 132 » » f, err := os.Create(a.cpuProfile) | 130 » if err := a.prof.Start(); err != nil { |
| 133 » » if err != nil { | 131 » » return fmt.Errorf("failed to start Profiler: %v", err) |
| 134 » » » return fmt.Errorf("failed to create CPU profile output:
%v", err) | |
| 135 » » } | |
| 136 » » pprof.StartCPUProfile(f) | |
| 137 » » defer pprof.StopCPUProfile() | |
| 138 } | 132 } |
| 133 defer a.prof.Stop() |
| 139 | 134 |
| 140 // Instantiate our Butler. | 135 // Instantiate our Butler. |
| 141 butlerOpts := butler.Config{ | 136 butlerOpts := butler.Config{ |
| 142 Project: a.project, | 137 Project: a.project, |
| 143 Prefix: a.prefix, | 138 Prefix: a.prefix, |
| 144 MaxBufferAge: time.Duration(a.maxBufferAge), | 139 MaxBufferAge: time.Duration(a.maxBufferAge), |
| 145 BufferLogs: !a.noBufferLogs, | 140 BufferLogs: !a.noBufferLogs, |
| 146 Output: out, | 141 Output: out, |
| 147 OutputWorkers: a.outputWorkers, | 142 OutputWorkers: a.outputWorkers, |
| 148 TeeStdout: os.Stdout, | 143 TeeStdout: os.Stdout, |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 245 }, | 240 }, |
| 246 } | 241 } |
| 247 // Install logging configuration flags. | 242 // Install logging configuration flags. |
| 248 flags := flag.NewFlagSet("flags", flag.ExitOnError) | 243 flags := flag.NewFlagSet("flags", flag.ExitOnError) |
| 249 logConfig := log.Config{ | 244 logConfig := log.Config{ |
| 250 Level: log.Warning, | 245 Level: log.Warning, |
| 251 } | 246 } |
| 252 logConfig.AddFlags(flags) | 247 logConfig.AddFlags(flags) |
| 253 a.addFlags(flags) | 248 a.addFlags(flags) |
| 254 a.authFlags.Register(flags, authOptions) | 249 a.authFlags.Register(flags, authOptions) |
| 250 a.prof.AddFlags(flags) |
| 255 | 251 |
| 256 // Parse the top-level flag set. | 252 // Parse the top-level flag set. |
| 257 if err := flags.Parse(argv); err != nil { | 253 if err := flags.Parse(argv); err != nil { |
| 258 log.WithError(err).Errorf(a, "Failed to parse command-line.") | 254 log.WithError(err).Errorf(a, "Failed to parse command-line.") |
| 259 return configErrorReturnCode | 255 return configErrorReturnCode |
| 260 } | 256 } |
| 261 | 257 |
| 262 a.Context = logConfig.Set(a.Context) | 258 a.Context = logConfig.Set(a.Context) |
| 263 | 259 |
| 264 // Install a global gRPC logger adapter. This routes gRPC log messages t
hat | 260 // Install a global gRPC logger adapter. This routes gRPC log messages t
hat |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 339 | 335 |
| 340 paniccatcher.Do(func() { | 336 paniccatcher.Do(func() { |
| 341 rc = mainImpl(ctx, os.Args[1:]) | 337 rc = mainImpl(ctx, os.Args[1:]) |
| 342 }, func(p *paniccatcher.Panic) { | 338 }, func(p *paniccatcher.Panic) { |
| 343 log.Fields{ | 339 log.Fields{ |
| 344 "panic.error": p.Reason, | 340 "panic.error": p.Reason, |
| 345 }.Errorf(ctx, "Panic caught in main:\n%s", p.Stack) | 341 }.Errorf(ctx, "Panic caught in main:\n%s", p.Stack) |
| 346 rc = runtimeErrorReturnCode | 342 rc = runtimeErrorReturnCode |
| 347 }) | 343 }) |
| 348 } | 344 } |
| OLD | NEW |