github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/cmd/loki-canary/main.go (about)

     1  package main
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"os"
     7  	"strconv"
     8  	"sync"
     9  	"syscall"
    10  	"time"
    11  
    12  	"crypto/tls"
    13  	"net/http"
    14  	"os/signal"
    15  
    16  	"github.com/prometheus/client_golang/prometheus/promhttp"
    17  	"github.com/prometheus/common/config"
    18  	"github.com/prometheus/common/version"
    19  
    20  	"github.com/grafana/loki/pkg/canary/comparator"
    21  	"github.com/grafana/loki/pkg/canary/reader"
    22  	"github.com/grafana/loki/pkg/canary/writer"
    23  	_ "github.com/grafana/loki/pkg/util/build"
    24  )
    25  
    26  type canary struct {
    27  	lock sync.Mutex
    28  
    29  	writer     *writer.Writer
    30  	reader     *reader.Reader
    31  	comparator *comparator.Comparator
    32  }
    33  
    34  func main() {
    35  
    36  	lName := flag.String("labelname", "name", "The label name for this instance of loki-canary to use in the log selector")
    37  	lVal := flag.String("labelvalue", "loki-canary", "The unique label value for this instance of loki-canary to use in the log selector")
    38  	sName := flag.String("streamname", "stream", "The stream name for this instance of loki-canary to use in the log selector")
    39  	sValue := flag.String("streamvalue", "stdout", "The unique stream value for this instance of loki-canary to use in the log selector")
    40  	port := flag.Int("port", 3500, "Port which loki-canary should expose metrics")
    41  	addr := flag.String("addr", "", "The Loki server URL:Port, e.g. loki:3100")
    42  	useTLS := flag.Bool("tls", false, "Does the loki connection use TLS?")
    43  	certFile := flag.String("cert-file", "", "Client PEM encoded X.509 certificate for optional use with TLS connection to Loki")
    44  	keyFile := flag.String("key-file", "", "Client PEM encoded X.509 key for optional use with TLS connection to Loki")
    45  	caFile := flag.String("ca-file", "", "Client certificate authority for optional use with TLS connection to Loki")
    46  	user := flag.String("user", "", "Loki username.")
    47  	pass := flag.String("pass", "", "Loki password.")
    48  	tenantID := flag.String("tenant-id", "", "Tenant ID to be set in X-Scope-OrgID header.")
    49  	queryTimeout := flag.Duration("query-timeout", 10*time.Second, "How long to wait for a query response from Loki")
    50  
    51  	interval := flag.Duration("interval", 1000*time.Millisecond, "Duration between log entries")
    52  	outOfOrderPercentage := flag.Int("out-of-order-percentage", 0, "Percentage (0-100) of log entries that should be sent out of order.")
    53  	outOfOrderMin := flag.Duration("out-of-order-min", 30*time.Second, "Minimum amount of time to go back for out of order entries (in seconds).")
    54  	outOfOrderMax := flag.Duration("out-of-order-max", 60*time.Second, "Maximum amount of time to go back for out of order entries (in seconds).")
    55  
    56  	size := flag.Int("size", 100, "Size in bytes of each log line")
    57  	wait := flag.Duration("wait", 60*time.Second, "Duration to wait for log entries on websocket before querying loki for them")
    58  	maxWait := flag.Duration("max-wait", 5*time.Minute, "Duration to keep querying Loki for missing websocket entries before reporting them missing")
    59  	pruneInterval := flag.Duration("pruneinterval", 60*time.Second, "Frequency to check sent vs received logs, "+
    60  		"also the frequency which queries for missing logs will be dispatched to loki")
    61  	buckets := flag.Int("buckets", 10, "Number of buckets in the response_latency histogram")
    62  
    63  	metricTestInterval := flag.Duration("metric-test-interval", 1*time.Hour, "The interval the metric test query should be run")
    64  	metricTestQueryRange := flag.Duration("metric-test-range", 24*time.Hour, "The range value [24h] used in the metric test instant-query."+
    65  		" Note: this value is truncated to the running time of the canary until this value is reached")
    66  
    67  	spotCheckInterval := flag.Duration("spot-check-interval", 15*time.Minute, "Interval that a single result will be kept from sent entries and spot-checked against Loki, "+
    68  		"e.g. 15min default one entry every 15 min will be saved and then queried again every 15min until spot-check-max is reached")
    69  	spotCheckMax := flag.Duration("spot-check-max", 4*time.Hour, "How far back to check a spot check entry before dropping it")
    70  	spotCheckQueryRate := flag.Duration("spot-check-query-rate", 1*time.Minute, "Interval that the canary will query Loki for the current list of all spot check entries")
    71  	spotCheckWait := flag.Duration("spot-check-initial-wait", 10*time.Second, "How long should the spot check query wait before starting to check for entries")
    72  
    73  	printVersion := flag.Bool("version", false, "Print this builds version information")
    74  
    75  	flag.Parse()
    76  
    77  	if *printVersion {
    78  		fmt.Println(version.Print("loki-canary"))
    79  		os.Exit(0)
    80  	}
    81  
    82  	if *addr == "" {
    83  		_, _ = fmt.Fprintf(os.Stderr, "Must specify a Loki address with -addr\n")
    84  		os.Exit(1)
    85  	}
    86  
    87  	if *outOfOrderPercentage < 0 || *outOfOrderPercentage > 100 {
    88  		_, _ = fmt.Fprintf(os.Stderr, "Out of order percentage must be between 0 and 100\n")
    89  		os.Exit(1)
    90  	}
    91  
    92  	var tlsConfig *tls.Config
    93  	tc := config.TLSConfig{}
    94  	if *certFile != "" || *keyFile != "" || *caFile != "" {
    95  		if !*useTLS {
    96  			_, _ = fmt.Fprintf(os.Stderr, "Must set --tls when specifying client certs\n")
    97  			os.Exit(1)
    98  		}
    99  		tc.CAFile = *caFile
   100  		tc.CertFile = *certFile
   101  		tc.KeyFile = *keyFile
   102  		tc.InsecureSkipVerify = false
   103  
   104  		var err error
   105  		tlsConfig, err = config.NewTLSConfig(&tc)
   106  		if err != nil {
   107  			_, _ = fmt.Fprintf(os.Stderr, "TLS configuration error: %s\n", err.Error())
   108  			os.Exit(1)
   109  		}
   110  	}
   111  
   112  	sentChan := make(chan time.Time)
   113  	receivedChan := make(chan time.Time)
   114  
   115  	c := &canary{}
   116  	startCanary := func() {
   117  		c.stop()
   118  
   119  		c.lock.Lock()
   120  		defer c.lock.Unlock()
   121  
   122  		var err error
   123  		c.writer = writer.NewWriter(os.Stdout, sentChan, *interval, *outOfOrderMin, *outOfOrderMax, *outOfOrderPercentage, *size)
   124  		c.reader, err = reader.NewReader(os.Stderr, receivedChan, *useTLS, tlsConfig, *caFile, *addr, *user, *pass, *tenantID, *queryTimeout, *lName, *lVal, *sName, *sValue, *interval)
   125  		if err != nil {
   126  			_, _ = fmt.Fprintf(os.Stderr, "Unable to create reader for Loki querier, check config: %s", err)
   127  			os.Exit(1)
   128  		}
   129  		c.comparator = comparator.NewComparator(os.Stderr, *wait, *maxWait, *pruneInterval, *spotCheckInterval, *spotCheckMax, *spotCheckQueryRate, *spotCheckWait, *metricTestInterval, *metricTestQueryRange, *interval, *buckets, sentChan, receivedChan, c.reader, true)
   130  	}
   131  
   132  	startCanary()
   133  
   134  	http.HandleFunc("/resume", func(_ http.ResponseWriter, _ *http.Request) {
   135  		_, _ = fmt.Fprintf(os.Stderr, "restarting\n")
   136  		startCanary()
   137  	})
   138  	http.HandleFunc("/suspend", func(_ http.ResponseWriter, _ *http.Request) {
   139  		_, _ = fmt.Fprintf(os.Stderr, "suspending\n")
   140  		c.stop()
   141  	})
   142  	http.Handle("/metrics", promhttp.Handler())
   143  	go func() {
   144  		err := http.ListenAndServe(":"+strconv.Itoa(*port), nil)
   145  		if err != nil {
   146  			panic(err)
   147  		}
   148  	}()
   149  
   150  	terminate := make(chan os.Signal, 1)
   151  	signal.Notify(terminate, syscall.SIGTERM, os.Interrupt)
   152  
   153  	for range terminate {
   154  		_, _ = fmt.Fprintf(os.Stderr, "shutting down\n")
   155  		c.stop()
   156  		return
   157  	}
   158  }
   159  
   160  func (c *canary) stop() {
   161  	c.lock.Lock()
   162  	defer c.lock.Unlock()
   163  
   164  	if c.writer == nil || c.reader == nil || c.comparator == nil {
   165  		return
   166  	}
   167  
   168  	c.writer.Stop()
   169  	c.reader.Stop()
   170  	c.comparator.Stop()
   171  
   172  	c.writer = nil
   173  	c.reader = nil
   174  	c.comparator = nil
   175  }