197 lines
4.6 KiB
Go
197 lines
4.6 KiB
Go
// 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.
|
|
|
|
package input
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/gofrs/uuid"
|
|
|
|
"github.com/elastic/beats/journalbeat/checkpoint"
|
|
"github.com/elastic/beats/journalbeat/reader"
|
|
"github.com/elastic/beats/libbeat/beat"
|
|
"github.com/elastic/beats/libbeat/common"
|
|
"github.com/elastic/beats/libbeat/logp"
|
|
"github.com/elastic/beats/libbeat/processors"
|
|
)
|
|
|
|
// Input manages readers and forwards entries from journals.
|
|
type Input struct {
|
|
readers []*reader.Reader
|
|
done chan struct{}
|
|
config Config
|
|
pipeline beat.Pipeline
|
|
states map[string]checkpoint.JournalState
|
|
id uuid.UUID
|
|
logger *logp.Logger
|
|
eventMeta common.EventMetadata
|
|
processors beat.ProcessorList
|
|
}
|
|
|
|
// New returns a new Inout
|
|
func New(
|
|
c *common.Config,
|
|
pipeline beat.Pipeline,
|
|
done chan struct{},
|
|
states map[string]checkpoint.JournalState,
|
|
) (*Input, error) {
|
|
config := DefaultConfig
|
|
if err := c.Unpack(&config); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
id, err := uuid.NewV4()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error while generating ID for input: %v", err)
|
|
}
|
|
|
|
logger := logp.NewLogger("input").With("id", id)
|
|
|
|
var readers []*reader.Reader
|
|
if len(config.Paths) == 0 {
|
|
cfg := reader.Config{
|
|
Path: reader.LocalSystemJournalID, // used to identify the state in the registry
|
|
Backoff: config.Backoff,
|
|
MaxBackoff: config.MaxBackoff,
|
|
Seek: config.Seek,
|
|
Matches: config.Matches,
|
|
}
|
|
|
|
state := states[reader.LocalSystemJournalID]
|
|
r, err := reader.NewLocal(cfg, done, state, logger)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error creating reader for local journal: %v", err)
|
|
}
|
|
readers = append(readers, r)
|
|
}
|
|
|
|
for _, p := range config.Paths {
|
|
cfg := reader.Config{
|
|
Path: p,
|
|
Backoff: config.Backoff,
|
|
MaxBackoff: config.MaxBackoff,
|
|
Seek: config.Seek,
|
|
Matches: config.Matches,
|
|
}
|
|
state := states[p]
|
|
r, err := reader.New(cfg, done, state, logger)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error creating reader for journal: %v", err)
|
|
}
|
|
readers = append(readers, r)
|
|
}
|
|
|
|
processors, err := processors.New(config.Processors)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
logger.Debugf("New input is created for paths %v", config.Paths)
|
|
|
|
return &Input{
|
|
readers: readers,
|
|
done: done,
|
|
config: config,
|
|
pipeline: pipeline,
|
|
states: states,
|
|
id: id,
|
|
logger: logger,
|
|
eventMeta: config.EventMetadata,
|
|
processors: processors,
|
|
}, nil
|
|
}
|
|
|
|
// Run connects to the output, collects entries from the readers
|
|
// and then publishes the events.
|
|
func (i *Input) Run() {
|
|
client, err := i.pipeline.ConnectWith(beat.ClientConfig{
|
|
PublishMode: beat.GuaranteedSend,
|
|
EventMetadata: i.eventMeta,
|
|
Meta: nil,
|
|
Processor: i.processors,
|
|
ACKCount: func(n int) {
|
|
i.logger.Infof("journalbeat successfully published %d events", n)
|
|
},
|
|
})
|
|
if err != nil {
|
|
i.logger.Error("Error connecting to output: %v", err)
|
|
return
|
|
}
|
|
defer client.Close()
|
|
|
|
i.publishAll(client)
|
|
}
|
|
|
|
// publishAll reads events from all readers and publishes them.
|
|
func (i *Input) publishAll(client beat.Client) {
|
|
out := make(chan *beat.Event)
|
|
defer close(out)
|
|
|
|
var wg sync.WaitGroup
|
|
defer wg.Wait()
|
|
for _, r := range i.readers {
|
|
wg.Add(1)
|
|
r := r
|
|
go func() {
|
|
defer wg.Done()
|
|
|
|
for {
|
|
select {
|
|
case <-i.done:
|
|
return
|
|
default:
|
|
}
|
|
|
|
event, err := r.Next()
|
|
if event == nil {
|
|
if err != nil {
|
|
i.logger.Errorf("Error while reading event: %v", err)
|
|
}
|
|
continue
|
|
}
|
|
|
|
select {
|
|
case <-i.done:
|
|
case out <- event:
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
for {
|
|
select {
|
|
case <-i.done:
|
|
return
|
|
case e := <-out:
|
|
client.Publish(*e)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Stop stops all readers of the input.
|
|
func (i *Input) Stop() {
|
|
for _, r := range i.readers {
|
|
r.Close()
|
|
}
|
|
}
|
|
|
|
// Wait waits until all readers are done.
|
|
func (i *Input) Wait() {
|
|
i.Stop()
|
|
}
|