This commit is contained in:
bel
2020-01-13 03:37:51 +00:00
commit c8eb52f9ba
2023 changed files with 702080 additions and 0 deletions

460
.rclone_repo/vendor/github.com/pengsrc/go-shared/log/event.go generated vendored Executable file
View File

@@ -0,0 +1,460 @@
package log
import (
"context"
"fmt"
"os"
"runtime"
"strconv"
"strings"
"sync"
"time"
"github.com/pengsrc/go-shared/buffer"
"github.com/pengsrc/go-shared/convert"
)
// A EventCallerPool is a type-safe wrapper around a sync.BytesBufferPool.
type EventCallerPool struct {
p *sync.Pool
}
// NewEventCallerPool constructs a new BytesBufferPool.
func NewEventCallerPool() EventCallerPool {
return EventCallerPool{
p: &sync.Pool{
New: func() interface{} {
return &EventCaller{}
},
},
}
}
// Get retrieves a EventCaller from the pool, creating one if necessary.
func (p EventCallerPool) Get() *EventCaller {
e := p.p.Get().(*EventCaller)
e.pool = p
e.Defined = false
e.PC = 0
e.File = ""
e.Line = 0
return e
}
func (p EventCallerPool) put(caller *EventCaller) {
p.p.Put(caller)
}
// EventCaller represents the caller of a logging function.
type EventCaller struct {
pool EventCallerPool
Defined bool
PC uintptr
File string
Line int
}
// Free returns the EventCaller to its EventCallerPool.
// Callers must not retain references to the EventCaller after calling Free.
func (ec *EventCaller) Free() {
ec.pool.put(ec)
}
// String returns the full path and line number of the caller.
func (ec EventCaller) String() string {
return ec.FullPath()
}
// FullPath returns a /full/path/to/package/file:line description of the
// caller.
func (ec EventCaller) FullPath() string {
if !ec.Defined {
return "undefined"
}
buf := buffer.GlobalBytesPool().Get()
defer buf.Free()
buf.AppendString(ec.File)
buf.AppendByte(':')
buf.AppendInt(int64(ec.Line))
return buf.String()
}
// TrimmedPath returns a package/file:line description of the caller,
// preserving only the leaf directory name and file name.
func (ec EventCaller) TrimmedPath() string {
if !ec.Defined {
return "undefined"
}
// nb. To make sure we trim the path correctly on Windows too, we
// counter-intuitively need to use '/' and *not* os.PathSeparator here,
// because the path given originates from Go stdlib, specifically
// runtime.Caller() which (as of Mar/17) returns forward slashes even on
// Windows.
//
// See https://github.com/golang/go/issues/3335
// and https://github.com/golang/go/issues/18151
//
// for discussion on the issue on Go side.
//
// Find the last separator.
//
idx := strings.LastIndexByte(ec.File, '/')
if idx == -1 {
return ec.FullPath()
}
// Find the penultimate separator.
idx = strings.LastIndexByte(ec.File[:idx], '/')
if idx == -1 {
return ec.FullPath()
}
buf := buffer.GlobalBytesPool().Get()
defer buf.Free()
// Keep everything after the penultimate separator.
buf.AppendString(ec.File[idx+1:])
buf.AppendByte(':')
buf.AppendInt(int64(ec.Line))
return buf.String()
}
// A EventPool is a type-safe wrapper around a sync.BytesBufferPool.
type EventPool struct {
p *sync.Pool
}
// NewEventPool constructs a new BytesBufferPool.
func NewEventPool() EventPool {
return EventPool{
p: &sync.Pool{
New: func() interface{} {
return &Event{}
},
},
}
}
// Get retrieves a Event from the pool, creating one if necessary.
func (p EventPool) Get() *Event {
e := p.p.Get().(*Event)
e.buffer = buffer.GlobalBytesPool().Get()
e.pool = p
e.level = MuteLevel
e.lw = nil
e.ctx = nil
e.ctxKeys = nil
e.isEnabled = false
e.isCallerEnabled = false
return e
}
func (p EventPool) put(event *Event) {
event.buffer.Free()
event.ctx = nil
event.ctxKeys = nil
p.p.Put(event)
}
// Event represents a log event. It is instanced by one of the with method of
// logger and finalized by the log method such as Debug().
type Event struct {
buffer *buffer.BytesBuffer
pool EventPool
level Level
lw LevelWriter
ctx context.Context
ctxKeys *[]interface{}
ctxKeysMap *map[interface{}]string
isEnabled bool
isCallerEnabled bool
}
// Free returns the Event to its EventPool.
// Callers must not retain references to the Event after calling Free.
func (e *Event) Free() {
e.pool.put(e)
}
// Message writes the *Event to level writer.
//
// NOTICE: Once this method is called, the *Event should be disposed.
// Calling twice can have unexpected result.
func (e *Event) Message(message string) {
if !e.isEnabled {
return
}
e.write(message)
}
// Messagef writes the *Event to level writer.
//
// NOTICE: Once this method is called, the *Event should be disposed.
// Calling twice can have unexpected result.
func (e *Event) Messagef(format string, v ...interface{}) {
if !e.isEnabled {
return
}
e.write(format, v...)
}
// Byte appends string key and byte value to event.
func (e *Event) Byte(key string, value byte) *Event {
return e.appendField(key, func() { e.buffer.AppendByte(value) })
}
// Bytes appends string key and bytes value to event.
func (e *Event) Bytes(key string, value []byte) *Event {
return e.appendField(key, func() {
if needsQuote(string(value)) {
e.buffer.AppendString(strconv.Quote(string(value)))
} else {
e.buffer.AppendBytes(value)
}
})
}
// String appends string key and string value to event.
func (e *Event) String(key string, value string) *Event {
return e.appendField(key, func() {
if needsQuote(string(value)) {
e.buffer.AppendString(strconv.Quote(value))
} else {
e.buffer.AppendString(value)
}
})
}
// Int appends string key and int value to event.
func (e *Event) Int(key string, value int) *Event {
return e.appendField(key, func() { e.buffer.AppendInt(int64(value)) })
}
// Int32 appends string key and int32 value to event.
func (e *Event) Int32(key string, value int32) *Event {
return e.appendField(key, func() { e.buffer.AppendInt(int64(value)) })
}
// Int64 appends string key and int64 value to event.
func (e *Event) Int64(key string, value int64) *Event {
return e.appendField(key, func() { e.buffer.AppendInt(value) })
}
// Uint appends string key and uint value to event.
func (e *Event) Uint(key string, value uint) *Event {
return e.appendField(key, func() { e.buffer.AppendUint(uint64(value)) })
}
// Uint32 appends string key and uint32 value to event.
func (e *Event) Uint32(key string, value uint32) *Event {
return e.appendField(key, func() { e.buffer.AppendUint(uint64(value)) })
}
// Uint64 appends string key and uint64 value to event.
func (e *Event) Uint64(key string, value uint64) *Event {
return e.appendField(key, func() { e.buffer.AppendUint(value) })
}
// Float32 appends string key and float32 value to event.
func (e *Event) Float32(key string, value float32) *Event {
return e.appendField(key, func() { e.buffer.AppendFloat(float64(value), 32) })
}
// Float64 appends string key and float value to event.
func (e *Event) Float64(key string, value float64) *Event {
return e.appendField(key, func() { e.buffer.AppendFloat(value, 64) })
}
// Bool appends string key and bool value to event.
func (e *Event) Bool(key string, value bool) *Event {
return e.appendField(key, func() { e.buffer.AppendBool(value) })
}
// Time appends string key and time value to event.
func (e *Event) Time(key string, value time.Time, format string) *Event {
buf := buffer.GlobalBytesPool().Get()
defer buf.Free()
buf.AppendTime(value, format)
return e.Bytes(key, buf.Bytes())
}
// Error appends string key and error value to event.
func (e *Event) Error(key string, err error) *Event {
return e.String(key, err.Error())
}
// Interface appends string key and interface value to event.
func (e *Event) Interface(key string, value interface{}) *Event {
switch v := value.(type) {
case byte:
e.Byte(key, v)
case []byte:
e.Bytes(key, v)
case string:
e.String(key, v)
case int:
e.Int(key, v)
case int32:
e.Int32(key, v)
case int64:
e.Int64(key, v)
case uint:
e.Uint(key, v)
case uint32:
e.Uint32(key, v)
case uint64:
e.Uint64(key, v)
case float32:
e.Float32(key, v)
case float64:
e.Float64(key, v)
case bool:
e.Bool(key, v)
case time.Time:
e.Time(key, v, convert.ISO8601Milli)
case error:
e.Error(key, v)
case nil:
e.String(key, "nil")
default:
panic(fmt.Sprintf("unknown field type: %v", value))
}
return e
}
func (e *Event) appendField(key string, appendFunc func()) *Event {
if !e.isEnabled {
return e
}
// Ignore field with empty key.
if len(key) <= 0 {
return e
}
// Append space if event field not empty.
if e.buffer.Len() != 0 {
e.buffer.AppendByte(' ')
}
e.buffer.AppendString(key)
e.buffer.AppendString("=")
appendFunc()
return e
}
func (e *Event) write(format string, v ...interface{}) {
defer e.Free()
if !e.isEnabled {
return
}
// Append interested contexts.
if e.ctx != nil && e.ctxKeys != nil && e.ctxKeysMap != nil {
for _, key := range *e.ctxKeys {
if value := e.ctx.Value(key); value != nil {
e.Interface((*e.ctxKeysMap)[key], e.ctx.Value(key))
}
}
}
// Append caller.
if e.isCallerEnabled {
ec := newEventCaller(runtime.Caller(callerSkipOffset))
e.String("source", ec.TrimmedPath())
}
// Compose and store current log.
buf := buffer.GlobalBytesPool().Get()
defer buf.Free()
// Format print message.
if format != "" {
if len(v) == 0 {
fmt.Fprint(buf, format)
} else {
fmt.Fprintf(buf, format, v...)
}
} else {
fmt.Fprint(buf, v...)
}
// Append filed.
buf.AppendByte(' ')
buf.AppendBytes(e.buffer.Bytes())
// Finally write.
if _, err := e.lw.WriteLevel(e.level, buf.Bytes()); err != nil {
fmt.Fprintf(os.Stderr, "log: could not write event: %v", err)
}
switch e.level {
case PanicLevel:
panic(buf.String())
case FatalLevel:
os.Exit(1)
}
}
func newEventCaller(pc uintptr, file string, line int, ok bool) (ec *EventCaller) {
ec = eventCallerPool.Get()
if ok {
ec.PC = pc
ec.File = file
ec.Line = line
ec.Defined = true
}
return
}
func newEvent(
ctx context.Context,
ctxKeys *[]interface{}, ctxKeysMap *map[interface{}]string,
level Level, lw LevelWriter,
isEnabled bool, isCallerEnabled bool,
) (e *Event) {
e = eventPool.Get()
e.level = level
e.lw = lw
e.ctx = ctx
e.ctxKeys = ctxKeys
e.ctxKeysMap = ctxKeysMap
e.isEnabled = isEnabled
e.isCallerEnabled = isCallerEnabled
return
}
func needsQuote(s string) bool {
for i := range s {
if s[i] < 0x20 || s[i] > 0x7e || s[i] == ' ' || s[i] == '\\' || s[i] == '"' {
return true
}
}
return false
}
const callerSkipOffset = 2
// eventCallerPool is a pool of newEvent callers.
var eventCallerPool = NewEventCallerPool()
// eventPool is a pool of events.
var eventPool = NewEventPool()

