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 }