Like Prometheus, but for logs.
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.
 
 
 
 
 
 
loki/pkg/ingester/stream.go

125 lines
3.3 KiB

package ingester
import (
"context"
"net/http"
"time"
"github.com/cortexproject/cortex/pkg/ingester/client"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/model"
"github.com/weaveworks/common/httpgrpc"
"github.com/grafana/loki/pkg/chunkenc"
"github.com/grafana/loki/pkg/iter"
"github.com/grafana/loki/pkg/logproto"
)
var (
chunksCreatedTotal = prometheus.NewCounter(prometheus.CounterOpts{
Namespace: "loki",
Name: "ingester_chunks_created_total",
Help: "The total number of chunks created in the ingester.",
})
chunksFlushedTotal = prometheus.NewCounter(prometheus.CounterOpts{
Namespace: "loki",
Name: "ingester_chunks_flushed_total",
Help: "The total number of chunks flushed by the ingester.",
})
samplesPerChunk = prometheus.NewHistogram(prometheus.HistogramOpts{
Namespace: "loki",
Subsystem: "ingester",
Name: "samples_per_chunk",
Help: "The number of samples in a chunk.",
Buckets: prometheus.LinearBuckets(4096, 2048, 6),
})
)
func init() {
prometheus.MustRegister(chunksCreatedTotal)
prometheus.MustRegister(chunksFlushedTotal)
prometheus.MustRegister(samplesPerChunk)
}
type stream struct {
// Newest chunk at chunks[n-1].
// Not thread-safe; assume accesses to this are locked by caller.
chunks []chunkDesc
fp model.Fingerprint
labels []client.LabelPair
}
type chunkDesc struct {
chunk chunkenc.Chunk
closed bool
flushed time.Time
lastUpdated time.Time
}
func newStream(fp model.Fingerprint, labels []client.LabelPair) *stream {
return &stream{
fp: fp,
labels: labels,
}
}
func (s *stream) Push(_ context.Context, entries []logproto.Entry) error {
if len(s.chunks) == 0 {
s.chunks = append(s.chunks, chunkDesc{
chunk: chunkenc.NewMemChunk(chunkenc.EncGZIP),
})
chunksCreatedTotal.Inc()
}
// Don't fail on the first append error - if samples are sent out of order,
// we still want to append the later ones.
var appendErr error
for i := range entries {
chunk := &s.chunks[len(s.chunks)-1]
if chunk.closed || !chunk.chunk.SpaceFor(&entries[i]) {
chunk.closed = true
samplesPerChunk.Observe(float64(chunk.chunk.Size()))
chunksCreatedTotal.Inc()
s.chunks = append(s.chunks, chunkDesc{
chunk: chunkenc.NewMemChunk(chunkenc.EncGZIP),
})
chunk = &s.chunks[len(s.chunks)-1]
}
if err := chunk.chunk.Append(&entries[i]); err != nil {
appendErr = err
}
chunk.lastUpdated = entries[i].Timestamp
}
if appendErr == chunkenc.ErrOutOfOrder {
return httpgrpc.Errorf(http.StatusBadRequest, "entry out of order for stream: %s", client.FromLabelPairsToLabels(s.labels).String())
}
return appendErr
}
// Returns an iterator.
func (s *stream) Iterator(from, through time.Time, direction logproto.Direction) (iter.EntryIterator, error) {
iterators := make([]iter.EntryIterator, 0, len(s.chunks))
for _, c := range s.chunks {
iter, err := c.chunk.Iterator(from, through, direction)
if err != nil {
return nil, err
}
if iter != nil {
iterators = append(iterators, iter)
}
}
if direction != logproto.FORWARD {
for left, right := 0, len(iterators)-1; left < right; left, right = left+1, right-1 {
iterators[left], iterators[right] = iterators[right], iterators[left]
}
}
return iter.NewNonOverlappingIterator(iterators, client.FromLabelPairsToLabels(s.labels).String()), nil
}