File: logsource_file.go

package info (click to toggle)
prometheus-postfix-exporter 0.12.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 348 kB
  • sloc: sh: 78; makefile: 52
file content (94 lines) | stat: -rw-r--r-- 2,605 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package main

import (
	"context"
	"io"
	"log"

	"github.com/nxadm/tail"
	"gopkg.in/alecthomas/kingpin.v2"
)

var defaultConfig = tail.Config{
	ReOpen:    true,                               // reopen the file if it's rotated
	MustExist: true,                               // fail immediately if the file is missing or has incorrect permissions
	Follow:    true,                               // run in follow mode
	Location:  &tail.SeekInfo{Whence: io.SeekEnd}, // seek to end of file
	Logger:    tail.DiscardingLogger,
}

// A FileLogSource can read lines from a file.
type FileLogSource struct {
	tailer *tail.Tail
}

// NewFileLogSource creates a new log source, tailing the given file.
func NewFileLogSource(path string, config tail.Config) (*FileLogSource, error) {
	tailer, err := tail.TailFile(path, config)
	if err != nil {
		return nil, err
	}
	return &FileLogSource{tailer}, nil
}

func (s *FileLogSource) Close() error {
	defer s.tailer.Cleanup()
	go func() {
		// Stop() waits for the tailer goroutine to shut down, but it
		// can be blocking on sending on the Lines channel...
		for range s.tailer.Lines {
		}
	}()
	return s.tailer.Stop()
}

func (s *FileLogSource) Path() string {
	return s.tailer.Filename
}

func (s *FileLogSource) Read(ctx context.Context) (string, error) {
	select {
	case line, ok := <-s.tailer.Lines:
		if !ok {
			return "", io.EOF
		}
		return line.Text, nil
	case <-ctx.Done():
		return "", ctx.Err()
	}
}

// A fileLogSourceFactory is a factory than can create log sources
// from command line flags.
//
// Because this factory is enabled by default, it must always be
// registered last.
type fileLogSourceFactory struct {
	path      string
	mustExist bool
	debug     bool
}

func (f *fileLogSourceFactory) Init(app *kingpin.Application) {
	app.Flag("postfix.logfile_path", "Path where Postfix writes log entries.").Default("/var/log/mail.log").StringVar(&f.path)
	app.Flag("postfix.logfile_must_exist", "Fail if the log file doesn't exist.").Default("true").BoolVar(&f.mustExist)
	app.Flag("postfix.logfile_debug", "Enable debug logging for the log file.").Default("false").BoolVar(&f.debug)
}

// config returns a tail.Config configured from the factory's fields.
func (f fileLogSourceFactory) config() tail.Config {
	conf := defaultConfig
	conf.MustExist = f.mustExist
	if f.debug {
		conf.Logger = tail.DefaultLogger
	}
	return conf
}

func (f *fileLogSourceFactory) New(ctx context.Context) (LogSourceCloser, error) {
	if f.path == "" {
		return nil, nil
	}
	log.Printf("Reading log events from %s", f.path)
	return NewFileLogSource(f.path, f.config())
}