View File

@@ -0,0 +1,151 @@
package log
import (
"context"
)
// Fatal logs a message with severity FATAL followed by a call to os.Exit(1).
func Fatal(ctx context.Context, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, FatalLevel).write("", v...)
}
}
// Panic logs a message with severity PANIC followed by a call to panic().
func Panic(ctx context.Context, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, PanicLevel).write("", v...)
}
}
// Error logs a message with severity ERROR.
func Error(ctx context.Context, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, ErrorLevel).write("", v...)
}
}
// Warn logs a message with severity WARN.
func Warn(ctx context.Context, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, WarnLevel).write("", v...)
}
}
// Info logs a message with severity INFO.
func Info(ctx context.Context, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, InfoLevel).write("", v...)
}
}
// Debug logs a message with severity DEBUG.
func Debug(ctx context.Context, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, DebugLevel).write("", v...)
}
}
// Fatalf logs a message with severity FATAL in format followed by a call to
// os.Exit(1).
func Fatalf(ctx context.Context, format string, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, FatalLevel).write(format, v...)
}
}
// Panicf logs a message with severity PANIC in format followed by a call to
// panic().
func Panicf(ctx context.Context, format string, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, PanicLevel).write(format, v...)
}
}
// Errorf logs a message with severity ERROR in format.
func Errorf(ctx context.Context, format string, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, ErrorLevel).write(format, v...)
}
}
// Warnf logs a message with severity WARN in format.
func Warnf(ctx context.Context, format string, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, WarnLevel).write(format, v...)
}
}
// Infof logs a message with severity INFO in format.
func Infof(ctx context.Context, format string, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, InfoLevel).write(format, v...)
}
}
// Debugf logs a message with severity DEBUG in format.
func Debugf(ctx context.Context, format string, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, DebugLevel).write(format, v...)
}
}
// FatalEvent returns a log event with severity FATAL.
func FatalEvent(ctx context.Context) *Event {
if globalLogger != nil {
return globalLogger.event(ctx, FatalLevel)
}
return nil
}
// PanicEvent returns a log event with severity PANIC.
func PanicEvent(ctx context.Context) *Event {
if globalLogger != nil {
return globalLogger.event(ctx, PanicLevel)
}
return nil
}
// ErrorEvent returns a log event with severity ERROR.
func ErrorEvent(ctx context.Context) *Event {
if globalLogger != nil {
return globalLogger.event(ctx, ErrorLevel)
}
return nil
}
// WarnEvent returns a log event with severity WARN.
func WarnEvent(ctx context.Context) *Event {
if globalLogger != nil {
return globalLogger.event(ctx, WarnLevel)
}
return nil
}
// InfoEvent returns a log event with severity INFO.
func InfoEvent(ctx context.Context) *Event {
if globalLogger != nil {
return globalLogger.event(ctx, InfoLevel)
}
return nil
}
// DebugEvent returns a log event with severity DEBUG.
func DebugEvent(ctx context.Context) *Event {
if globalLogger != nil {
return globalLogger.event(ctx, DebugLevel)
}
return nil
}
// SetGlobalLogger sets a logger as global logger.
func SetGlobalLogger(l *Logger) {
globalLogger = l
}
// GlobalLogger returns the global logger.
func GlobalLogger() *Logger {
return globalLogger
}
var globalLogger *Logger

