Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions filebeat/_meta/config/filebeat.inputs.reference.yml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,11 @@ filebeat.inputs:
# Time strings like 2h (2 hours), 5m (5 minutes) can be used.
#ignore_older: 0

# Ignore files that have not been updated since the selected event.
# ignore_inactive is disabled by default, so no files are ignored by setting it to "".
# Available options: since_first_start, since_last_start.
#ignore_inactive: ""

# Defines the buffer size every harvester uses when fetching the file
#harvester_buffer_size: 16384

Expand Down
5 changes: 5 additions & 0 deletions filebeat/filebeat.reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,11 @@ filebeat.inputs:
# Time strings like 2h (2 hours), 5m (5 minutes) can be used.
#ignore_older: 0

# Ignore files that have not been updated since the selected event.
# ignore_inactive is disabled by default, so no files are ignored by setting it to "".
# Available options: since_first_start, since_last_start.
#ignore_inactive: ""

# Defines the buffer size every harvester uses when fetching the file
#harvester_buffer_size: 16384

Expand Down
6 changes: 3 additions & 3 deletions filebeat/input/default-inputs/inputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ import (

func Init(info beat.Info, log *logp.Logger, components beater.StateStore) []v2.Plugin {
return append(
genericInputs(log, components),
genericInputs(info, log, components),
osInputs(info, log, components)...,
)
}

func genericInputs(log *logp.Logger, components beater.StateStore) []v2.Plugin {
func genericInputs(info beat.Info, log *logp.Logger, components beater.StateStore) []v2.Plugin {
return []v2.Plugin{
filestream.Plugin(log, components),
filestream.Plugin(info, log, components),
unix.Plugin(),
}
}
18 changes: 10 additions & 8 deletions filebeat/input/filestream/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/dustin/go-humanize"

loginp "github.com/elastic/beats/v7/filebeat/input/filestream/internal/input-logfile"
"github.com/elastic/beats/v7/libbeat/common"
"github.com/elastic/beats/v7/libbeat/common/match"
"github.com/elastic/beats/v7/libbeat/reader/readfile"
Expand All @@ -32,14 +33,15 @@ import (
type config struct {
Reader readerConfig `config:",inline"`

Paths []string `config:"paths"`
Close closerConfig `config:"close"`
FileWatcher *common.ConfigNamespace `config:"prospector"`
FileIdentity *common.ConfigNamespace `config:"file_identity"`
CleanInactive time.Duration `config:"clean_inactive" validate:"min=0"`
CleanRemoved bool `config:"clean_removed"`
HarvesterLimit uint32 `config:"harvester_limit" validate:"min=0"`
IgnoreOlder time.Duration `config:"ignore_older"`
Paths []string `config:"paths"`
Close closerConfig `config:"close"`
FileWatcher *common.ConfigNamespace `config:"prospector"`
FileIdentity *common.ConfigNamespace `config:"file_identity"`
CleanInactive time.Duration `config:"clean_inactive" validate:"min=0"`
CleanRemoved bool `config:"clean_removed"`
HarvesterLimit uint32 `config:"harvester_limit" validate:"min=0"`
IgnoreOlder time.Duration `config:"ignore_older"`
IgnoreInactive loginp.IgnoreInactiveType `config:"ignore_inactive"`
}

type closerConfig struct {
Expand Down
2 changes: 1 addition & 1 deletion filebeat/input/filestream/environment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (e *inputTestingEnvironment) mustCreateInput(config map[string]interface{})

func (e *inputTestingEnvironment) getManager() v2.InputManager {
e.pluginInitOnce.Do(func() {
e.plugin = Plugin(logp.L(), e.stateStore)
e.plugin = Plugin(beat.Info{FirstStart: time.Now(), StartTime: time.Now()}, logp.L(), e.stateStore)
})
return e.plugin.Manager
}
Expand Down
4 changes: 3 additions & 1 deletion filebeat/input/filestream/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ type filestream struct {
}

// Plugin creates a new filestream input plugin for creating a stateful input.
func Plugin(log *logp.Logger, store loginp.StateStore) input.Plugin {
func Plugin(info beat.Info, log *logp.Logger, store loginp.StateStore) input.Plugin {
return input.Plugin{
Name: pluginName,
Stability: feature.Experimental,
Expand All @@ -69,6 +69,8 @@ func Plugin(log *logp.Logger, store loginp.StateStore) input.Plugin {
Doc: "The filestream input collects logs from the local filestream service",
Manager: &loginp.InputManager{
Logger: log,
FirstStart: info.FirstStart,
StartTime: info.StartTime,
StateStore: store,
Type: pluginName,
Configure: configure,
Expand Down
25 changes: 21 additions & 4 deletions filebeat/input/filestream/internal/input-logfile/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ import (
type InputManager struct {
Logger *logp.Logger

// FirstStart is the time when Filebeat was first started on a given host.
FirstStart time.Time
// StartTime is the last time Filebeat was started on a given host.
StartTime time.Time

// StateStore gives the InputManager access to the persitent key value store.
StateStore StateStore

Expand Down Expand Up @@ -155,9 +160,10 @@ func (cim *InputManager) Create(config *common.Config) (input.Input, error) {
}

settings := struct {
ID string `config:"id"`
CleanTimeout time.Duration `config:"clean_timeout"`
HarvesterLimit uint64 `config:"harvester_limit"`
ID string `config:"id"`
CleanTimeout time.Duration `config:"clean_timeout"`
HarvesterLimit uint64 `config:"harvester_limit"`
IgnoreSince IgnoreInactiveType `config:"ignore_since"`
}{ID: "", CleanTimeout: cim.DefaultCleanTimeout, HarvesterLimit: 0}
if err := config.Unpack(&settings); err != nil {
return nil, err
Expand All @@ -179,7 +185,7 @@ func (cim *InputManager) Create(config *common.Config) (input.Input, error) {
pStore := cim.getRetainedStore()
defer pStore.Release()
prospectorStore := newSourceStore(pStore, sourceIdentifier)
err = prospector.Init(prospectorStore)
err = prospector.Init(prospectorStore, cim.getIgnoreSince(settings.IgnoreSince))
if err != nil {
return nil, err
}
Expand All @@ -195,6 +201,17 @@ func (cim *InputManager) Create(config *common.Config) (input.Input, error) {
}, nil
}

func (cim *InputManager) getIgnoreSince(t IgnoreInactiveType) time.Time {
switch t {
case IgnoreInactiveSinceLastStart:
return cim.StartTime
case IgnoreInactiveSinceFirstStart:
return cim.FirstStart
default:
return time.Time{}
}
}

func (cim *InputManager) getRetainedStore() *store {
store := cim.store
store.Retain()
Expand Down
32 changes: 31 additions & 1 deletion filebeat/input/filestream/internal/input-logfile/prospector.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
package input_logfile

import (
"fmt"
"time"

input "github.com/elastic/beats/v7/filebeat/input/v2"
)

Expand All @@ -26,7 +29,7 @@ import (
// It also updates the statestore with the meta data of the running harvesters.
type Prospector interface {
// Init runs the cleanup processes before starting the prospector.
Init(c ProspectorCleaner) error
Init(c ProspectorCleaner, ignoreSince time.Time) error
// Run starts the event loop and handles the incoming events
// either by starting/stopping a harvester, or updating the statestore.
Run(input.Context, StateMetadataUpdater, HarvesterGroup)
Expand Down Expand Up @@ -63,3 +66,30 @@ type Value interface {
// UnpackCursorMeta returns the cursor metadata required by the prospector.
UnpackCursorMeta(to interface{}) error
}

type IgnoreInactiveType uint8

const (
InvalidIgnoreInactive = iota
IgnoreInactiveSinceLastStart
IgnoreInactiveSinceFirstStart

ignoreInactiveSinceLastStartStr = "since_last_start"
ignoreInactiveSinceFirstStartStr = "since_first_start"
)

var (
ignoreInactiveSettings = map[string]IgnoreInactiveType{
ignoreInactiveSinceLastStartStr: IgnoreInactiveSinceLastStart,
ignoreInactiveSinceFirstStartStr: IgnoreInactiveSinceFirstStart,
}
)

func (t *IgnoreInactiveType) Unpack(v string) error {
val, ok := ignoreInactiveSettings[v]
if !ok {
return fmt.Errorf("invalid ignore_inactive setting: %s", v)
}
*t = val
return nil
}
21 changes: 15 additions & 6 deletions filebeat/input/filestream/prospector.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,15 @@ const (
// The FS events then trigger either new Harvester runs or updates
// the statestore.
type fileProspector struct {
filewatcher loginp.FSWatcher
identifier fileIdentifier
ignoreOlder time.Duration
cleanRemoved bool
stateChangeCloser stateChangeCloserConfig
filewatcher loginp.FSWatcher
identifier fileIdentifier
ignoreOlder time.Duration
ignoreInactiveSince time.Time
cleanRemoved bool
stateChangeCloser stateChangeCloserConfig
}

func (p *fileProspector) Init(cleaner loginp.ProspectorCleaner) error {
func (p *fileProspector) Init(cleaner loginp.ProspectorCleaner, ignoreSince time.Time) error {
files := p.filewatcher.GetFiles()

if p.cleanRemoved {
Expand Down Expand Up @@ -82,6 +83,8 @@ func (p *fileProspector) Init(cleaner loginp.ProspectorCleaner) error {
return "", fm
})

p.ignoreInactiveSince = ignoreSince
Comment thread
urso marked this conversation as resolved.
Outdated

return nil
}

Expand Down Expand Up @@ -130,6 +133,12 @@ func (p *fileProspector) Run(ctx input.Context, s loginp.StateMetadataUpdater, h
break
}
}

if !p.ignoreInactiveSince.IsZero() && fe.Info.ModTime().Sub(p.ignoreInactiveSince) <= 0 {
log.Debugf("Ignore file because ignore_since.* reached time %v. File %s", p.ignoreInactiveSince, fe.NewPath)
break
}

hg.Start(ctx, src)

case loginp.OpTruncate:
Expand Down
4 changes: 2 additions & 2 deletions filebeat/input/filestream/prospector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func TestProspector_InitCleanIfRemoved(t *testing.T) {
cleanRemoved: testCase.cleanRemoved,
filewatcher: &mockFileWatcher{filesOnDisk: testCase.filesOnDisk},
}
p.Init(testStore)
p.Init(testStore, time.Time{})

assert.ElementsMatch(t, testCase.expectedCleanedKeys, testStore.cleanedKeys)
})
Expand Down Expand Up @@ -152,7 +152,7 @@ func TestProspector_InitUpdateIdentifiers(t *testing.T) {
identifier: mustPathIdentifier(false),
filewatcher: &mockFileWatcher{filesOnDisk: testCase.filesOnDisk},
}
p.Init(testStore)
p.Init(testStore, time.Time{})

assert.EqualValues(t, testCase.expectedUpdatedKeys, testStore.updatedKeys)
})
Expand Down
8 changes: 7 additions & 1 deletion libbeat/beat/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@

package beat

import "github.com/gofrs/uuid"
import (
"time"

"github.com/gofrs/uuid"
)

// Info stores a beats instance meta data.
type Info struct {
Expand All @@ -29,6 +33,8 @@ type Info struct {
Hostname string // hostname
ID uuid.UUID // ID assigned to beat machine
EphemeralID uuid.UUID // ID assigned to beat process invocation (PID)
FirstStart time.Time // The time of the first start of the Beat.
StartTime time.Time // The time of last start of the Beat. Updated when the Beat is started or restarted.

// Monitoring-related fields
Monitoring struct {
Expand Down
16 changes: 13 additions & 3 deletions libbeat/cmd/instance/beat.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,8 @@ func NewBeat(name, indexPrefix, v string, elasticLicensed bool) (*Beat, error) {
Name: hostname,
Hostname: hostname,
ID: id,
FirstStart: time.Now(),
StartTime: time.Now(),
EphemeralID: metrics.EphemeralID(),
},
Fields: fields,
Expand Down Expand Up @@ -695,7 +697,8 @@ func (b *Beat) configure(settings Settings) error {

func (b *Beat) loadMeta(metaPath string) error {
type meta struct {
UUID uuid.UUID `json:"uuid"`
UUID uuid.UUID `json:"uuid"`
FirstStart time.Time `json:"first_start"`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will need some testing. the stdlib JSON encoder/decoder might not play well with that type.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my manual tests, it worked like a charm. What might be the problem?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concern would be that timestamp serialization and parsing is not supported. If it works +1. We have some tests for loadMeta I think. Still would be nice to enhance the existing tests.

}

logp.Debug("beat", "Beat metadata path: %v", metaPath)
Expand All @@ -713,14 +716,21 @@ func (b *Beat) loadMeta(metaPath string) error {
}

f.Close()

if !m.FirstStart.IsZero() {
b.Info.FirstStart = m.FirstStart
}
valid := m.UUID != uuid.Nil
if valid {
b.Info.ID = m.UUID
}

if valid && !m.FirstStart.IsZero() {
return nil
}
}

// file does not exist or ID is invalid, let's create a new one
// file does not exist or ID is invalid or first start time is not defined, let's create a new one

// write temporary file first
tempFile := metaPath + ".new"
Expand All @@ -729,7 +739,7 @@ func (b *Beat) loadMeta(metaPath string) error {
return fmt.Errorf("Failed to create Beat meta file: %s", err)
}

encodeErr := json.NewEncoder(f).Encode(meta{UUID: b.Info.ID})
encodeErr := json.NewEncoder(f).Encode(meta{UUID: b.Info.ID, FirstStart: b.Info.FirstStart})
err = f.Sync()
if err != nil {
return fmt.Errorf("Beat meta file failed to write: %s", err)
Expand Down
5 changes: 5 additions & 0 deletions x-pack/filebeat/filebeat.reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2578,6 +2578,11 @@ filebeat.inputs:
# Time strings like 2h (2 hours), 5m (5 minutes) can be used.
#ignore_older: 0

# Ignore files that have not been updated since the selected event.
# ignore_inactive is disabled by default, so no files are ignored by setting it to "".
# Available options: since_first_start, since_last_start.
#ignore_inactive: ""

# Defines the buffer size every harvester uses when fetching the file
#harvester_buffer_size: 16384

Expand Down