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  }