View File

@@ -0,0 +1,67 @@
package log
import (
"fmt"
"strings"
)
// Level defines log levels.
type Level uint8
// String returns name of the level.
func (l Level) String() string {
switch l {
case MuteLevel:
return "MUTE"
case FatalLevel:
return "FATAL"
case PanicLevel:
return "PANIC"
case ErrorLevel:
return "ERROR"
case WarnLevel:
return "WARN"
case InfoLevel:
return "INFO"
case DebugLevel:
return "DEBUG"
}
return ""
}
const (
// MuteLevel disables the logger.
MuteLevel Level = iota
// FatalLevel defines fatal log level.
FatalLevel
// PanicLevel defines panic log level.
PanicLevel
// ErrorLevel defines error log level.
ErrorLevel
// WarnLevel defines warn log level.
WarnLevel
// InfoLevel defines info log level.
InfoLevel
// DebugLevel defines debug log level.
DebugLevel
)
// ParseLevel takes a string level and returns the log level constant.
func ParseLevel(level string) (Level, error) {
switch strings.ToUpper(level) {
case "FATAL":
return FatalLevel, nil
case "PANIC":
return PanicLevel, nil
case "ERROR":
return ErrorLevel, nil
case "WARN":
return WarnLevel, nil
case "INFO":
return InfoLevel, nil
case "DEBUG":
return DebugLevel, nil
}
return MuteLevel, fmt.Errorf(`"%q" is not a valid log Level`, level)
}

