From 74ac00e17c8f692921898b617290677b0747e290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9mi=20V=C3=A1nyi?= Date: Mon, 3 Sep 2018 21:09:32 +0200 Subject: [PATCH] Cherry-pick #7991 to 6.x: Add tag "truncated" to "log.flags" if incoming line is longer than configured limit (#8165) * Add tag "truncated" to "log.flags" if incoming line is longer than configured limit (#7991) A new field is added to store the flags of an event named "log.flags". If a message is truncated, "truncated" flag is added to the list. Example event with "truncated" flag: { "@timestamp": "2018-08-16T13:00:46.759Z", "@metadata": { "beat": "filebeat", "type": "doc", "version": "7.0.0-alpha1" }, "host": { "name": "sleipnir" }, "source": "/home/n/test.log", "offset": 33, "log": { "flags": [ "truncated" ], }, "message": "test line", "prospector": { "type": "log" }, "input": { "type": "log" }, "beat": { "hostname": "sleipnir", "version": "7.0.0-alpha1", "name": "sleipnir" } } Closes #7022 (cherry picked from commit 08842368ca744ee682e1e20f338774c8c69fe931) * fix changelog && rebase --- CHANGELOG-developer.asciidoc | 1 + CHANGELOG.asciidoc | 2 + filebeat/_meta/fields.common.yml | 4 + filebeat/docs/fields.asciidoc | 8 ++ filebeat/include/fields.go | 2 +- filebeat/reader/message.go | 23 +++++- filebeat/reader/multiline/multiline.go | 17 ++++ filebeat/reader/multiline/multiline_test.go | 83 +++++++++++++++++++ filebeat/reader/readfile/limit.go | 1 + filebeat/reader/readfile/limit_test.go | 88 +++++++++++++++++++++ libbeat/common/mapstr.go | 27 +++++-- libbeat/common/mapstr_test.go | 73 +++++++++++++++++ 12 files changed, 317 insertions(+), 12 deletions(-) create mode 100644 filebeat/reader/readfile/limit_test.go diff --git a/CHANGELOG-developer.asciidoc b/CHANGELOG-developer.asciidoc index cf844df59cfa..96f2db8ef2c2 100644 --- a/CHANGELOG-developer.asciidoc +++ b/CHANGELOG-developer.asciidoc @@ -39,3 +39,4 @@ The list below covers the major changes between 6.3.0 and master only. - Libbeat provides a global registry for beats developer that allow to register and retrieve plugin. {pull}7392[7392] - Added more options to control required and optional fields in schema.Apply(), error returned is a plain nil if no error happened {pull}7335[7335] - Packaging on MacOS now produces a .dmg file containing an installer (.pkg) and uninstaller for the Beat. {pull}7481[7481] +- New function `AddTagsWithKey` is added, so `common.MapStr` can be enriched with tags with an arbitrary key. {pull}7991[7991] diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 6f2f2de98f1d..9579c669cc39 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -83,6 +83,8 @@ https://github.com/elastic/beats/compare/v6.4.0...6.x[Check the HEAD diff] *Filebeat* +- Add tag "truncated" to "log.flags" if incoming line is longer than configured limit. {pull}7991[7991] + *Heartbeat* *Metricbeat* diff --git a/filebeat/_meta/fields.common.yml b/filebeat/_meta/fields.common.yml index 5781e4978792..930e0f67f906 100644 --- a/filebeat/_meta/fields.common.yml +++ b/filebeat/_meta/fields.common.yml @@ -108,6 +108,10 @@ description: > Logging level. + - name: log.flags + description: > + This field contains the flags of the event. + - name: event.created type: date description: > diff --git a/filebeat/docs/fields.asciidoc b/filebeat/docs/fields.asciidoc index edd866198bc6..05ac09dce9a0 100644 --- a/filebeat/docs/fields.asciidoc +++ b/filebeat/docs/fields.asciidoc @@ -2652,6 +2652,14 @@ type: keyword Logging level. +-- + +*`log.flags`*:: ++ +-- +This field contains the flags of the event. + + -- *`event.created`*:: diff --git a/filebeat/include/fields.go b/filebeat/include/fields.go index 54f7edfc95dc..fda0f3f48415 100644 --- a/filebeat/include/fields.go +++ b/filebeat/include/fields.go @@ -31,5 +31,5 @@ func init() { // Asset returns asset data func Asset() string { - return "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" + return "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" } diff --git a/filebeat/reader/message.go b/filebeat/reader/message.go index c5e965b10d04..a92bc9092ccf 100644 --- a/filebeat/reader/message.go +++ b/filebeat/reader/message.go @@ -49,13 +49,28 @@ func (m *Message) IsEmpty() bool { return false } -func (msg *Message) AddFields(fields common.MapStr) { +// AddFields adds fields to the message. +func (m *Message) AddFields(fields common.MapStr) { if fields == nil { return } - if msg.Fields == nil { - msg.Fields = common.MapStr{} + if m.Fields == nil { + m.Fields = common.MapStr{} } - msg.Fields.Update(fields) + m.Fields.Update(fields) +} + +// AddFlagsWithKey adds flags to the message with an arbitrary key. +// If the field does not exist, it is created. +func (m *Message) AddFlagsWithKey(key string, flags ...string) error { + if len(flags) == 0 { + return nil + } + + if m.Fields == nil { + m.Fields = common.MapStr{} + } + + return common.AddTagsWithKey(m.Fields, key, flags) } diff --git a/filebeat/reader/multiline/multiline.go b/filebeat/reader/multiline/multiline.go index 57209be94cdc..2e974c19b235 100644 --- a/filebeat/reader/multiline/multiline.go +++ b/filebeat/reader/multiline/multiline.go @@ -48,6 +48,7 @@ type Reader struct { separator []byte last []byte numLines int + truncated int err error // last seen error state func(*Reader) (reader.Message, error) message reader.Message @@ -262,13 +263,19 @@ func (mlr *Reader) clear() { mlr.message = reader.Message{} mlr.last = nil mlr.numLines = 0 + mlr.truncated = 0 mlr.err = nil } // finalize writes the existing content into the returned message and resets all reader variables. func (mlr *Reader) finalize() reader.Message { + if mlr.truncated > 0 { + mlr.message.AddFlagsWithKey("log.flags", "truncated") + } + // Copy message from existing content msg := mlr.message + mlr.clear() return msg } @@ -303,6 +310,16 @@ func (mlr *Reader) addLine(m reader.Message) { } mlr.message.Content = append(tmp, m.Content[:space]...) mlr.numLines++ + + // add number of truncated bytes to fields + diff := len(m.Content) - space + if diff > 0 { + mlr.truncated += diff + } + } else { + // increase the number of skipped bytes, if cannot add + mlr.truncated += len(m.Content) + } mlr.last = m.Content diff --git a/filebeat/reader/multiline/multiline_test.go b/filebeat/reader/multiline/multiline_test.go index 6fe05fde0d28..1c6ea96c6f38 100644 --- a/filebeat/reader/multiline/multiline_test.go +++ b/filebeat/reader/multiline/multiline_test.go @@ -150,6 +150,41 @@ func TestMultilineBeforeNegateOKWithEmptyLine(t *testing.T) { ) } +func TestMultilineAfterTruncated(t *testing.T) { + pattern := match.MustCompile(`^[ ]`) // next line is indented a space + maxLines := 2 + testMultilineTruncated(t, + Config{ + Pattern: &pattern, + Match: "after", + MaxLines: &maxLines, + }, + 2, + true, + []string{ + "line1\n line1.1\n line1.2\n", + "line2\n line2.1\n line2.2\n"}, + []string{ + "line1\n line1.1", + "line2\n line2.1"}, + ) + testMultilineTruncated(t, + Config{ + Pattern: &pattern, + Match: "after", + MaxLines: &maxLines, + }, + 2, + false, + []string{ + "line1\n line1.1\n", + "line2\n line2.1\n"}, + []string{ + "line1\n line1.1", + "line2\n line2.1"}, + ) +} + func testMultilineOK(t *testing.T, cfg Config, events int, expected ...string) { _, buf := createLineBuffer(expected...) r := createMultilineTestReader(t, buf, cfg) @@ -177,6 +212,54 @@ func testMultilineOK(t *testing.T, cfg Config, events int, expected ...string) { } } +func testMultilineTruncated(t *testing.T, cfg Config, events int, truncated bool, input, expected []string) { + _, buf := createLineBuffer(input...) + r := createMultilineTestReader(t, buf, cfg) + + var messages []reader.Message + for { + message, err := r.Next() + if err != nil { + break + } + + messages = append(messages, message) + } + + if len(messages) != events { + t.Fatalf("expected %v lines, read only %v line(s)", len(expected), len(messages)) + } + + for _, message := range messages { + found := false + statusFlags, err := message.Fields.GetValue("log.flags") + if err != nil { + if !truncated { + assert.False(t, found) + return + } + t.Fatalf("error while getting log.status field: %v", err) + } + + switch flags := statusFlags.(type) { + case []string: + for _, f := range flags { + if f == "truncated" { + found = true + } + } + default: + t.Fatalf("incorrect type for log.flags") + } + + if truncated { + assert.True(t, found) + } else { + assert.False(t, found) + } + } +} + func createMultilineTestReader(t *testing.T, in *bytes.Buffer, cfg Config) reader.Reader { encFactory, ok := encoding.FindEncoding("plain") if !ok { diff --git a/filebeat/reader/readfile/limit.go b/filebeat/reader/readfile/limit.go index 1d7b46e2a471..42f9635ba124 100644 --- a/filebeat/reader/readfile/limit.go +++ b/filebeat/reader/readfile/limit.go @@ -38,6 +38,7 @@ func (r *LimitReader) Next() (reader.Message, error) { message, err := r.reader.Next() if len(message.Content) > r.maxBytes { message.Content = message.Content[:r.maxBytes] + message.AddFlagsWithKey("log.flags", "truncated") } return message, err } diff --git a/filebeat/reader/readfile/limit_test.go b/filebeat/reader/readfile/limit_test.go new file mode 100644 index 000000000000..c7096cb938f3 --- /dev/null +++ b/filebeat/reader/readfile/limit_test.go @@ -0,0 +1,88 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build !integration + +package readfile + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/elastic/beats/filebeat/reader" +) + +type mockReader struct { + line []byte +} + +func (m *mockReader) Next() (reader.Message, error) { + return reader.Message{ + Content: m.line, + }, nil +} + +var limitTests = []struct { + line string + maxBytes int + truncated bool +}{ + {"long-long-line", 5, true}, + {"long-long-line", 3, true}, + {"long-long-line", len("long-long-line"), false}, +} + +func TestLimitReader(t *testing.T) { + for _, test := range limitTests { + r := NewLimitReader(&mockReader{[]byte(test.line)}, test.maxBytes) + + msg, err := r.Next() + if err != nil { + t.Fatalf("Error reading from mock reader: %v", err) + } + + assert.Equal(t, test.maxBytes, len(msg.Content)) + + found := false + statusFlags, err := msg.Fields.GetValue("log.flags") + if err != nil { + if !test.truncated { + assert.False(t, found) + return + } + t.Fatalf("Error getting truncated value: %v", err) + } + + switch flags := statusFlags.(type) { + case []string: + for _, f := range flags { + if f == "truncated" { + found = true + } + } + default: + t.Fatalf("incorrect type for log.flags") + } + + if test.truncated { + assert.True(t, found) + } else { + assert.False(t, found) + } + } +} diff --git a/libbeat/common/mapstr.go b/libbeat/common/mapstr.go index 0bc2d4b2a161..2b825fd67e39 100644 --- a/libbeat/common/mapstr.go +++ b/libbeat/common/mapstr.go @@ -305,25 +305,38 @@ func MergeFields(ms, fields MapStr, underRoot bool) error { // exist then it will be created. If the tags field exists and is not a []string // then an error will be returned. It does not deduplicate the list of tags. func AddTags(ms MapStr, tags []string) error { + return AddTagsWithKey(ms, TagsKey, tags) +} + +// AddTagsWithKey appends a tag to the key field of ms. If the field does not +// exist then it will be created. If the field exists and is not a []string +// then an error will be returned. It does not deduplicate the list. +func AddTagsWithKey(ms MapStr, key string, tags []string) error { if ms == nil || len(tags) == 0 { return nil } - eventTags, exists := ms[TagsKey] - if !exists { - ms[TagsKey] = tags + + k, subMap, oldTags, present, err := mapFind(key, ms, true) + if err != nil { + return err + } + + if !present { + subMap[k] = tags return nil } - switch arr := eventTags.(type) { + switch arr := oldTags.(type) { case []string: - ms[TagsKey] = append(arr, tags...) + subMap[k] = append(arr, tags...) case []interface{}: for _, tag := range tags { arr = append(arr, tag) } - ms[TagsKey] = arr + subMap[k] = arr default: - return errors.Errorf("expected string array by type is %T", eventTags) + return errors.Errorf("expected string array by type is %T", oldTags) + } return nil } diff --git a/libbeat/common/mapstr_test.go b/libbeat/common/mapstr_test.go index 9002e5dfe03c..bb582c53b761 100644 --- a/libbeat/common/mapstr_test.go +++ b/libbeat/common/mapstr_test.go @@ -546,6 +546,79 @@ func TestAddTag(t *testing.T) { } } +func TestAddTagsWithKey(t *testing.T) { + type io struct { + Event MapStr + Key string + Tags []string + Output MapStr + Err string + } + tests := []io{ + // No existing tags, creates new tag array + { + Event: MapStr{}, + Key: "tags", + Tags: []string{"json"}, + Output: MapStr{ + "tags": []string{"json"}, + }, + }, + // Existing tags is a []string, appends + { + Event: MapStr{ + "tags": []string{"json"}, + }, + Key: "tags", + Tags: []string{"docker"}, + Output: MapStr{ + "tags": []string{"json", "docker"}, + }, + }, + // Existing tags are in submap and is a []interface{}, appends + { + Event: MapStr{ + "log": MapStr{ + "flags": []interface{}{"json"}, + }, + }, + Key: "log.flags", + Tags: []string{"docker"}, + Output: MapStr{ + "log": MapStr{ + "flags": []interface{}{"json", "docker"}, + }, + }, + }, + // Existing tags are in a submap and is not a []string or []interface{} + { + Event: MapStr{ + "log": MapStr{ + "flags": "not a slice", + }, + }, + Key: "log.flags", + Tags: []string{"docker"}, + Output: MapStr{ + "log": MapStr{ + "flags": "not a slice", + }, + }, + Err: "expected string array", + }, + } + + for _, test := range tests { + err := AddTagsWithKey(test.Event, test.Key, test.Tags) + assert.Equal(t, test.Output, test.Event) + if test.Err != "" { + assert.Contains(t, err.Error(), test.Err) + } else { + assert.NoError(t, err) + } + } +} + func TestFlatten(t *testing.T) { type data struct { Event MapStr