action/log/logadapter/loki.go (86 lines of code) (raw):
package logadapter
import (
"encoding/json"
"fmt"
"github.com/seata/seata-ctl/tool"
"io"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
"time"
)
// QueryLogs queries logs from Loki based on the filter and settings provided
func (l *Loki) QueryLogs(filter map[string]interface{}, currency *Currency, number int) error {
// Prepare the query URL with time range
params := url.Values{}
params.Set("query", filter["query"].(string))
params.Set("limit", strconv.Itoa(number))
// Set start time if provided
if value, ok := filter["start"]; ok {
res, err := parseToTimestamp(value.(string))
if err != nil {
return err
}
params.Set("start", fmt.Sprintf("%d", res))
}
// Set end time if provided
if value, ok := filter["end"]; ok {
res, err := parseToTimestamp(value.(string))
if err != nil {
return err
}
params.Set("end", fmt.Sprintf("%d", res))
}
queryURL := currency.Address + LokiAddressPath + params.Encode()
// Send GET request to Loki
resp, err := http.Get(queryURL)
if err != nil {
return fmt.Errorf("error sending request to Loki: %v", err)
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
tool.Logger.Errorf("Failed to close response body: %v", err)
}
}(resp.Body)
// Read the response body
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("error reading response from Loki: %v", err)
}
// Parse the JSON response
var result LokiQueryResult
err = json.Unmarshal(body, &result)
if err != nil {
return fmt.Errorf("error parsing Loki response: %v", err)
}
// Process and print logs
if result.Status == "success" {
if len(result.Data.Result) == 0 {
return fmt.Errorf("loki query returned no results")
}
for _, stream := range result.Data.Result {
for _, entry := range stream.Values {
// Extract timestamp and log message
value := entry[1]
// Print the readable timestamp and log message
if strings.Contains(value, "INFO") {
tool.Logger.Info(fmt.Sprintf("%v", value))
}
if strings.Contains(value, "ERROR") {
tool.Logger.Error(fmt.Sprintf("%v", value))
}
if strings.Contains(value, "WARN") {
tool.Logger.Warn(fmt.Sprintf("%v", value))
}
}
}
} else {
fmt.Printf("Query failed, status: %s\n", result.Status)
}
return nil
}
// parseToTimestamp parses a time string to a Unix nanosecond timestamp
func parseToTimestamp(timeStr string) (int64, error) {
// Load the specified time zone (UTC in this case)
loc, err := time.LoadLocation("UTC")
if err != nil {
return 0, fmt.Errorf("failed to load timezone: %v", err)
}
// Parse the time string using the specified layout and timezone
t, err := time.ParseInLocation(TimeLayout, timeStr, loc)
if err != nil {
return 0, fmt.Errorf("failed to parse time: %v", err)
}
// Convert to Unix nanosecond timestamp
timestamp := t.UnixNano()
return timestamp, nil
}