View File

@@ -0,0 +1,327 @@
// Package log provides support for logging to stdout, stderr and file.
package log
import (
"bufio"
"context"
"errors"
"fmt"
"io"
"os"
"os/signal"
"path"
"syscall"
"time"
"github.com/pengsrc/go-shared/check"
"github.com/pengsrc/go-shared/reopen"
)
// Logger presents a logger.
// The only way to initialize a logger is using the convention construct
// functions like NewLogger().
type Logger struct {
level Level
lw LevelWriter
// Interested context keys.
ctxKeys []interface{}
ctxKeysMap map[interface{}]string
// isCallerEnabled sets whether to annotating logs with the calling
// function's file name and line number. By default, all logs are annotated.
isCallerEnabled bool
}
// GetLevel get the log level string.
func (l *Logger) GetLevel() string {
return l.level.String()
}
// SetLevel sets the log level.
// Valid levels are "debug", "info", "warn", "error", and "fatal".
func (l *Logger) SetLevel(level string) (err error) {
levelFlag, err := ParseLevel(level)
if err == nil {
l.level = levelFlag
}
return err
}
// SetInterestContextKeys sets the contexts keys that the logger should be
// interested in. Value of the interested context key will extract and print as
// newEvent filed.
func (l *Logger) SetInterestContextKeys(keys []interface{}) {
l.ctxKeys = keys
l.ctxKeysMap = make(map[interface{}]string)
for _, key := range l.ctxKeys {
l.ctxKeysMap[key] = fmt.Sprintf("%v", key)
}
}
// SetCallerFlag sets whether to annotating logs with the caller.
func (l *Logger) SetCallerFlag(isEnabled bool) {
l.isCallerEnabled = isEnabled
}
// Flush writes buffered logs.
func (l *Logger) Flush() {
if flusher, ok := l.lw.(Flusher); ok {
flusher.Flush()
}
}
// Fatal logs a message with severity FATAL followed by a call to os.Exit(1).
func (l *Logger) Fatal(ctx context.Context, v ...interface{}) {
l.event(ctx, FatalLevel).write("", v...)
}
// Panic logs a message with severity PANIC followed by a call to panic().
func (l *Logger) Panic(ctx context.Context, v ...interface{}) {
l.event(ctx, PanicLevel).write("", v...)
}
// Error logs a message with severity ERROR.
func (l *Logger) Error(ctx context.Context, v ...interface{}) {
l.event(ctx, ErrorLevel).write("", v...)
}
// Warn logs a message with severity WARN.
func (l *Logger) Warn(ctx context.Context, v ...interface{}) {
l.event(ctx, WarnLevel).write("", v...)
}
// Info logs a message with severity INFO.
func (l *Logger) Info(ctx context.Context, v ...interface{}) {
l.event(ctx, InfoLevel).write("", v...)
}
// Debug logs a message with severity DEBUG.
func (l *Logger) Debug(ctx context.Context, v ...interface{}) {
l.event(ctx, DebugLevel).write("", v...)
}
// Fatalf logs a message with severity FATAL in format followed by a call to
// os.Exit(1).
func (l *Logger) Fatalf(ctx context.Context, format string, v ...interface{}) {
l.event(ctx, FatalLevel).write(format, v...)
}
// Panicf logs a message with severity PANIC in format followed by a call to
// panic().
func (l *Logger) Panicf(ctx context.Context, format string, v ...interface{}) {
l.event(ctx, PanicLevel).write(format, v...)
}
// Errorf logs a message with severity ERROR in format.
func (l *Logger) Errorf(ctx context.Context, format string, v ...interface{}) {
l.event(ctx, ErrorLevel).write(format, v...)
}
// Warnf logs a message with severity WARN in format.
func (l *Logger) Warnf(ctx context.Context, format string, v ...interface{}) {
l.event(ctx, WarnLevel).write(format, v...)
}
// Infof logs a message with severity INFO in format.
func (l *Logger) Infof(ctx context.Context, format string, v ...interface{}) {
l.event(ctx, InfoLevel).write(format, v...)
}
// Debugf logs a message with severity DEBUG in format.
func (l *Logger) Debugf(ctx context.Context, format string, v ...interface{}) {
l.event(ctx, DebugLevel).write(format, v...)
}
// FatalEvent returns a log event with severity FATAL.
func (l *Logger) FatalEvent(ctx context.Context) *Event {
return l.event(ctx, FatalLevel)
}
// PanicEvent returns a log event with severity PANIC.
func (l *Logger) PanicEvent(ctx context.Context) *Event {
return l.event(ctx, PanicLevel)
}
// ErrorEvent returns a log event with severity ERROR.
func (l *Logger) ErrorEvent(ctx context.Context) *Event {
return l.event(ctx, ErrorLevel)
}
// WarnEvent returns a log event with severity WARN.
func (l *Logger) WarnEvent(ctx context.Context) *Event {
return l.event(ctx, WarnLevel)
}
// InfoEvent returns a log event with severity INFO.
func (l *Logger) InfoEvent(ctx context.Context) *Event {
return l.event(ctx, InfoLevel)
}
// DebugEvent returns a log event with severity DEBUG.
func (l *Logger) DebugEvent(ctx context.Context) *Event {
return l.event(ctx, DebugLevel)
}
func (l *Logger) event(ctx context.Context, level Level) (e *Event) {
var ctxKeys *[]interface{}
var ctxKeysMap *map[interface{}]string
if len(l.ctxKeys) > 0 {
ctxKeys = &l.ctxKeys
}
if len(l.ctxKeysMap) > 0 {
ctxKeysMap = &l.ctxKeysMap
}
return newEvent(ctx, ctxKeys, ctxKeysMap, level, l.lw, level <= l.level, l.isCallerEnabled)
}
// NewLogger creates a new logger for given out and level, and the level is
// optional.
func NewLogger(out io.Writer, level ...string) (*Logger, error) {
return NewLoggerWithError(out, nil, level...)
}
// NewLoggerWithError creates a new logger for given out, err out, level, and the
// err out can be nil, and the level is optional.
func NewLoggerWithError(out, errOut io.Writer, level ...string) (l *Logger, err error) {
if out == nil {
return nil, errors.New("logger output must specified")
}
sw := &StandardWriter{w: out, ew: errOut, pid: os.Getpid()}
l = &Logger{lw: sw}
if len(level) == 1 {
if err = l.SetLevel(level[0]); err != nil {
return nil, err
}
}
return l, nil
}
// NewTerminalLogger creates a logger that write into terminal.
func NewTerminalLogger(level ...string) (*Logger, error) {
return NewLogger(os.Stdout, level...)
}
// NewBufferedTerminalLogger creates a buffered logger that write into terminal.
func NewBufferedTerminalLogger(level ...string) (*Logger, error) {
return NewLogger(bufio.NewWriter(os.Stdout), level...)
}
// NewFileLogger creates a logger that write into file.
func NewFileLogger(filePath string, level ...string) (*Logger, error) {
return NewFileLoggerWithError(filePath, "", level...)
}
// NewFileLoggerWithError creates a logger that write into files.
func NewFileLoggerWithError(filePath, errFilePath string, level ...string) (*Logger, error) {
if err := check.Dir(path.Dir(filePath)); err != nil {
return nil, err
}
if errFilePath != "" {
if err := check.Dir(path.Dir(errFilePath)); err != nil {
return nil, err
}
}
out, err := reopen.NewFileWriter(filePath)
if err != nil {
return nil, err
}
var errOut *reopen.FileWriter
if errFilePath != "" {
errOut, err = reopen.NewFileWriter(errFilePath)
if err != nil {
return nil, err
}
}
c := make(chan os.Signal)
go func() {
for {
select {
case <-c:
out.Reopen()
if errOut != nil {
errOut.Reopen()
}
}
}
}()
signal.Notify(c, syscall.SIGHUP)
if errOut == nil {
return NewLoggerWithError(out, nil, level...)
}
return NewLoggerWithError(out, errOut, level...)
}
// NewBufferedFileLogger creates a logger that write into file with buffer.
// The flushSeconds's unit is second.
func NewBufferedFileLogger(filePath string, flushInterval int, level ...string) (*Logger, error) {
return NewBufferedFileLoggerWithError(filePath, "", flushInterval, level...)
}
// NewBufferedFileLoggerWithError creates a logger that write into files with buffer.
// The flushSeconds's unit is second.
func NewBufferedFileLoggerWithError(filePath, errFilePath string, flushInterval int, level ...string) (*Logger, error) {
if err := check.Dir(path.Dir(filePath)); err != nil {
return nil, err
}
if errFilePath != "" {
if err := check.Dir(path.Dir(errFilePath)); err != nil {
return nil, err
}
}
if flushInterval == 0 {
flushInterval = 10
}
out, err := reopen.NewFileWriter(filePath)
if err != nil {
return nil, err
}
var errOut *reopen.FileWriter
if errFilePath != "" {
errOut, err = reopen.NewFileWriter(errFilePath)
if err != nil {
return nil, err
}
}
bufferedOut := reopen.NewBufferedFileWriter(out)
var bufferedErrOut *reopen.BufferedFileWriter
if errOut != nil {
bufferedErrOut = reopen.NewBufferedFileWriter(errOut)
}
c := make(chan os.Signal)
go func() {
for {
select {
case <-c:
bufferedOut.Reopen()
if bufferedErrOut != nil {
bufferedErrOut.Reopen()
}
case <-time.After(time.Duration(flushInterval) * time.Second):
bufferedOut.Flush()
if bufferedErrOut != nil {
bufferedErrOut.Flush()
}
}
}
}()
signal.Notify(c, syscall.SIGHUP)
if bufferedErrOut == nil {
return NewLoggerWithError(bufferedOut, nil, level...)
}
return NewLoggerWithError(bufferedOut, bufferedErrOut, level...)
}

