Skip to content

Instantly share code, notes, and snippets.

@womchik
Created May 3, 2023 17:43
Show Gist options
  • Save womchik/910d2dc5667c9aca41d01c2f599d47c3 to your computer and use it in GitHub Desktop.
Save womchik/910d2dc5667c9aca41d01c2f599d47c3 to your computer and use it in GitHub Desktop.
package main
import (
"bytes"
"encoding/json"
"fmt"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
"io"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"time"
"unicode/utf8"
)
const (
defaultDelay = 100
defaultLimit = "100"
defaultQuery = "{namespace=~\"firecore.+\"} |~ \"PANIC\""
defaultLokiUrl = "http://localhost:3100"
fakeSend = false
maxLen = 4000
)
var (
chatID int64
apiToken string
delay int
query string
limit string
lokiUrl string
)
type Message struct {
ChatID int64 `json:"chat_id"`
Text string `json:"text"`
ParseMode string `json:"parse_mode"`
}
type Response struct {
Status string `json:"status"`
Data struct {
ResultType string `json:"resultType"`
Result []QueryResult
} `json:"data"`
}
type QueryResult struct {
Stream interface{} `json:"stream"`
Values [][]string `json:"values"`
}
type TelegramResponse struct {
Ok bool `json:"ok"`
ErrorCode int `json:"error_code"`
Description string `json:"description"`
}
func SplitHeader(longString string) []string {
splits := []string{}
var l, r int
for l, r = 0, maxLen; r < len(longString); l, r = r, r+maxLen {
for !utf8.RuneStart(longString[r]) {
r--
}
splits = append(splits, longString[l:r])
}
splits = append(splits, longString[l:])
return splits
}
func GetToken(configPath string) error {
viper.AutomaticEnv()
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
viper.SetDefault("LOGLEVEL", "debug")
viper.SetDefault("QUERY", defaultQuery)
viper.SetDefault("DELAY", defaultDelay)
viper.SetDefault("LIMIT", defaultLimit)
viper.SetDefault("LOKI_URL", defaultLokiUrl)
if configPath != "" {
log.Infof("Parsing config: %s", configPath)
viper.SetConfigFile(configPath)
err := viper.ReadInConfig()
if err != nil {
log.Warnf("Unable to read config file: %s", err)
}
} else {
log.Warnf("Config file is not specified.")
}
logLevelString := viper.GetString("loglevel")
logLevel, err := log.ParseLevel(logLevelString)
if err != nil {
log.Errorf("Unable to parse loglevel: %s", logLevelString)
}
log.SetLevel(logLevel)
apiToken = viper.GetString("BOT_TOKEN")
if apiToken == "" {
log.Errorf("Bot token error")
os.Exit(2)
}
delay = viper.GetInt("DELAY")
limit = viper.GetString("LIMIT")
chatID = viper.GetInt64("CHAT_ID")
apiToken = viper.GetString("BOT_TOKEN")
query = viper.GetString("QUERY")
lokiUrl = viper.GetString("LOKI_URL")
log.Infof("%s", apiToken)
log.Infof("%v", chatID)
log.Infof("%v", delay)
log.Infof("%v", query)
return nil
}
func SendMessage(url string, message *Message) error {
if fakeSend {
return nil
}
payload, err := json.Marshal(message)
if err != nil {
return err
}
response, err := http.Post(url, "application/json", bytes.NewBuffer(payload))
if err != nil {
return err
}
resp, _ := io.ReadAll(response.Body)
defer func(body io.ReadCloser) {
if err := body.Close(); err != nil {
log.Println("failed to close response body")
}
}(response.Body)
if response.StatusCode != http.StatusOK {
var tgResp TelegramResponse
if err := json.Unmarshal(resp, &tgResp); err != nil {
log.Errorf("SendMessage UnMarshal error")
}
return fmt.Errorf("failed to send successful request. Status was %d, text %s", tgResp.ErrorCode, tgResp.Description)
}
return nil
}
func main() {
customFormatter := new(log.TextFormatter)
customFormatter.TimestampFormat = "2006-01-02 15:04:05"
customFormatter.FullTimestamp = true
customFormatter.DisableLevelTruncation = false
log.SetFormatter(customFormatter)
err := GetToken(".env")
if err != nil {
log.Panicf("GetEnv error")
}
tgUrl := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", apiToken)
log.Infof(tgUrl)
now := time.Now()
endNano := strconv.FormatInt(now.UnixNano(), 10)
startNano := strconv.FormatInt(now.Add(-time.Minute*10).UnixNano(), 10)
//query = "{namespace=~\"firecore-.+\"} |~ \"ERROR|CRITICAL\" !~ \"401 error\" !~ \"paid has been failed\" !~ \"paid is failed\" !~ \"no more reserve\" !~ \"onlineCommission\" !~ \"apple\""
params := url.Values{}
params.Add("direction", "BACKWARD")
params.Add("limit", limit)
params.Add("query", query)
params.Add("start", startNano)
params.Add("end", endNano)
params.Add("step", "2")
lokiFullUrl := fmt.Sprintf("%s/loki/api/v1/query_range?%s", strings.TrimSuffix(lokiUrl, "/"), params.Encode())
log.Infoln(lokiFullUrl)
req, err := http.NewRequest(http.MethodGet, lokiFullUrl, nil)
if err != nil {
log.Errorln(err)
os.Exit(2)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Errorln(err)
os.Exit(3)
}
defer func() {
err := resp.Body.Close()
if err != nil {
log.Fatal(err)
}
}()
if resp.StatusCode != 200 {
log.Errorln("The requested URL was not found.")
}
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Errorln(err)
os.Exit(3)
}
var f Response
if err := json.Unmarshal([]byte(body), &f); err != nil {
log.Errorln("Can't unmarshal JSON")
}
var str strings.Builder
for _, r := range f.Data.Result {
//str.Reset()
ErrorLine := r.Stream.(map[string]interface{})
pre := fmt.Sprintf("<b>%s/%s:</b> ", ErrorLine["namespace"], ErrorLine["container"])
str.WriteString(pre)
//str.WriteString("```")
for _, v := range r.Values {
str.WriteString(v[1])
}
str.WriteString("\n")
//str.WriteString("```\n")
//msg := Message{chatID, str.String(), "Markdown"}
//Messages = append(Messages, msg)
}
MessagesCount := 0
for _, s := range SplitHeader(str.String()) {
msg := Message{chatID, s, "HTML"}
log.Infof("%d: %s\n", len(s), s)
if len(s) > 0 {
err = SendMessage(tgUrl, &msg)
MessagesCount += 1
if err != nil {
log.Errorln(err)
}
}
}
log.Infof(strconv.Itoa(MessagesCount))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment