package metrics import ( "encoding/json" "fmt" "io/ioutil" "net/http" "regexp" "strconv" "strings" "time" log "github.com/sirupsen/logrus" ) type LokiMetricConfig struct { Url string BotUserAgents *[]regexp.Regexp Enabled bool } // Checks if we should send a metric ping to Loki based on the served path. func (c *LokiMetricConfig) ShouldSendMetrics(path, userAgent string) bool { if !strings.HasSuffix(path, ".html") || !c.Enabled { return false } // Filter out bots for _, pattern := range *c.BotUserAgents { if pattern.MatchString(userAgent) { return false } } return true } func (c *LokiMetricConfig) SendMetricPing(domain, path, referrer string) { msg := fmt.Sprintf("path=\"%s\" referrer=\"%s\"", path, referrer) data := map[string]interface{}{ "streams": []map[string]interface{}{ { "stream": map[string]string{ // Labels "service": "rio", "domain": domain, "type": "metric", }, "values": [][]interface{}{ { strconv.Itoa(int(time.Now().UnixNano())), msg, }, }, }, }, } jsonData, err := json.Marshal(data) if err != nil { log.Errorf("Failed to send metric ping to Loki: %v", err) return } log.Debugf("Sending payload %s", string(jsonData)) // Send the ping to the Loki server go func() { res, err := http.Post( c.Url, "application/json", strings.NewReader(string(jsonData)), ) if err != nil { log.Errorf("Failed to send payload to Loki: %v", err) return } defer res.Body.Close() if res.StatusCode != 204 { log.Errorf("Loki returned non-204 status code %d", res.StatusCode) body, err := ioutil.ReadAll(res.Body) if err != nil { log.Warnf("Failed to read body. No more specific error message") return } log.Errorf("-> %s", body) } }() } // Reads a JSON array of bot user agents from disk and parses them // into regular expressions. func ReadBotPatterns(file string) ([]regexp.Regexp, error) { content, err := ioutil.ReadFile(file) if err != nil { log.Warnf("Failed to read bot metrics file: %v", err) return []regexp.Regexp{}, err } var payload []string err = json.Unmarshal(content, &payload) if err != nil { log.Warnf("Failed to unmarshal file: %v", err) return []regexp.Regexp{}, err } patterns := make([]regexp.Regexp, 0) for _, v := range payload { patterns = append( patterns, *regexp.MustCompile(v), ) } return patterns, nil }