View File

@@ -0,0 +1,83 @@
package log
import (
"io"
"time"
"github.com/pengsrc/go-shared/buffer"
"github.com/pengsrc/go-shared/convert"
)
// LevelWriter defines as interface a writer may implement in order
// to receive level information with payload.
type LevelWriter interface {
io.Writer
WriteLevel(level Level, message []byte) (n int, err error)
}
// Flusher defines a interface with Flush() method.
type Flusher interface {
Flush() error
}
// StandardWriter implements io.Writer{} and LevelWriter{} interface.
type StandardWriter struct {
w io.Writer
ew io.Writer // Writer for WARN, ERROR, FATAL, PANIC
dl Level // Default level
pid int
}
// Write implements the io.Writer{} interface.
func (sw *StandardWriter) Write(p []byte) (n int, err error) {
return sw.WriteLevel(sw.dl, p)
}
// WriteLevel implements the LevelWriter{} interface.
func (sw *StandardWriter) WriteLevel(level Level, message []byte) (n int, err error) {
levelString := level.String()
if len(levelString) == 4 {
levelString = " " + levelString
}
buf := buffer.GlobalBytesPool().Get()
defer buf.Free()
buf.AppendString("[")
buf.AppendTime(time.Now().UTC(), convert.ISO8601Milli)
buf.AppendString(" #")
buf.AppendInt(int64(sw.pid))
buf.AppendString("] ")
buf.AppendString(levelString)
buf.AppendString(" -- : ")
buf.AppendBytes(message)
buf.AppendString("\n")
if sw.ew != nil {
if level > MuteLevel && level <= WarnLevel {
n, err = sw.ew.Write(buf.Bytes())
if err != nil {
return
}
}
}
return sw.w.Write(buf.Bytes())
}
// Flush implements the Flusher{} interface.
func (sw *StandardWriter) Flush() (err error) {
if flusher, ok := sw.w.(Flusher); ok {
err = flusher.Flush()
if err != nil {
return err
}
}
if sw.ew != nil {
if flusher, ok := sw.ew.(Flusher); ok {
err = flusher.Flush()
return err
}
}
return nil
}