mirror of
https://github.com/datarhei/core.git
synced 2025-09-26 20:11:29 +08:00
Compare commits
3 Commits
ea108a011f
...
logging
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4a12b0293f | ||
![]() |
f472fe150f | ||
![]() |
37e00407cc |
@@ -10,6 +10,7 @@ import (
|
||||
gonet "net"
|
||||
gohttp "net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
@@ -147,7 +148,12 @@ func (a *api) Reload() error {
|
||||
a.errorChan = make(chan error, 1)
|
||||
}
|
||||
|
||||
logger := log.New("Core").WithOutput(log.NewConsoleWriter(a.log.writer, log.Lwarn, true))
|
||||
logger := log.New("Core").WithOutput(
|
||||
log.NewLevelWriter(
|
||||
log.NewConsoleWriter(a.log.writer, true),
|
||||
log.Lwarn,
|
||||
),
|
||||
)
|
||||
|
||||
store, err := configstore.NewJSON(a.config.path, func() {
|
||||
a.errorChan <- ErrConfigReload
|
||||
@@ -183,31 +189,54 @@ func (a *api) Reload() error {
|
||||
break
|
||||
}
|
||||
|
||||
buffer := log.NewBufferWriter(loglevel, cfg.Log.MaxLines)
|
||||
buffer := log.NewBufferWriter(cfg.Log.MaxLines)
|
||||
var writer log.Writer
|
||||
|
||||
logger = logger.WithOutput(log.NewLevelRewriter(
|
||||
log.NewMultiWriter(
|
||||
log.NewTopicWriter(
|
||||
log.NewConsoleWriter(a.log.writer, loglevel, true),
|
||||
cfg.Log.Topics,
|
||||
),
|
||||
buffer,
|
||||
),
|
||||
[]log.LevelRewriteRule{
|
||||
// FFmpeg annoyance, move all warnings about unathorized access to memfs from ffmpeg to debug level
|
||||
// ts=2022-04-28T07:24:27Z level=WARN component="HTTP" address=":8080" client="::1" latency_ms=0 method="PUT" path="/memfs/00a10a69-416a-4cd5-9d4f-6d88ed3dd7f5_0917.ts" proto="HTTP/1.1" size_bytes=65 status=401 status_text="Unauthorized" user_agent="Lavf/58.76.100"
|
||||
{
|
||||
Level: log.Ldebug,
|
||||
Component: "HTTP",
|
||||
Match: map[string]string{
|
||||
"client": "^(::1|127.0.0.1)$",
|
||||
"method": "^(PUT|POST|DELETE)$",
|
||||
"status_text": "^Unauthorized$",
|
||||
"user_agent": "^Lavf/",
|
||||
if cfg.Log.Target.Output == "stdout" {
|
||||
writer = log.NewConsoleWriter(
|
||||
os.Stdout,
|
||||
true,
|
||||
)
|
||||
} else if cfg.Log.Target.Output == "file" {
|
||||
writer = log.NewFileWriter(
|
||||
cfg.Log.Target.Path,
|
||||
log.NewJSONFormatter(),
|
||||
)
|
||||
} else {
|
||||
writer = log.NewConsoleWriter(
|
||||
os.Stderr,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
logger = logger.WithOutput(
|
||||
log.NewLevelWriter(
|
||||
log.NewLevelRewriter(
|
||||
log.NewMultiWriter(
|
||||
log.NewTopicWriter(
|
||||
writer,
|
||||
cfg.Log.Topics,
|
||||
),
|
||||
buffer,
|
||||
),
|
||||
[]log.LevelRewriteRule{
|
||||
// FFmpeg annoyance, move all warnings about unathorized access to memfs from ffmpeg to debug level
|
||||
// ts=2022-04-28T07:24:27Z level=WARN component="HTTP" address=":8080" client="::1" latency_ms=0 method="PUT" path="/memfs/00a10a69-416a-4cd5-9d4f-6d88ed3dd7f5_0917.ts" proto="HTTP/1.1" size_bytes=65 status=401 status_text="Unauthorized" user_agent="Lavf/58.76.100"
|
||||
{
|
||||
Level: log.Ldebug,
|
||||
Component: "HTTP",
|
||||
Match: map[string]string{
|
||||
"client": "^(::1|127.0.0.1)$",
|
||||
"method": "^(PUT|POST|DELETE)$",
|
||||
"status_text": "^Unauthorized$",
|
||||
"user_agent": "^Lavf/",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
))
|
||||
),
|
||||
loglevel,
|
||||
),
|
||||
)
|
||||
|
||||
logfields := log.Fields{
|
||||
"application": app.Name,
|
||||
@@ -1297,4 +1326,6 @@ func (a *api) Destroy() {
|
||||
a.memfs.DeleteAll()
|
||||
a.memfs = nil
|
||||
}
|
||||
|
||||
a.log.logger.core.Close()
|
||||
}
|
||||
|
@@ -17,7 +17,12 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
logger := log.New("Migration").WithOutput(log.NewConsoleWriter(os.Stderr, log.Linfo, true)).WithFields(log.Fields{
|
||||
logger := log.New("Migration").WithOutput(
|
||||
log.NewLevelWriter(
|
||||
log.NewConsoleWriter(os.Stderr, true),
|
||||
log.Linfo,
|
||||
),
|
||||
).WithFields(log.Fields{
|
||||
"from": "ffmpeg4",
|
||||
"to": "ffmpeg5",
|
||||
})
|
||||
@@ -65,6 +70,27 @@ func doMigration(logger log.Logger, configstore cfgstore.Store) error {
|
||||
return fmt.Errorf("the configuration contains errors: %v", messages)
|
||||
}
|
||||
|
||||
var writer log.Writer
|
||||
|
||||
if cfg.Log.Target.Output == "stdout" {
|
||||
writer = log.NewConsoleWriter(
|
||||
os.Stdout,
|
||||
true,
|
||||
)
|
||||
} else if cfg.Log.Target.Output == "file" {
|
||||
writer = log.NewFileWriter(
|
||||
cfg.Log.Target.Path,
|
||||
log.NewJSONFormatter(),
|
||||
)
|
||||
} else {
|
||||
writer = log.NewConsoleWriter(
|
||||
os.Stderr,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
logger = logger.WithOutput(writer)
|
||||
|
||||
ff, err := ffmpeg.New(ffmpeg.Config{
|
||||
Binary: cfg.FFmpeg.Binary,
|
||||
})
|
||||
|
@@ -13,7 +13,12 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
logger := log.New("Import").WithOutput(log.NewConsoleWriter(os.Stderr, log.Linfo, true)).WithField("version", "v1")
|
||||
logger := log.New("Import").WithOutput(
|
||||
log.NewLevelWriter(
|
||||
log.NewConsoleWriter(os.Stderr, true),
|
||||
log.Linfo,
|
||||
),
|
||||
).WithField("version", "v1")
|
||||
|
||||
configfile := cfgstore.Location(os.Getenv("CORE_CONFIGFILE"))
|
||||
|
||||
@@ -33,8 +38,6 @@ func doImport(logger log.Logger, configstore cfgstore.Store) error {
|
||||
logger = log.New("")
|
||||
}
|
||||
|
||||
logger.Info().Log("Database import")
|
||||
|
||||
cfg := configstore.Get()
|
||||
|
||||
// Merging the persisted config with the environment variables
|
||||
@@ -60,6 +63,27 @@ func doImport(logger log.Logger, configstore cfgstore.Store) error {
|
||||
return fmt.Errorf("the configuration contains errors: %v", messages)
|
||||
}
|
||||
|
||||
var writer log.Writer
|
||||
|
||||
if cfg.Log.Target.Output == "stdout" {
|
||||
writer = log.NewConsoleWriter(
|
||||
os.Stdout,
|
||||
true,
|
||||
)
|
||||
} else if cfg.Log.Target.Output == "file" {
|
||||
writer = log.NewFileWriter(
|
||||
cfg.Log.Target.Path,
|
||||
log.NewJSONFormatter(),
|
||||
)
|
||||
} else {
|
||||
writer = log.NewConsoleWriter(
|
||||
os.Stderr,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
logger = logger.WithOutput(writer)
|
||||
|
||||
logger.Info().Log("Checking for database ...")
|
||||
|
||||
// Check if there's a v1.json from the old Restreamer
|
||||
|
@@ -141,6 +141,8 @@ func (d *Config) init() {
|
||||
d.vars.Register(value.NewString(&d.Log.Level, "info"), "log.level", "CORE_LOG_LEVEL", nil, "Loglevel: silent, error, warn, info, debug", false, false)
|
||||
d.vars.Register(value.NewStringList(&d.Log.Topics, []string{}, ","), "log.topics", "CORE_LOG_TOPICS", nil, "Show only selected log topics", false, false)
|
||||
d.vars.Register(value.NewInt(&d.Log.MaxLines, 1000), "log.max_lines", "CORE_LOG_MAXLINES", nil, "Number of latest log lines to keep in memory", false, false)
|
||||
d.vars.Register(value.NewString(&d.Log.Target.Output, "stderr"), "log.target.output", "CORE_LOG_TARGET_OUTPUT", nil, "Where to write the logs to: stdout, stderr, file", false, false)
|
||||
d.vars.Register(value.NewString(&d.Log.Target.Path, ""), "log.target.path", "CORE_LOG_TARGET_PATH", nil, "Path to log file if output is 'file'", false, false)
|
||||
|
||||
// DB
|
||||
d.vars.Register(value.NewMustDir(&d.DB.Dir, "./config"), "db.dir", "CORE_DB_DIR", nil, "Directory for holding the operational data", false, false)
|
||||
|
@@ -22,6 +22,10 @@ type Data struct {
|
||||
Level string `json:"level" enums:"debug,info,warn,error,silent" jsonschema:"enum=debug,enum=info,enum=warn,enum=error,enum=silent"`
|
||||
Topics []string `json:"topics"`
|
||||
MaxLines int `json:"max_lines"`
|
||||
Target struct {
|
||||
Output string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
} `json:"target"` // discard, stderr, stdout, file:/path/to/file.log
|
||||
} `json:"log"`
|
||||
DB struct {
|
||||
Dir string `json:"dir"`
|
||||
@@ -182,7 +186,6 @@ func MergeV2toV3(data *Data, d *v2.Data) (*Data, error) {
|
||||
data.Address = d.Address
|
||||
data.CheckForUpdates = d.CheckForUpdates
|
||||
|
||||
data.Log = d.Log
|
||||
data.DB = d.DB
|
||||
data.Host = d.Host
|
||||
data.API = d.API
|
||||
@@ -195,8 +198,6 @@ func MergeV2toV3(data *Data, d *v2.Data) (*Data, error) {
|
||||
data.Service = d.Service
|
||||
data.Router = d.Router
|
||||
|
||||
data.Log.Topics = copy.Slice(d.Log.Topics)
|
||||
|
||||
data.Host.Name = copy.Slice(d.Host.Name)
|
||||
|
||||
data.API.Access.HTTP.Allow = copy.Slice(d.API.Access.HTTP.Allow)
|
||||
@@ -228,6 +229,12 @@ func MergeV2toV3(data *Data, d *v2.Data) (*Data, error) {
|
||||
data.Storage.Memory = d.Storage.Memory
|
||||
|
||||
// Actual changes
|
||||
data.Log.Level = d.Log.Level
|
||||
data.Log.Topics = copy.Slice(d.Log.Topics)
|
||||
data.Log.MaxLines = d.Log.MaxLines
|
||||
data.Log.Target.Output = "stderr"
|
||||
data.Log.Target.Path = ""
|
||||
|
||||
data.Debug.Profiling = d.Debug.Profiling
|
||||
data.Debug.ForceGC = d.Debug.ForceGC
|
||||
data.Debug.MemoryLimit = 0
|
||||
@@ -263,7 +270,6 @@ func DowngradeV3toV2(d *Data) (*v2.Data, error) {
|
||||
data.Address = d.Address
|
||||
data.CheckForUpdates = d.CheckForUpdates
|
||||
|
||||
data.Log = d.Log
|
||||
data.DB = d.DB
|
||||
data.Host = d.Host
|
||||
data.API = d.API
|
||||
@@ -276,8 +282,6 @@ func DowngradeV3toV2(d *Data) (*v2.Data, error) {
|
||||
data.Service = d.Service
|
||||
data.Router = d.Router
|
||||
|
||||
data.Log.Topics = copy.Slice(d.Log.Topics)
|
||||
|
||||
data.Host.Name = copy.Slice(d.Host.Name)
|
||||
|
||||
data.API.Access.HTTP.Allow = copy.Slice(d.API.Access.HTTP.Allow)
|
||||
@@ -302,6 +306,10 @@ func DowngradeV3toV2(d *Data) (*v2.Data, error) {
|
||||
data.Router.Routes = copy.StringMap(d.Router.Routes)
|
||||
|
||||
// Actual changes
|
||||
data.Log.Level = d.Log.Level
|
||||
data.Log.Topics = copy.Slice(d.Log.Topics)
|
||||
data.Log.MaxLines = d.Log.MaxLines
|
||||
|
||||
data.Debug.Profiling = d.Debug.Profiling
|
||||
data.Debug.ForceGC = d.Debug.ForceGC
|
||||
|
||||
|
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
func (r *queryResolver) Log(ctx context.Context) ([]string, error) {
|
||||
if r.LogBuffer == nil {
|
||||
r.LogBuffer = log.NewBufferWriter(log.Lsilent, 1)
|
||||
r.LogBuffer = log.NewBufferWriter(1)
|
||||
}
|
||||
|
||||
events := r.LogBuffer.Events()
|
||||
|
@@ -22,7 +22,7 @@ func NewLog(buffer log.BufferWriter) *LogHandler {
|
||||
}
|
||||
|
||||
if l.buffer == nil {
|
||||
l.buffer = log.NewBufferWriter(log.Lsilent, 1)
|
||||
l.buffer = log.NewBufferWriter(1)
|
||||
}
|
||||
|
||||
return l
|
||||
|
50
log/log.go
50
log/log.go
@@ -14,28 +14,29 @@ import (
|
||||
type Level uint
|
||||
|
||||
const (
|
||||
Lsilent Level = 0
|
||||
Lerror Level = 1
|
||||
Lwarn Level = 2
|
||||
Linfo Level = 3
|
||||
Ldebug Level = 4
|
||||
Lsilent Level = 0b0000
|
||||
Lerror Level = 0b0001
|
||||
Lwarn Level = 0b0010
|
||||
Linfo Level = 0b0100
|
||||
Ldebug Level = 0b1000
|
||||
)
|
||||
|
||||
// String returns a string representing the log level.
|
||||
func (level Level) String() string {
|
||||
names := []string{
|
||||
"SILENT",
|
||||
"ERROR",
|
||||
"WARN",
|
||||
"INFO",
|
||||
"DEBUG",
|
||||
}
|
||||
|
||||
if level > Ldebug {
|
||||
switch level {
|
||||
case Lsilent:
|
||||
return "SILENT"
|
||||
case Lerror:
|
||||
return "ERROR"
|
||||
case Lwarn:
|
||||
return "WARN"
|
||||
case Linfo:
|
||||
return "INFO"
|
||||
case Ldebug:
|
||||
return "DEBUG"
|
||||
default:
|
||||
return `¯\_(ツ)_/¯`
|
||||
}
|
||||
|
||||
return names[level]
|
||||
}
|
||||
|
||||
func (level *Level) MarshalJSON() ([]byte, error) {
|
||||
@@ -97,6 +98,9 @@ type Logger interface {
|
||||
// Write implements the io.Writer interface such that it can be used in e.g. the
|
||||
// the log/Logger facility. Messages will be printed with debug level.
|
||||
Write(p []byte) (int, error)
|
||||
|
||||
// Close closes the underlying writer.
|
||||
Close()
|
||||
}
|
||||
|
||||
// logger is an implementation of the Logger interface.
|
||||
@@ -184,6 +188,10 @@ func (l *logger) Write(p []byte) (int, error) {
|
||||
return newEvent(l).Write(p)
|
||||
}
|
||||
|
||||
func (l *logger) Close() {
|
||||
l.output.Close()
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
logger *logger
|
||||
|
||||
@@ -352,12 +360,6 @@ func (l *Event) Write(p []byte) (int, error) {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
type Eventx struct {
|
||||
Time time.Time `json:"ts"`
|
||||
Level Level `json:"level"`
|
||||
Component string `json:"component"`
|
||||
Reference string `json:"ref"`
|
||||
Message string `json:"message"`
|
||||
Caller string `json:"caller"`
|
||||
Detail interface{} `json:"detail"`
|
||||
func (l *Event) Close() {
|
||||
l.logger.Close()
|
||||
}
|
||||
|
@@ -5,25 +5,25 @@ import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestLoglevelNames(t *testing.T) {
|
||||
assert.Equal(t, "DEBUG", Ldebug.String())
|
||||
assert.Equal(t, "ERROR", Lerror.String())
|
||||
assert.Equal(t, "WARN", Lwarn.String())
|
||||
assert.Equal(t, "INFO", Linfo.String())
|
||||
assert.Equal(t, `SILENT`, Lsilent.String())
|
||||
require.Equal(t, "DEBUG", Ldebug.String())
|
||||
require.Equal(t, "ERROR", Lerror.String())
|
||||
require.Equal(t, "WARN", Lwarn.String())
|
||||
require.Equal(t, "INFO", Linfo.String())
|
||||
require.Equal(t, `SILENT`, Lsilent.String())
|
||||
}
|
||||
|
||||
func TestLogColorToNotTTY(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
writer := bufio.NewWriter(&buffer)
|
||||
|
||||
w := NewConsoleWriter(writer, Linfo, true).(*syncWriter)
|
||||
w := NewLevelWriter(NewConsoleWriter(writer, true), Linfo).(*levelWriter).writer.(*syncWriter)
|
||||
formatter := w.writer.(*consoleWriter).formatter.(*consoleFormatter)
|
||||
|
||||
assert.NotEqual(t, true, formatter.color, "Color should not be used on a buffer logger")
|
||||
require.NotEqual(t, true, formatter.color, "Color should not be used on a buffer logger")
|
||||
}
|
||||
|
||||
func TestLogContext(t *testing.T) {
|
||||
@@ -31,7 +31,7 @@ func TestLogContext(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
writer := bufio.NewWriter(&buffer)
|
||||
|
||||
logger := New("component").WithOutput(NewConsoleWriter(writer, Ldebug, false))
|
||||
logger := New("component").WithOutput(NewLevelWriter(NewConsoleWriter(writer, false), Ldebug))
|
||||
|
||||
logger.Debug().Log("debug")
|
||||
logger.Info().Log("info")
|
||||
@@ -53,19 +53,19 @@ func TestLogContext(t *testing.T) {
|
||||
lenWithoutCtx := buffer.Len()
|
||||
buffer.Reset()
|
||||
|
||||
assert.Greater(t, lenWithCtx, lenWithoutCtx, "Log line length without context is not shorter than with context")
|
||||
require.Greater(t, lenWithCtx, lenWithoutCtx, "Log line length without context is not shorter than with context")
|
||||
}
|
||||
|
||||
func TestLogClone(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
writer := bufio.NewWriter(&buffer)
|
||||
|
||||
logger := New("test").WithOutput(NewConsoleWriter(writer, Linfo, false))
|
||||
logger := New("test").WithOutput(NewLevelWriter(NewConsoleWriter(writer, false), Linfo))
|
||||
|
||||
logger.Info().Log("info")
|
||||
writer.Flush()
|
||||
|
||||
assert.Contains(t, buffer.String(), `component="test"`)
|
||||
require.Contains(t, buffer.String(), `component="test"`)
|
||||
|
||||
buffer.Reset()
|
||||
|
||||
@@ -74,33 +74,33 @@ func TestLogClone(t *testing.T) {
|
||||
logger2.Info().Log("info")
|
||||
writer.Flush()
|
||||
|
||||
assert.Contains(t, buffer.String(), `component="tset"`)
|
||||
require.Contains(t, buffer.String(), `component="tset"`)
|
||||
}
|
||||
|
||||
func TestLogSilent(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
writer := bufio.NewWriter(&buffer)
|
||||
|
||||
logger := New("test").WithOutput(NewConsoleWriter(writer, Lsilent, false))
|
||||
logger := New("test").WithOutput(NewLevelWriter(NewConsoleWriter(writer, false), Lsilent))
|
||||
|
||||
logger.Debug().Log("debug")
|
||||
writer.Flush()
|
||||
assert.Equal(t, 0, buffer.Len(), "Buffer should be empty")
|
||||
require.Equal(t, 0, buffer.Len(), "Buffer should be empty")
|
||||
buffer.Reset()
|
||||
|
||||
logger.Info().Log("info")
|
||||
writer.Flush()
|
||||
assert.Equal(t, 0, buffer.Len(), "Buffer should be empty")
|
||||
require.Equal(t, 0, buffer.Len(), "Buffer should be empty")
|
||||
buffer.Reset()
|
||||
|
||||
logger.Warn().Log("warn")
|
||||
writer.Flush()
|
||||
assert.Equal(t, 0, buffer.Len(), "Buffer should be empty")
|
||||
require.Equal(t, 0, buffer.Len(), "Buffer should be empty")
|
||||
buffer.Reset()
|
||||
|
||||
logger.Error().Log("error")
|
||||
writer.Flush()
|
||||
assert.Equal(t, 0, buffer.Len(), "Buffer should be empty")
|
||||
require.Equal(t, 0, buffer.Len(), "Buffer should be empty")
|
||||
buffer.Reset()
|
||||
}
|
||||
|
||||
@@ -108,26 +108,26 @@ func TestLogDebug(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
writer := bufio.NewWriter(&buffer)
|
||||
|
||||
logger := New("test").WithOutput(NewConsoleWriter(writer, Ldebug, false))
|
||||
logger := New("test").WithOutput(NewLevelWriter(NewConsoleWriter(writer, false), Ldebug))
|
||||
|
||||
logger.Debug().Log("debug")
|
||||
writer.Flush()
|
||||
assert.NotEqual(t, 0, buffer.Len(), "Buffer should not be empty")
|
||||
require.NotEqual(t, 0, buffer.Len(), "Buffer should not be empty")
|
||||
buffer.Reset()
|
||||
|
||||
logger.Info().Log("info")
|
||||
writer.Flush()
|
||||
assert.NotEqual(t, 0, buffer.Len(), "Buffer should not be empty")
|
||||
require.NotEqual(t, 0, buffer.Len(), "Buffer should not be empty")
|
||||
buffer.Reset()
|
||||
|
||||
logger.Warn().Log("warn")
|
||||
writer.Flush()
|
||||
assert.NotEqual(t, 0, buffer.Len(), "Buffer should not be empty")
|
||||
require.NotEqual(t, 0, buffer.Len(), "Buffer should not be empty")
|
||||
buffer.Reset()
|
||||
|
||||
logger.Error().Log("error")
|
||||
writer.Flush()
|
||||
assert.NotEqual(t, 0, buffer.Len(), "Buffer should not be empty")
|
||||
require.NotEqual(t, 0, buffer.Len(), "Buffer should not be empty")
|
||||
buffer.Reset()
|
||||
}
|
||||
|
||||
@@ -135,26 +135,26 @@ func TestLogInfo(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
writer := bufio.NewWriter(&buffer)
|
||||
|
||||
logger := New("test").WithOutput(NewConsoleWriter(writer, Linfo, false))
|
||||
logger := New("test").WithOutput(NewLevelWriter(NewConsoleWriter(writer, false), Linfo))
|
||||
|
||||
logger.Debug().Log("debug")
|
||||
writer.Flush()
|
||||
assert.Equal(t, 0, buffer.Len(), "Buffer should be empty")
|
||||
require.Equal(t, 0, buffer.Len(), "Buffer should be empty")
|
||||
buffer.Reset()
|
||||
|
||||
logger.Info().Log("info")
|
||||
writer.Flush()
|
||||
assert.NotEqual(t, 0, buffer.Len(), "Buffer should not be empty")
|
||||
require.NotEqual(t, 0, buffer.Len(), "Buffer should not be empty")
|
||||
buffer.Reset()
|
||||
|
||||
logger.Warn().Log("warn")
|
||||
writer.Flush()
|
||||
assert.NotEqual(t, 0, buffer.Len(), "Buffer should not be empty")
|
||||
require.NotEqual(t, 0, buffer.Len(), "Buffer should not be empty")
|
||||
buffer.Reset()
|
||||
|
||||
logger.Error().Log("error")
|
||||
writer.Flush()
|
||||
assert.NotEqual(t, 0, buffer.Len(), "Buffer should not be empty")
|
||||
require.NotEqual(t, 0, buffer.Len(), "Buffer should not be empty")
|
||||
buffer.Reset()
|
||||
}
|
||||
|
||||
@@ -162,26 +162,26 @@ func TestLogWarn(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
writer := bufio.NewWriter(&buffer)
|
||||
|
||||
logger := New("test").WithOutput(NewConsoleWriter(writer, Lwarn, false))
|
||||
logger := New("test").WithOutput(NewLevelWriter(NewConsoleWriter(writer, false), Lwarn))
|
||||
|
||||
logger.Debug().Log("debug")
|
||||
writer.Flush()
|
||||
assert.Equal(t, 0, buffer.Len(), "Buffer should be empty")
|
||||
require.Equal(t, 0, buffer.Len(), "Buffer should be empty")
|
||||
buffer.Reset()
|
||||
|
||||
logger.Info().Log("info")
|
||||
writer.Flush()
|
||||
assert.Equal(t, 0, buffer.Len(), "Buffer should be empty")
|
||||
require.Equal(t, 0, buffer.Len(), "Buffer should be empty")
|
||||
buffer.Reset()
|
||||
|
||||
logger.Warn().Log("warn")
|
||||
writer.Flush()
|
||||
assert.NotEqual(t, 0, buffer.Len(), "Buffer should not be empty")
|
||||
require.NotEqual(t, 0, buffer.Len(), "Buffer should not be empty")
|
||||
buffer.Reset()
|
||||
|
||||
logger.Error().Log("error")
|
||||
writer.Flush()
|
||||
assert.NotEqual(t, 0, buffer.Len(), "Buffer should not be empty")
|
||||
require.NotEqual(t, 0, buffer.Len(), "Buffer should not be empty")
|
||||
buffer.Reset()
|
||||
}
|
||||
|
||||
@@ -189,25 +189,25 @@ func TestLogError(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
writer := bufio.NewWriter(&buffer)
|
||||
|
||||
logger := New("test").WithOutput(NewConsoleWriter(writer, Lerror, false))
|
||||
logger := New("test").WithOutput(NewLevelWriter(NewConsoleWriter(writer, false), Lerror))
|
||||
|
||||
logger.Debug().Log("debug")
|
||||
writer.Flush()
|
||||
assert.Equal(t, 0, buffer.Len(), "Buffer should be empty")
|
||||
require.Equal(t, 0, buffer.Len(), "Buffer should be empty")
|
||||
buffer.Reset()
|
||||
|
||||
logger.Info().Log("info")
|
||||
writer.Flush()
|
||||
assert.Equal(t, 0, buffer.Len(), "Buffer should be empty")
|
||||
require.Equal(t, 0, buffer.Len(), "Buffer should be empty")
|
||||
buffer.Reset()
|
||||
|
||||
logger.Warn().Log("warn")
|
||||
writer.Flush()
|
||||
assert.Equal(t, 0, buffer.Len(), "Buffer should be empty")
|
||||
require.Equal(t, 0, buffer.Len(), "Buffer should be empty")
|
||||
buffer.Reset()
|
||||
|
||||
logger.Error().Log("error")
|
||||
writer.Flush()
|
||||
assert.NotEqual(t, 0, buffer.Len(), "Buffer should not be empty")
|
||||
require.NotEqual(t, 0, buffer.Len(), "Buffer should not be empty")
|
||||
buffer.Reset()
|
||||
}
|
||||
|
43
log/output.go
Normal file
43
log/output.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/mattn/go-isatty"
|
||||
)
|
||||
|
||||
type consoleOutput struct {
|
||||
writer io.Writer
|
||||
formatter Formatter
|
||||
}
|
||||
|
||||
func NewConsoleOutput(w io.Writer, useColor bool) Writer {
|
||||
writer := &consoleOutput{
|
||||
writer: w,
|
||||
}
|
||||
|
||||
color := useColor
|
||||
|
||||
if color {
|
||||
if w, ok := w.(*os.File); ok {
|
||||
if !isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd()) {
|
||||
color = false
|
||||
}
|
||||
} else {
|
||||
color = false
|
||||
}
|
||||
}
|
||||
|
||||
writer.formatter = NewConsoleFormatter(color)
|
||||
|
||||
return NewSyncWriter(writer)
|
||||
}
|
||||
|
||||
func (w *consoleOutput) Write(e *Event) error {
|
||||
_, err := w.writer.Write(w.formatter.Bytes(e))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *consoleOutput) Close() {}
|
148
log/writer.go
148
log/writer.go
@@ -13,18 +13,50 @@ import (
|
||||
|
||||
type Writer interface {
|
||||
Write(e *Event) error
|
||||
Close()
|
||||
}
|
||||
|
||||
type discardWriter struct{}
|
||||
|
||||
func NewDiscardWriter() Writer {
|
||||
return &discardWriter{}
|
||||
}
|
||||
|
||||
func (w *discardWriter) Write(e *Event) error { return nil }
|
||||
func (w *discardWriter) Close() {}
|
||||
|
||||
type levelWriter struct {
|
||||
writer Writer
|
||||
level Level
|
||||
}
|
||||
|
||||
func NewLevelWriter(w Writer, level Level) Writer {
|
||||
return &levelWriter{
|
||||
writer: w,
|
||||
level: level,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *levelWriter) Write(e *Event) error {
|
||||
if w.level < e.Level || e.Level == Lsilent {
|
||||
return nil
|
||||
}
|
||||
|
||||
return w.writer.Write(e)
|
||||
}
|
||||
|
||||
func (w *levelWriter) Close() {
|
||||
w.writer.Close()
|
||||
}
|
||||
|
||||
type jsonWriter struct {
|
||||
writer io.Writer
|
||||
level Level
|
||||
formatter Formatter
|
||||
}
|
||||
|
||||
func NewJSONWriter(w io.Writer, level Level) Writer {
|
||||
func NewJSONWriter(w io.Writer) Writer {
|
||||
writer := &jsonWriter{
|
||||
writer: w,
|
||||
level: level,
|
||||
formatter: NewJSONFormatter(),
|
||||
}
|
||||
|
||||
@@ -32,25 +64,21 @@ func NewJSONWriter(w io.Writer, level Level) Writer {
|
||||
}
|
||||
|
||||
func (w *jsonWriter) Write(e *Event) error {
|
||||
if w.level < e.Level || e.Level == Lsilent {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err := w.writer.Write(w.formatter.Bytes(e))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *jsonWriter) Close() {}
|
||||
|
||||
type consoleWriter struct {
|
||||
writer io.Writer
|
||||
level Level
|
||||
formatter Formatter
|
||||
}
|
||||
|
||||
func NewConsoleWriter(w io.Writer, level Level, useColor bool) Writer {
|
||||
func NewConsoleWriter(w io.Writer, useColor bool) Writer {
|
||||
writer := &consoleWriter{
|
||||
writer: w,
|
||||
level: level,
|
||||
}
|
||||
|
||||
color := useColor
|
||||
@@ -71,15 +99,13 @@ func NewConsoleWriter(w io.Writer, level Level, useColor bool) Writer {
|
||||
}
|
||||
|
||||
func (w *consoleWriter) Write(e *Event) error {
|
||||
if w.level < e.Level || e.Level == Lsilent {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err := w.writer.Write(w.formatter.Bytes(e))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *consoleWriter) Close() {}
|
||||
|
||||
type topicWriter struct {
|
||||
writer Writer
|
||||
topics map[string]struct{}
|
||||
@@ -112,6 +138,10 @@ func (w *topicWriter) Write(e *Event) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *topicWriter) Close() {
|
||||
w.writer.Close()
|
||||
}
|
||||
|
||||
type levelRewriter struct {
|
||||
writer Writer
|
||||
rules []levelRewriteRule
|
||||
@@ -182,6 +212,10 @@ rules:
|
||||
return w.writer.Write(e)
|
||||
}
|
||||
|
||||
func (w *levelRewriter) Close() {
|
||||
w.writer.Close()
|
||||
}
|
||||
|
||||
type syncWriter struct {
|
||||
mu sync.Mutex
|
||||
writer Writer
|
||||
@@ -193,11 +227,15 @@ func NewSyncWriter(writer Writer) Writer {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *syncWriter) Write(e *Event) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
func (w *syncWriter) Write(e *Event) error {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
return s.writer.Write(e)
|
||||
return w.writer.Write(e)
|
||||
}
|
||||
|
||||
func (w *syncWriter) Close() {
|
||||
w.writer.Close()
|
||||
}
|
||||
|
||||
type multiWriter struct {
|
||||
@@ -212,8 +250,8 @@ func NewMultiWriter(writer ...Writer) Writer {
|
||||
return mw
|
||||
}
|
||||
|
||||
func (m *multiWriter) Write(e *Event) error {
|
||||
for _, w := range m.writer {
|
||||
func (w *multiWriter) Write(e *Event) error {
|
||||
for _, w := range w.writer {
|
||||
if err := w.Write(e); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -222,6 +260,12 @@ func (m *multiWriter) Write(e *Event) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *multiWriter) Close() {
|
||||
for _, w := range w.writer {
|
||||
w.Close()
|
||||
}
|
||||
}
|
||||
|
||||
type BufferWriter interface {
|
||||
Writer
|
||||
Events() []*Event
|
||||
@@ -230,13 +274,10 @@ type BufferWriter interface {
|
||||
type bufferWriter struct {
|
||||
lines *ring.Ring
|
||||
lock sync.RWMutex
|
||||
level Level
|
||||
}
|
||||
|
||||
func NewBufferWriter(level Level, lines int) BufferWriter {
|
||||
b := &bufferWriter{
|
||||
level: level,
|
||||
}
|
||||
func NewBufferWriter(lines int) BufferWriter {
|
||||
b := &bufferWriter{}
|
||||
|
||||
if lines > 0 {
|
||||
b.lines = ring.New(lines)
|
||||
@@ -245,33 +286,31 @@ func NewBufferWriter(level Level, lines int) BufferWriter {
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *bufferWriter) Write(e *Event) error {
|
||||
if b.level < e.Level || e.Level == Lsilent {
|
||||
return nil
|
||||
}
|
||||
func (w *bufferWriter) Write(e *Event) error {
|
||||
w.lock.Lock()
|
||||
defer w.lock.Unlock()
|
||||
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
if b.lines != nil {
|
||||
b.lines.Value = e.clone()
|
||||
b.lines = b.lines.Next()
|
||||
if w.lines != nil {
|
||||
w.lines.Value = e.clone()
|
||||
w.lines = w.lines.Next()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *bufferWriter) Events() []*Event {
|
||||
func (w *bufferWriter) Close() {}
|
||||
|
||||
func (w *bufferWriter) Events() []*Event {
|
||||
var lines = []*Event{}
|
||||
|
||||
if b.lines == nil {
|
||||
if w.lines == nil {
|
||||
return lines
|
||||
}
|
||||
|
||||
b.lock.RLock()
|
||||
defer b.lock.RUnlock()
|
||||
w.lock.RLock()
|
||||
defer w.lock.RUnlock()
|
||||
|
||||
b.lines.Do(func(l interface{}) {
|
||||
w.lines.Do(func(l interface{}) {
|
||||
if l == nil {
|
||||
return
|
||||
}
|
||||
@@ -281,3 +320,32 @@ func (b *bufferWriter) Events() []*Event {
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
type fileWriter struct {
|
||||
writer *os.File
|
||||
formatter Formatter
|
||||
}
|
||||
|
||||
func NewFileWriter(path string, formatter Formatter) Writer {
|
||||
file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_RDWR|os.O_SYNC, 0600)
|
||||
if err != nil {
|
||||
return NewDiscardWriter()
|
||||
}
|
||||
|
||||
writer := &fileWriter{
|
||||
writer: file,
|
||||
formatter: formatter,
|
||||
}
|
||||
|
||||
return NewSyncWriter(writer)
|
||||
}
|
||||
|
||||
func (w *fileWriter) Write(e *Event) error {
|
||||
_, err := w.writer.Write(append(w.formatter.Bytes(e), '\n'))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *fileWriter) Close() {
|
||||
w.writer.Close()
|
||||
}
|
||||
|
12
main.go
12
main.go
@@ -12,7 +12,15 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
logger := log.New("Core").WithOutput(log.NewConsoleWriter(os.Stderr, log.Lwarn, true))
|
||||
logger := log.New("Core").WithOutput(
|
||||
log.NewLevelWriter(
|
||||
log.NewConsoleWriter(
|
||||
os.Stderr,
|
||||
true,
|
||||
),
|
||||
log.Lwarn,
|
||||
),
|
||||
)
|
||||
|
||||
configfile := store.Location(os.Getenv("CORE_CONFIGFILE"))
|
||||
|
||||
@@ -54,6 +62,8 @@ func main() {
|
||||
signal.Notify(quit, os.Interrupt)
|
||||
<-quit
|
||||
|
||||
logger.Close()
|
||||
|
||||
// Stop the app
|
||||
app.Destroy()
|
||||
}
|
||||
|
Reference in New Issue
Block a user