github.com/thanos-io/thanos@v0.32.5/cmd/thanos/main.go (about) 1 // Copyright (c) The Thanos Authors. 2 // Licensed under the Apache License 2.0. 3 4 package main 5 6 import ( 7 "context" 8 "fmt" 9 "io" 10 "os" 11 "os/signal" 12 "path/filepath" 13 "regexp" 14 "runtime" 15 "runtime/debug" 16 "syscall" 17 18 "github.com/go-kit/log" 19 "github.com/go-kit/log/level" 20 "github.com/oklog/run" 21 "github.com/opentracing/opentracing-go" 22 "github.com/pkg/errors" 23 "github.com/prometheus/client_golang/prometheus" 24 "github.com/prometheus/client_golang/prometheus/collectors" 25 "github.com/prometheus/common/version" 26 "go.uber.org/automaxprocs/maxprocs" 27 "gopkg.in/alecthomas/kingpin.v2" 28 29 "github.com/thanos-io/thanos/pkg/extkingpin" 30 "github.com/thanos-io/thanos/pkg/logging" 31 "github.com/thanos-io/thanos/pkg/tracing/client" 32 ) 33 34 func main() { 35 // We use mmaped resources in most of the components so hardcode PanicOnFault to true. This allows us to recover (if we can e.g if queries 36 // are temporarily accessing unmapped memory). 37 debug.SetPanicOnFault(true) 38 39 if os.Getenv("DEBUG") != "" { 40 runtime.SetMutexProfileFraction(10) 41 runtime.SetBlockProfileRate(10) 42 } 43 44 app := extkingpin.NewApp(kingpin.New(filepath.Base(os.Args[0]), "A block storage based long-term storage for Prometheus.").Version(version.Print("thanos"))) 45 debugName := app.Flag("debug.name", "Name to add as prefix to log lines.").Hidden().String() 46 logLevel := app.Flag("log.level", "Log filtering level."). 47 Default("info").Enum("error", "warn", "info", "debug") 48 logFormat := app.Flag("log.format", "Log format to use. Possible options: logfmt or json."). 49 Default(logging.LogFormatLogfmt).Enum(logging.LogFormatLogfmt, logging.LogFormatJSON) 50 tracingConfig := extkingpin.RegisterCommonTracingFlags(app) 51 52 registerSidecar(app) 53 registerStore(app) 54 registerQuery(app) 55 registerRule(app) 56 registerCompact(app) 57 registerTools(app) 58 registerReceive(app) 59 registerQueryFrontend(app) 60 61 cmd, setup := app.Parse() 62 logger := logging.NewLogger(*logLevel, *logFormat, *debugName) 63 64 // Running in container with limits but with empty/wrong value of GOMAXPROCS env var could lead to throttling by cpu 65 // maxprocs will automate adjustment by using cgroups info about cpu limit if it set as value for runtime.GOMAXPROCS. 66 undo, err := maxprocs.Set(maxprocs.Logger(func(template string, args ...interface{}) { 67 level.Debug(logger).Log("msg", fmt.Sprintf(template, args)) 68 })) 69 defer undo() 70 if err != nil { 71 level.Warn(logger).Log("warn", errors.Wrapf(err, "failed to set GOMAXPROCS: %v", err)) 72 } 73 74 metrics := prometheus.NewRegistry() 75 metrics.MustRegister( 76 version.NewCollector("thanos"), 77 collectors.NewGoCollector( 78 collectors.WithGoCollectorRuntimeMetrics(collectors.GoRuntimeMetricsRule{Matcher: regexp.MustCompile("/.*")}), 79 ), 80 collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}), 81 ) 82 83 // Some packages still use default Register. Replace to have those metrics. 84 prometheus.DefaultRegisterer = metrics 85 86 var g run.Group 87 var tracer opentracing.Tracer 88 // Setup optional tracing. 89 { 90 var ( 91 ctx = context.Background() 92 closer io.Closer 93 confContentYaml []byte 94 ) 95 96 confContentYaml, err = tracingConfig.Content() 97 if err != nil { 98 level.Error(logger).Log("msg", "getting tracing config failed", "err", err) 99 os.Exit(1) 100 } 101 102 if len(confContentYaml) == 0 { 103 tracer = client.NoopTracer() 104 } else { 105 tracer, closer, err = client.NewTracer(ctx, logger, metrics, confContentYaml) 106 if err != nil { 107 fmt.Fprintln(os.Stderr, errors.Wrapf(err, "tracing failed")) 108 os.Exit(1) 109 } 110 } 111 112 // This is bad, but Prometheus does not support any other tracer injections than just global one. 113 // TODO(bplotka): Work with basictracer to handle gracefully tracker mismatches, and also with Prometheus to allow 114 // tracer injection. 115 opentracing.SetGlobalTracer(tracer) 116 117 ctx, cancel := context.WithCancel(ctx) 118 g.Add(func() error { 119 <-ctx.Done() 120 return ctx.Err() 121 }, func(error) { 122 if closer != nil { 123 if err := closer.Close(); err != nil { 124 level.Warn(logger).Log("msg", "closing tracer failed", "err", err) 125 } 126 } 127 cancel() 128 }) 129 } 130 // Create a signal channel to dispatch reload events to sub-commands. 131 reloadCh := make(chan struct{}, 1) 132 133 if err := setup(&g, logger, metrics, tracer, reloadCh, *logLevel == "debug"); err != nil { 134 // Use %+v for github.com/pkg/errors error to print with stack. 135 level.Error(logger).Log("err", fmt.Sprintf("%+v", errors.Wrapf(err, "preparing %s command failed", cmd))) 136 os.Exit(1) 137 } 138 139 // Listen for termination signals. 140 { 141 cancel := make(chan struct{}) 142 g.Add(func() error { 143 return interrupt(logger, cancel) 144 }, func(error) { 145 close(cancel) 146 }) 147 } 148 149 // Listen for reload signals. 150 { 151 cancel := make(chan struct{}) 152 g.Add(func() error { 153 return reload(logger, cancel, reloadCh) 154 }, func(error) { 155 close(cancel) 156 }) 157 } 158 159 if err := g.Run(); err != nil { 160 // Use %+v for github.com/pkg/errors error to print with stack. 161 level.Error(logger).Log("err", fmt.Sprintf("%+v", errors.Wrapf(err, "%s command failed", cmd))) 162 os.Exit(1) 163 } 164 level.Info(logger).Log("msg", "exiting") 165 } 166 167 func interrupt(logger log.Logger, cancel <-chan struct{}) error { 168 c := make(chan os.Signal, 1) 169 signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 170 select { 171 case s := <-c: 172 level.Info(logger).Log("msg", "caught signal. Exiting.", "signal", s) 173 return nil 174 case <-cancel: 175 return errors.New("canceled") 176 } 177 } 178 179 func reload(logger log.Logger, cancel <-chan struct{}, r chan<- struct{}) error { 180 c := make(chan os.Signal, 1) 181 signal.Notify(c, syscall.SIGHUP) 182 for { 183 select { 184 case s := <-c: 185 level.Info(logger).Log("msg", "caught signal. Reloading.", "signal", s) 186 select { 187 case r <- struct{}{}: 188 level.Info(logger).Log("msg", "reload dispatched.") 189 default: 190 } 191 case <-cancel: 192 return errors.New("canceled") 193 } 194 } 195 } 196 197 func getFlagsMap(flags []*kingpin.FlagModel) map[string]string { 198 flagsMap := map[string]string{} 199 200 // Exclude kingpin default flags to expose only Thanos ones. 201 boilerplateFlags := kingpin.New("", "").Version("") 202 203 for _, f := range flags { 204 if boilerplateFlags.GetFlag(f.Name) != nil { 205 continue 206 } 207 flagsMap[f.Name] = f.Value.String() 208 } 209 210 return flagsMap 211 }