Add in process log rotation (#5778)
This commit is contained in:
committed by
Daniel Nelson
parent
bae7f59bbf
commit
bcf7516a23
@@ -1,12 +1,15 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/telegraf/internal"
|
||||
"github.com/influxdata/telegraf/internal/rotate"
|
||||
"github.com/influxdata/wlog"
|
||||
)
|
||||
|
||||
@@ -15,12 +18,32 @@ var prefixRegex = regexp.MustCompile("^[DIWE]!")
|
||||
// newTelegrafWriter returns a logging-wrapped writer.
|
||||
func newTelegrafWriter(w io.Writer) io.Writer {
|
||||
return &telegrafLog{
|
||||
writer: wlog.NewWriter(w),
|
||||
writer: wlog.NewWriter(w),
|
||||
internalWriter: w,
|
||||
}
|
||||
}
|
||||
|
||||
// LogConfig contains the log configuration settings
|
||||
type LogConfig struct {
|
||||
// will set the log level to DEBUG
|
||||
Debug bool
|
||||
//will set the log level to ERROR
|
||||
Quiet bool
|
||||
// will direct the logging output to a file. Empty string is
|
||||
// interpreted as stderr. If there is an error opening the file the
|
||||
// logger will fallback to stderr
|
||||
Logfile string
|
||||
// will rotate when current file at the specified time interval
|
||||
RotationInterval internal.Duration
|
||||
// will rotate when current file size exceeds this parameter.
|
||||
RotationMaxSize internal.Size
|
||||
// maximum rotated files to keep (older ones will be deleted)
|
||||
RotationMaxArchives int
|
||||
}
|
||||
|
||||
type telegrafLog struct {
|
||||
writer io.Writer
|
||||
writer io.Writer
|
||||
internalWriter io.Writer
|
||||
}
|
||||
|
||||
func (t *telegrafLog) Write(b []byte) (n int, err error) {
|
||||
@@ -33,31 +56,40 @@ func (t *telegrafLog) Write(b []byte) (n int, err error) {
|
||||
return t.writer.Write(line)
|
||||
}
|
||||
|
||||
func (t *telegrafLog) Close() error {
|
||||
closer, isCloser := t.internalWriter.(io.Closer)
|
||||
if !isCloser {
|
||||
return errors.New("the underlying writer cannot be closed")
|
||||
}
|
||||
return closer.Close()
|
||||
}
|
||||
|
||||
// SetupLogging configures the logging output.
|
||||
// debug will set the log level to DEBUG
|
||||
// quiet will set the log level to ERROR
|
||||
// logfile will direct the logging output to a file. Empty string is
|
||||
// interpreted as stderr. If there is an error opening the file the
|
||||
// logger will fallback to stderr.
|
||||
func SetupLogging(debug, quiet bool, logfile string) {
|
||||
func SetupLogging(config LogConfig) {
|
||||
newLogWriter(config)
|
||||
}
|
||||
|
||||
func newLogWriter(config LogConfig) io.Writer {
|
||||
log.SetFlags(0)
|
||||
if debug {
|
||||
if config.Debug {
|
||||
wlog.SetLevel(wlog.DEBUG)
|
||||
}
|
||||
if quiet {
|
||||
if config.Quiet {
|
||||
wlog.SetLevel(wlog.ERROR)
|
||||
}
|
||||
|
||||
var oFile *os.File
|
||||
if logfile != "" {
|
||||
var writer io.Writer
|
||||
if config.Logfile != "" {
|
||||
var err error
|
||||
if oFile, err = os.OpenFile(logfile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModeAppend|0644); err != nil {
|
||||
log.Printf("E! Unable to open %s (%s), using stderr", logfile, err)
|
||||
oFile = os.Stderr
|
||||
if writer, err = rotate.NewFileWriter(config.Logfile, config.RotationInterval.Duration, config.RotationMaxSize.Size, config.RotationMaxArchives); err != nil {
|
||||
log.Printf("E! Unable to open %s (%s), using stderr", config.Logfile, err)
|
||||
writer = os.Stderr
|
||||
}
|
||||
} else {
|
||||
oFile = os.Stderr
|
||||
writer = os.Stderr
|
||||
}
|
||||
|
||||
log.SetOutput(newTelegrafWriter(oFile))
|
||||
telegrafLog := newTelegrafWriter(writer)
|
||||
log.SetOutput(telegrafLog)
|
||||
return telegrafLog
|
||||
}
|
||||
|
||||
@@ -2,12 +2,16 @@ package logger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/influxdata/telegraf/internal"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestWriteLogToFile(t *testing.T) {
|
||||
@@ -15,7 +19,8 @@ func TestWriteLogToFile(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
defer func() { os.Remove(tmpfile.Name()) }()
|
||||
|
||||
SetupLogging(false, false, tmpfile.Name())
|
||||
config := createBasicLogConfig(tmpfile.Name())
|
||||
SetupLogging(config)
|
||||
log.Printf("I! TEST")
|
||||
log.Printf("D! TEST") // <- should be ignored
|
||||
|
||||
@@ -28,8 +33,9 @@ func TestDebugWriteLogToFile(t *testing.T) {
|
||||
tmpfile, err := ioutil.TempFile("", "")
|
||||
assert.NoError(t, err)
|
||||
defer func() { os.Remove(tmpfile.Name()) }()
|
||||
|
||||
SetupLogging(true, false, tmpfile.Name())
|
||||
config := createBasicLogConfig(tmpfile.Name())
|
||||
config.Debug = true
|
||||
SetupLogging(config)
|
||||
log.Printf("D! TEST")
|
||||
|
||||
f, err := ioutil.ReadFile(tmpfile.Name())
|
||||
@@ -41,8 +47,9 @@ func TestErrorWriteLogToFile(t *testing.T) {
|
||||
tmpfile, err := ioutil.TempFile("", "")
|
||||
assert.NoError(t, err)
|
||||
defer func() { os.Remove(tmpfile.Name()) }()
|
||||
|
||||
SetupLogging(false, true, tmpfile.Name())
|
||||
config := createBasicLogConfig(tmpfile.Name())
|
||||
config.Quiet = true
|
||||
SetupLogging(config)
|
||||
log.Printf("E! TEST")
|
||||
log.Printf("I! TEST") // <- should be ignored
|
||||
|
||||
@@ -55,8 +62,9 @@ func TestAddDefaultLogLevel(t *testing.T) {
|
||||
tmpfile, err := ioutil.TempFile("", "")
|
||||
assert.NoError(t, err)
|
||||
defer func() { os.Remove(tmpfile.Name()) }()
|
||||
|
||||
SetupLogging(true, false, tmpfile.Name())
|
||||
config := createBasicLogConfig(tmpfile.Name())
|
||||
config.Debug = true
|
||||
SetupLogging(config)
|
||||
log.Printf("TEST")
|
||||
|
||||
f, err := ioutil.ReadFile(tmpfile.Name())
|
||||
@@ -68,8 +76,9 @@ func TestWriteToTruncatedFile(t *testing.T) {
|
||||
tmpfile, err := ioutil.TempFile("", "")
|
||||
assert.NoError(t, err)
|
||||
defer func() { os.Remove(tmpfile.Name()) }()
|
||||
|
||||
SetupLogging(true, false, tmpfile.Name())
|
||||
config := createBasicLogConfig(tmpfile.Name())
|
||||
config.Debug = true
|
||||
SetupLogging(config)
|
||||
log.Printf("TEST")
|
||||
|
||||
f, err := ioutil.ReadFile(tmpfile.Name())
|
||||
@@ -87,6 +96,23 @@ func TestWriteToTruncatedFile(t *testing.T) {
|
||||
assert.Equal(t, f[19:], []byte("Z I! SHOULD BE FIRST\n"))
|
||||
}
|
||||
|
||||
func TestWriteToFileInRotation(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "LogRotation")
|
||||
require.NoError(t, err)
|
||||
config := createBasicLogConfig(filepath.Join(tempDir, "test.log"))
|
||||
config.RotationMaxSize = internal.Size{Size: int64(30)}
|
||||
writer := newLogWriter(config)
|
||||
// Close the writer here, otherwise the temp folder cannot be deleted because the current log file is in use.
|
||||
closer, isCloser := writer.(io.Closer)
|
||||
assert.True(t, isCloser)
|
||||
defer func() { closer.Close(); os.RemoveAll(tempDir) }()
|
||||
|
||||
log.Printf("I! TEST 1") // Writes 31 bytes, will rotate
|
||||
log.Printf("I! TEST") // Writes 29 byes, no rotation expected
|
||||
files, _ := ioutil.ReadDir(tempDir)
|
||||
assert.Equal(t, 2, len(files))
|
||||
}
|
||||
|
||||
func BenchmarkTelegrafLogWrite(b *testing.B) {
|
||||
var msg = []byte("test")
|
||||
var buf bytes.Buffer
|
||||
@@ -96,3 +122,10 @@ func BenchmarkTelegrafLogWrite(b *testing.B) {
|
||||
w.Write(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func createBasicLogConfig(filename string) LogConfig {
|
||||
return LogConfig{
|
||||
Logfile: filename,
|
||||
RotationMaxArchives: -1,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user