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/logcli/client/file_test.go

223 lines
6.1 KiB

package client
import (
"bytes"
"errors"
"io"
"sort"
"strings"
"testing"
"time"
"github.com/grafana/loki/pkg/loghttp"
"github.com/grafana/loki/pkg/logproto"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFileClient_QueryRangeLogQueries(t *testing.T) {
input := []string{
`level=info event="loki started" caller=main.go ts=1625995076`,
`level=info event="runtime loader started" caller=main.go ts=1625995077`,
`level=error event="unable to read rules directory" file="/tmp/rules" caller=rules.go ts=1625995090`,
`level=error event="failed to apply wal" error="/tmp/wal/ corrupted" caller=wal.go ts=1625996090`,
`level=info event="loki ready" caller=main.go ts=1625996095`,
}
reversed := make([]string, len(input))
copy(reversed, input)
sort.Slice(reversed, func(i, j int) bool {
return i > j
})
now := time.Now()
cases := []struct {
name string
limit int
start, end time.Time
direction logproto.Direction
step, interval time.Duration
expectedStatus loghttp.QueryStatus
expected []string
}{
{
name: "return-all-logs-backward",
limit: 10, // more than input
start: now.Add(-1 * time.Hour),
end: now,
direction: logproto.BACKWARD,
step: 0, // let client decide based on start and end
interval: 0,
expectedStatus: loghttp.QueryStatusSuccess,
expected: reversed,
},
{
name: "return-all-logs-forward",
limit: 10, // more than input
start: now.Add(-1 * time.Hour),
end: now,
direction: logproto.FORWARD,
step: 0, // let the client decide based on start and end
interval: 0,
expectedStatus: loghttp.QueryStatusSuccess,
expected: input,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
client := NewFileClient(io.NopCloser(strings.NewReader(strings.Join(input, "\n"))))
resp, err := client.QueryRange(
`{foo="bar"}`, // label matcher doesn't matter.
c.limit,
c.start,
c.end,
c.direction,
c.step,
c.interval,
true,
)
require.NoError(t, err)
require.Equal(t, loghttp.QueryStatusSuccess, resp.Status)
assert.Equal(t, string(resp.Data.ResultType), loghttp.ResultTypeStream)
assertStreams(t, resp.Data.Result, c.expected)
})
}
}
func TestFileClient_Query(t *testing.T) {
input := []string{
`level=info event="loki started" caller=main.go ts=1625995076`,
`level=info event="runtime loader started" caller=main.go ts=1625995077`,
`level=error event="unable to read rules directory" file="/tmp/rules" caller=rules.go ts=1625995090`,
`level=error event="failed to apply wal" error="/tmp/wal/ corrupted" caller=wal.go ts=1625996090`,
`level=info event="loki ready" caller=main.go ts=1625996095`,
}
reversed := make([]string, len(input))
copy(reversed, input)
sort.Slice(reversed, func(i, j int) bool {
return i > j
})
now := time.Now()
cases := []struct {
name string
limit int
ts time.Time
direction logproto.Direction
expectedStatus loghttp.QueryStatus
expected []string
}{
{
name: "return-all-logs-backward",
limit: 10, // more than input
ts: now.Add(-1 * time.Hour),
direction: logproto.BACKWARD,
expectedStatus: loghttp.QueryStatusSuccess,
expected: reversed,
},
{
name: "return-all-logs-forward",
limit: 10, // more than input
ts: now.Add(-1 * time.Hour),
direction: logproto.FORWARD,
expectedStatus: loghttp.QueryStatusSuccess,
expected: input,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
client := NewFileClient(io.NopCloser(strings.NewReader(strings.Join(input, "\n"))))
resp, err := client.Query(
`{foo="bar"}`, // label matcher doesn't matter.
c.limit,
c.ts,
c.direction,
true,
)
require.NoError(t, err)
require.Equal(t, loghttp.QueryStatusSuccess, resp.Status)
assert.Equal(t, string(resp.Data.ResultType), loghttp.ResultTypeStream)
assertStreams(t, resp.Data.Result, c.expected)
})
}
}
func TestFileClient_ListLabelNames(t *testing.T) {
c := newEmptyClient(t)
values, err := c.ListLabelNames(true, time.Now(), time.Now())
require.NoError(t, err)
assert.Equal(t, &loghttp.LabelResponse{
Data: []string{defaultLabelKey},
Status: loghttp.QueryStatusSuccess,
}, values)
}
func TestFileClient_ListLabelValues(t *testing.T) {
c := newEmptyClient(t)
values, err := c.ListLabelValues(defaultLabelKey, true, time.Now(), time.Now())
require.NoError(t, err)
assert.Equal(t, &loghttp.LabelResponse{
Data: []string{defaultLabelValue},
Status: loghttp.QueryStatusSuccess,
}, values)
}
func TestFileClient_Series(t *testing.T) {
c := newEmptyClient(t)
got, err := c.Series(nil, time.Now(), time.Now(), true)
require.NoError(t, err)
exp := &loghttp.SeriesResponse{
Data: []loghttp.LabelSet{
{defaultLabelKey: defaultLabelValue},
},
Status: loghttp.QueryStatusSuccess,
}
assert.Equal(t, exp, got)
}
func TestFileClient_LiveTail(t *testing.T) {
c := newEmptyClient(t)
x, err := c.LiveTailQueryConn("", time.Second, 0, time.Now(), true)
require.Error(t, err)
require.Nil(t, x)
assert.True(t, errors.Is(err, ErrNotSupported))
}
func TestFileClient_GetOrgID(t *testing.T) {
c := newEmptyClient(t)
assert.Equal(t, defaultOrgID, c.GetOrgID())
}
func newEmptyClient(t *testing.T) *FileClient {
t.Helper()
return NewFileClient(io.NopCloser(&bytes.Buffer{}))
}
func assertStreams(t *testing.T, result loghttp.ResultValue, logLines []string) {
t.Helper()
streams, ok := result.(loghttp.Streams)
require.True(t, ok, "response type should be `loghttp.Streams`")
require.Len(t, streams, 1, "there should be only one stream for FileClient")
got := streams[0]
sort.Slice(got.Entries, func(i, j int) bool {
return got.Entries[i].Timestamp.UnixNano() < got.Entries[j].Timestamp.UnixNano()
})
require.Equal(t, len(got.Entries), len(logLines))
for i, entry := range got.Entries {
assert.Equal(t, entry.Line, logLines[i])
}
}