The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/services/ngalert/state/historian/encode.go

107 lines
2.5 KiB

package historian
import (
"encoding/json"
"fmt"
"slices"
"strconv"
"strings"
"github.com/gogo/protobuf/proto"
"github.com/golang/snappy"
"github.com/prometheus/common/model"
"github.com/grafana/grafana/pkg/components/loki/logproto"
)
type JsonEncoder struct{}
func (e JsonEncoder) encode(s []Stream) ([]byte, error) {
body := struct {
Streams []Stream `json:"streams"`
}{Streams: s}
enc, err := json.Marshal(body)
if err != nil {
return nil, fmt.Errorf("failed to serialize Loki payload: %w", err)
}
return enc, nil
}
func (e JsonEncoder) headers() map[string]string {
return map[string]string{
"Content-Type": "application/json",
}
}
type SnappyProtoEncoder struct{}
func (e SnappyProtoEncoder) encode(s []Stream) ([]byte, error) {
body := logproto.PushRequest{
Streams: make([]logproto.Stream, 0, len(s)),
}
for _, str := range s {
entries := make([]logproto.Entry, 0, len(str.Values))
for _, sample := range str.Values {
entries = append(entries, logproto.Entry{
Timestamp: sample.T,
Line: sample.V,
})
}
body.Streams = append(body.Streams, logproto.Stream{
Labels: labelsMapToString(str.Stream, ""),
Entries: entries,
// Hash seems to be mainly used for query responses. Promtail does not seem to calculate this field on push.
})
}
buf, err := proto.Marshal(&body)
if err != nil {
return nil, fmt.Errorf("failed to serialize Loki payload to proto: %w", err)
}
buf = snappy.Encode(nil, buf)
return buf, nil
}
func (e SnappyProtoEncoder) headers() map[string]string {
return map[string]string{
"Content-Type": "application/x-protobuf",
"Content-Encoding": "snappy",
}
}
// Copied from promtail.
// Modified slightly to work in terms of plain map[string]string to avoid some unnecessary copies and type casts.
// TODO: pkg/components/loki/lokihttp/batch.go contains an older (loki 2.7.4 released) version of this.
// TODO: Consider replacing that one, with this one.
func labelsMapToString(ls map[string]string, without model.LabelName) string {
var b strings.Builder
totalSize := 2
lstrs := make([]string, 0, len(ls))
for l, v := range ls {
if l == string(without) {
continue
}
lstrs = append(lstrs, l)
// guess size increase: 2 for `, ` between labels and 3 for the `=` and quotes around label value
totalSize += len(l) + 2 + len(v) + 3
}
b.Grow(totalSize)
b.WriteByte('{')
slices.Sort(lstrs)
for i, l := range lstrs {
if i > 0 {
b.WriteString(", ")
}
b.WriteString(l)
b.WriteString(`=`)
b.WriteString(strconv.Quote(ls[l]))
}
b.WriteByte('}')
return b.String()
}