Use docker log timestamp as metric time (#7434)
This commit is contained in:
parent
022ff63d29
commit
0924ad2668
|
@ -2,8 +2,10 @@ package docker_log
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -287,7 +289,7 @@ func (d *DockerLogs) tailContainerLogs(
|
||||||
logOptions := types.ContainerLogsOptions{
|
logOptions := types.ContainerLogsOptions{
|
||||||
ShowStdout: true,
|
ShowStdout: true,
|
||||||
ShowStderr: true,
|
ShowStderr: true,
|
||||||
Timestamps: false,
|
Timestamps: true,
|
||||||
Details: false,
|
Details: false,
|
||||||
Follow: true,
|
Follow: true,
|
||||||
Tail: tail,
|
Tail: tail,
|
||||||
|
@ -311,6 +313,30 @@ func (d *DockerLogs) tailContainerLogs(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseLine(line []byte) (time.Time, string, error) {
|
||||||
|
parts := bytes.SplitN(line, []byte(" "), 2)
|
||||||
|
|
||||||
|
switch len(parts) {
|
||||||
|
case 1:
|
||||||
|
parts = append(parts, []byte(""))
|
||||||
|
}
|
||||||
|
|
||||||
|
tsString := string(parts[0])
|
||||||
|
|
||||||
|
// Keep any leading space, but remove whitespace from end of line.
|
||||||
|
// This preserves space in, for example, stacktraces, while removing
|
||||||
|
// annoying end of line characters and is similar to how other logging
|
||||||
|
// plugins such as syslog behave.
|
||||||
|
message := bytes.TrimRightFunc(parts[1], unicode.IsSpace)
|
||||||
|
|
||||||
|
ts, err := time.Parse(time.RFC3339Nano, tsString)
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, "", fmt.Errorf("error parsing timestamp %q: %v", tsString, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ts, string(message), nil
|
||||||
|
}
|
||||||
|
|
||||||
func tailStream(
|
func tailStream(
|
||||||
acc telegraf.Accumulator,
|
acc telegraf.Accumulator,
|
||||||
baseTags map[string]string,
|
baseTags map[string]string,
|
||||||
|
@ -328,22 +354,19 @@ func tailStream(
|
||||||
|
|
||||||
r := bufio.NewReaderSize(reader, 64*1024)
|
r := bufio.NewReaderSize(reader, 64*1024)
|
||||||
|
|
||||||
var err error
|
|
||||||
var message string
|
|
||||||
for {
|
for {
|
||||||
message, err = r.ReadString('\n')
|
line, err := r.ReadBytes('\n')
|
||||||
|
|
||||||
// Keep any leading space, but remove whitespace from end of line.
|
if len(line) != 0 {
|
||||||
// This preserves space in, for example, stacktraces, while removing
|
ts, message, err := parseLine(line)
|
||||||
// annoying end of line characters and is similar to how other logging
|
if err != nil {
|
||||||
// plugins such as syslog behave.
|
acc.AddError(err)
|
||||||
message = strings.TrimRightFunc(message, unicode.IsSpace)
|
} else {
|
||||||
|
|
||||||
if len(message) != 0 {
|
|
||||||
acc.AddFields("docker_log", map[string]interface{}{
|
acc.AddFields("docker_log", map[string]interface{}{
|
||||||
"container_id": containerID,
|
"container_id": containerID,
|
||||||
"message": message,
|
"message": message,
|
||||||
}, tags)
|
}, tags, ts)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -53,6 +53,14 @@ func (r *Response) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MustParse(layout, value string) time.Time {
|
||||||
|
tm, err := time.Parse(layout, value)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return tm
|
||||||
|
}
|
||||||
|
|
||||||
func Test(t *testing.T) {
|
func Test(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -87,7 +95,7 @@ func Test(t *testing.T) {
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
ContainerLogsF: func(ctx context.Context, containerID string, options types.ContainerLogsOptions) (io.ReadCloser, error) {
|
ContainerLogsF: func(ctx context.Context, containerID string, options types.ContainerLogsOptions) (io.ReadCloser, error) {
|
||||||
return &Response{Reader: bytes.NewBuffer([]byte("hello\n"))}, nil
|
return &Response{Reader: bytes.NewBuffer([]byte("2020-04-28T18:43:16.432691200Z hello\n"))}, nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: []telegraf.Metric{
|
expected: []telegraf.Metric{
|
||||||
|
@ -104,7 +112,7 @@ func Test(t *testing.T) {
|
||||||
"container_id": "deadbeef",
|
"container_id": "deadbeef",
|
||||||
"message": "hello",
|
"message": "hello",
|
||||||
},
|
},
|
||||||
time.Now(),
|
MustParse(time.RFC3339Nano, "2020-04-28T18:43:16.432691200Z"),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -130,7 +138,7 @@ func Test(t *testing.T) {
|
||||||
ContainerLogsF: func(ctx context.Context, containerID string, options types.ContainerLogsOptions) (io.ReadCloser, error) {
|
ContainerLogsF: func(ctx context.Context, containerID string, options types.ContainerLogsOptions) (io.ReadCloser, error) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
w := stdcopy.NewStdWriter(&buf, stdcopy.Stdout)
|
w := stdcopy.NewStdWriter(&buf, stdcopy.Stdout)
|
||||||
w.Write([]byte("hello from stdout"))
|
w.Write([]byte("2020-04-28T18:42:16.432691200Z hello from stdout"))
|
||||||
return &Response{Reader: &buf}, nil
|
return &Response{Reader: &buf}, nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -148,7 +156,7 @@ func Test(t *testing.T) {
|
||||||
"container_id": "deadbeef",
|
"container_id": "deadbeef",
|
||||||
"message": "hello from stdout",
|
"message": "hello from stdout",
|
||||||
},
|
},
|
||||||
time.Now(),
|
MustParse(time.RFC3339Nano, "2020-04-28T18:42:16.432691200Z"),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -172,7 +180,9 @@ func Test(t *testing.T) {
|
||||||
acc.Wait(len(tt.expected))
|
acc.Wait(len(tt.expected))
|
||||||
plugin.Stop()
|
plugin.Stop()
|
||||||
|
|
||||||
testutil.RequireMetricsEqual(t, tt.expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
|
require.Nil(t, acc.Errors) // no errors during gathering
|
||||||
|
|
||||||
|
testutil.RequireMetricsEqual(t, tt.expected, acc.GetTelegrafMetrics())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue