commit
5742cde46f
@ -0,0 +1,2 @@ |
|||||||
|
.idea/ |
||||||
|
.gitlab-ci.yml |
@ -0,0 +1,15 @@ |
|||||||
|
#### Usage |
||||||
|
``` |
||||||
|
|
||||||
|
import git.qietv.work/public/logkit |
||||||
|
|
||||||
|
|
||||||
|
logkit.Init(FIlE, "test", logkit.LevelDebug) |
||||||
|
defer logkit.Exit() |
||||||
|
|
||||||
|
logkit.Info("this is a log") |
||||||
|
|
||||||
|
logkit.Infof("this is log %s", "arg") |
||||||
|
|
||||||
|
|
||||||
|
``` |
@ -0,0 +1,214 @@ |
|||||||
|
package logkit |
||||||
|
|
||||||
|
import ( |
||||||
|
"bufio" |
||||||
|
"bytes" |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"os" |
||||||
|
"path/filepath" |
||||||
|
"sync" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
type bufferNode struct { |
||||||
|
bytes.Buffer |
||||||
|
next *bufferNode |
||||||
|
} |
||||||
|
|
||||||
|
type mFileLogger struct { |
||||||
|
writer *bufferWriter |
||||||
|
mu sync.Mutex |
||||||
|
|
||||||
|
filepath string |
||||||
|
name string |
||||||
|
freeList *bufferNode |
||||||
|
freeListMu sync.Mutex |
||||||
|
flushInterval time.Duration |
||||||
|
fileSplitSize uint64 |
||||||
|
bufferSize int |
||||||
|
} |
||||||
|
|
||||||
|
type bufferWriter struct { |
||||||
|
*bufio.Writer |
||||||
|
logPath string |
||||||
|
logName string |
||||||
|
file *os.File |
||||||
|
slot int |
||||||
|
startTime time.Time |
||||||
|
byteSize uint64 // The number of bytes written to this file
|
||||||
|
maxFileSize uint64 |
||||||
|
bufferSize int |
||||||
|
} |
||||||
|
|
||||||
|
func (w *mFileLogger) getBuffer() *bufferNode { |
||||||
|
w.freeListMu.Lock() |
||||||
|
b := w.freeList |
||||||
|
if b != nil { |
||||||
|
w.freeList = b.next |
||||||
|
} |
||||||
|
w.freeListMu.Unlock() |
||||||
|
if b == nil { |
||||||
|
b = new(bufferNode) |
||||||
|
} else { |
||||||
|
b.next = nil |
||||||
|
b.Reset() |
||||||
|
} |
||||||
|
return b |
||||||
|
} |
||||||
|
|
||||||
|
func (w *mFileLogger) putBuffer(b *bufferNode) { |
||||||
|
if b.Len() >= 256 { |
||||||
|
// Let big buffers die with gc.
|
||||||
|
return |
||||||
|
} |
||||||
|
w.freeListMu.Lock() |
||||||
|
b.next = w.freeList |
||||||
|
w.freeList = b |
||||||
|
w.freeListMu.Unlock() |
||||||
|
} |
||||||
|
|
||||||
|
func (w *mFileLogger) Close() error { |
||||||
|
return w.flush() |
||||||
|
} |
||||||
|
|
||||||
|
func NewFileLogger(path, name string, flushInterval time.Duration, fileSplitSize uint64, bufferSize int) io.Writer { |
||||||
|
writer := &mFileLogger{ |
||||||
|
filepath: path, |
||||||
|
name: name, |
||||||
|
flushInterval: flushInterval, |
||||||
|
fileSplitSize: fileSplitSize, |
||||||
|
bufferSize: bufferSize, |
||||||
|
} |
||||||
|
go writer.flushDaemon() |
||||||
|
return writer |
||||||
|
} |
||||||
|
|
||||||
|
func (w *mFileLogger) flushDaemon() { |
||||||
|
for _ = range time.NewTicker(w.flushInterval).C { |
||||||
|
w.flush() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (w *mFileLogger) flush() (err error) { |
||||||
|
if w.writer == nil { |
||||||
|
return |
||||||
|
} |
||||||
|
err = w.writer.Flush() |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
err = w.writer.Sync() |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func (w *mFileLogger) Write(msg []byte) (n int, err error) { |
||||||
|
|
||||||
|
buf := w.getBuffer() |
||||||
|
buf.Write(msg) |
||||||
|
w.mu.Lock() |
||||||
|
defer w.mu.Unlock() |
||||||
|
|
||||||
|
writer := w.writer |
||||||
|
|
||||||
|
if writer == nil { |
||||||
|
w.writer = &bufferWriter{ |
||||||
|
logPath: w.filepath, |
||||||
|
logName: w.name, |
||||||
|
maxFileSize: w.fileSplitSize, |
||||||
|
bufferSize: w.bufferSize, |
||||||
|
} |
||||||
|
writer = w.writer |
||||||
|
} |
||||||
|
|
||||||
|
if err = writer.checkRotate(time.Now()); err != nil { |
||||||
|
fmt.Println("[logkit] check rotate err: " + err.Error()) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
w.putBuffer(buf) |
||||||
|
return writer.Write(buf.Bytes()) |
||||||
|
} |
||||||
|
|
||||||
|
func (bufferW *bufferWriter) Write(p []byte) (int, error) { |
||||||
|
n, err := bufferW.Writer.Write(p) |
||||||
|
bufferW.byteSize += uint64(n) |
||||||
|
return n, err |
||||||
|
} |
||||||
|
|
||||||
|
func (bufferW *bufferWriter) Sync() error { |
||||||
|
return bufferW.file.Sync() |
||||||
|
} |
||||||
|
|
||||||
|
func (bufferW *bufferWriter) checkRotate(now time.Time) error { |
||||||
|
if bufferW.file == nil { |
||||||
|
return bufferW.rotate(now, 0) |
||||||
|
} |
||||||
|
sYear, sMonth, sDay := bufferW.startTime.Date() |
||||||
|
year, month, day := now.Date() |
||||||
|
if year != sYear || month != sMonth || day != sDay { |
||||||
|
return bufferW.rotate(now, 0) |
||||||
|
} |
||||||
|
if bufferW.byteSize >= bufferW.maxFileSize { |
||||||
|
return bufferW.rotate(now, bufferW.slot+1) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (bufferW *bufferWriter) write(p []byte) (int, error) { |
||||||
|
n, err := bufferW.Writer.Write(p) |
||||||
|
bufferW.byteSize += uint64(n) |
||||||
|
return n, err |
||||||
|
} |
||||||
|
|
||||||
|
func (bufferW *bufferWriter) rotate(oldTime time.Time, slot int) error { |
||||||
|
if bufferW.file != nil { |
||||||
|
bufferW.Flush() |
||||||
|
bufferW.file.Close() |
||||||
|
var newFileName string |
||||||
|
|
||||||
|
year, month, day := oldTime.Date() |
||||||
|
|
||||||
|
if slot > 0 { |
||||||
|
newFileName = fmt.Sprintf("%s-%02d%02d%02d.%02d", bufferW.logPath, year, month, day, slot-1) |
||||||
|
} else { |
||||||
|
newFileName = fmt.Sprintf("%s-%02d%02d%02d", bufferW.logPath, year, month, day) |
||||||
|
} |
||||||
|
os.Rename(bufferW.logPath, newFileName) |
||||||
|
} |
||||||
|
|
||||||
|
if err := bufferW.openFile(bufferW.logPath, bufferW.logName); err != nil { |
||||||
|
return fmt.Errorf("rotate file error: %#v", err) |
||||||
|
} |
||||||
|
fileInfo, _ := bufferW.file.Stat() |
||||||
|
bufferW.byteSize = uint64(fileInfo.Size()) |
||||||
|
bufferW.Writer = bufio.NewWriterSize(bufferW.file, bufferW.bufferSize) |
||||||
|
bufferW.slot = slot |
||||||
|
bufferW.startTime = time.Now() |
||||||
|
bufferW.byteSize = 0 |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (bufferW *bufferWriter) openFile(fileName, logName string) error { |
||||||
|
var file *os.File |
||||||
|
var err error |
||||||
|
for { |
||||||
|
file, err = os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) |
||||||
|
if err == nil { |
||||||
|
break |
||||||
|
} |
||||||
|
// try to create all the parent directories for specified log file
|
||||||
|
// if it doesn't exist
|
||||||
|
if os.IsNotExist(err) { |
||||||
|
err2 := os.MkdirAll(filepath.Dir(fileName), 0755) |
||||||
|
if err2 != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
continue |
||||||
|
} |
||||||
|
return err |
||||||
|
} |
||||||
|
bufferW.file = file |
||||||
|
return nil |
||||||
|
} |
@ -0,0 +1,235 @@ |
|||||||
|
package logkit |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"path" |
||||||
|
"runtime" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
inited bool |
||||||
|
logWriter io.Writer |
||||||
|
logLevel = LevelInfo |
||||||
|
logLevelName string |
||||||
|
logName string |
||||||
|
logPath string |
||||||
|
channel Channel |
||||||
|
alsoStdout bool |
||||||
|
withCaller Caller |
||||||
|
|
||||||
|
levelToNames = map[Level]string{ |
||||||
|
LevelFatal: "FATAL", |
||||||
|
LevelError: "ERROR", |
||||||
|
LevelWarn: "WARN", |
||||||
|
LevelInfo: "INFO", |
||||||
|
LevelDebug: "DEBUG", |
||||||
|
LevelTrace: "TRACE", |
||||||
|
} |
||||||
|
) |
||||||
|
|
||||||
|
//Level 日志等级
|
||||||
|
type Level int |
||||||
|
|
||||||
|
const ( |
||||||
|
Default Level = iota |
||||||
|
LevelTrace |
||||||
|
LevelDebug |
||||||
|
LevelInfo |
||||||
|
LevelWarn |
||||||
|
LevelError |
||||||
|
LevelFatal |
||||||
|
) |
||||||
|
|
||||||
|
type Channel byte |
||||||
|
type Caller byte |
||||||
|
|
||||||
|
const ( |
||||||
|
FIlE Channel = iota |
||||||
|
SYSLOG |
||||||
|
KAFKA |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
_ Caller = iota |
||||||
|
NONE |
||||||
|
FullPATHFunc |
||||||
|
BasePathFunc |
||||||
|
BasePath |
||||||
|
) |
||||||
|
|
||||||
|
type Writer interface { |
||||||
|
//Write 写日志
|
||||||
|
Write(msg []byte) (int, error) |
||||||
|
//Exit 日志退出
|
||||||
|
Close() error |
||||||
|
} |
||||||
|
|
||||||
|
func Exit() { |
||||||
|
logWriter.(io.Closer).Close() |
||||||
|
} |
||||||
|
|
||||||
|
func Init(_channel Channel, name string, level Level, _alsoStdout bool, _withCaller Caller) (writer io.Writer, err error ){ |
||||||
|
if inited { |
||||||
|
return nil, fmt.Errorf("logkit has been inited") |
||||||
|
} |
||||||
|
|
||||||
|
if name != "" { |
||||||
|
logName = name |
||||||
|
} else { |
||||||
|
return nil, fmt.Errorf("log name must not be empty") |
||||||
|
} |
||||||
|
|
||||||
|
if logWriter == nil && channel == FIlE { |
||||||
|
if logPath == "" { |
||||||
|
logPath = "/data/logs/" + logName + ".log" |
||||||
|
} |
||||||
|
logWriter = NewFileLogger(logPath, logName, time.Second*5, 1204*1024*1800, 4*1024) |
||||||
|
} |
||||||
|
if logWriter == nil && channel == SYSLOG { |
||||||
|
logWriter, _ = NewSyslogWriter("", "", level, logName) |
||||||
|
} |
||||||
|
inited = true |
||||||
|
|
||||||
|
logLevel = level |
||||||
|
channel = _channel |
||||||
|
alsoStdout = _alsoStdout |
||||||
|
withCaller = _withCaller |
||||||
|
return logWriter, nil |
||||||
|
} |
||||||
|
|
||||||
|
func SetPath(path string) { |
||||||
|
logPath = path |
||||||
|
} |
||||||
|
func getLevelName(level Level) string { |
||||||
|
levelName, _ := levelToNames[level] |
||||||
|
return levelName |
||||||
|
} |
||||||
|
|
||||||
|
func format(level Level, msg string) string { |
||||||
|
if withCaller != NONE { |
||||||
|
var ( |
||||||
|
context string |
||||||
|
pc uintptr |
||||||
|
file string |
||||||
|
line int |
||||||
|
) |
||||||
|
pc, file, line, _ = runtime.Caller(3) |
||||||
|
switch withCaller { |
||||||
|
case FullPATHFunc: |
||||||
|
context = fmt.Sprintf("%s:%03d::%s", file, line, path.Base(runtime.FuncForPC(pc).Name())) |
||||||
|
case BasePathFunc: |
||||||
|
context = fmt.Sprintf("%s:%03d::%s", path.Base(file), line, path.Base(runtime.FuncForPC(pc).Name())) |
||||||
|
case BasePath: |
||||||
|
context = fmt.Sprintf("%s:%03d", path.Base(file), line) |
||||||
|
default: |
||||||
|
context = fmt.Sprintf("%s:%03d", path.Base(file), line) |
||||||
|
} |
||||||
|
|
||||||
|
return fmt.Sprintf("%s\t[%3s]\t%s\t%s\n", time.Now().Format("2006-01-02 15:04:05.999"), getLevelName(level), context, msg) |
||||||
|
} else { |
||||||
|
return fmt.Sprintf("%s\t[%3s]\t%s\n", time.Now().Format("2006-01-02 15:04:05.999"), getLevelName(level), msg) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func write(level Level, msg string) (err error) { |
||||||
|
if !inited { |
||||||
|
return fmt.Errorf("logkit has been inited") |
||||||
|
} |
||||||
|
messageStr := format(level, msg) |
||||||
|
_, err = logWriter.Write([]byte(messageStr)) |
||||||
|
if alsoStdout { |
||||||
|
fmt.Print(messageStr) |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func level() Level { |
||||||
|
return logLevel |
||||||
|
} |
||||||
|
|
||||||
|
func Debug(str string) { |
||||||
|
if level() <= LevelDebug { |
||||||
|
write(LevelDebug, str) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func Debugs(args ...interface{}) { |
||||||
|
if level() <= LevelDebug { |
||||||
|
write(LevelDebug, fmt.Sprintln(args...)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func Debugf(format string, args ...interface{}) { |
||||||
|
if level() <= LevelDebug { |
||||||
|
write(LevelDebug, fmt.Sprintf(format, args...)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func Info(str string) { |
||||||
|
if level() <= LevelInfo { |
||||||
|
write(LevelInfo, str) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func Infos(args ...interface{}) { |
||||||
|
if level() <= LevelInfo { |
||||||
|
write(LevelInfo, fmt.Sprintln(args...)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func Infof(format string, args ...interface{}) { |
||||||
|
if level() <= LevelInfo { |
||||||
|
write(LevelInfo, fmt.Sprintf(format, args...)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func Warn(str string) { |
||||||
|
if level() <= LevelWarn { |
||||||
|
write(LevelWarn, str) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func Warns(args ...interface{}) { |
||||||
|
if level() <= LevelWarn { |
||||||
|
write(LevelWarn, fmt.Sprintln(args...)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func Warnf(format string, args ...interface{}) { |
||||||
|
if level() <= LevelWarn { |
||||||
|
write(LevelWarn, fmt.Sprintf(format, args...)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func Error(str string) { |
||||||
|
if level() <= LevelError { |
||||||
|
write(LevelError, str) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func Errors(args ...interface{}) { |
||||||
|
if level() <= LevelError { |
||||||
|
write(LevelError, fmt.Sprintln(args...)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func Errorf(format string, args ...interface{}) { |
||||||
|
if level() <= LevelError { |
||||||
|
write(LevelError, fmt.Sprintf(format, args...)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func NewLogWriter(level Level) io.Writer { |
||||||
|
return &stdWriter{level} |
||||||
|
} |
||||||
|
|
||||||
|
type stdWriter struct { |
||||||
|
level Level |
||||||
|
} |
||||||
|
|
||||||
|
func (this *stdWriter) Write(data []byte) (int, error) { |
||||||
|
write(this.level, string(data)) |
||||||
|
return len(data), nil |
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
package logkit |
||||||
|
|
||||||
|
import ( |
||||||
|
"flag" |
||||||
|
"fmt" |
||||||
|
"strconv" |
||||||
|
"testing" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
func init() { |
||||||
|
var l Level |
||||||
|
flag.Int("level", int(l) , "l") |
||||||
|
flag.Parse() |
||||||
|
} |
||||||
|
|
||||||
|
func BenchmarkGoKit(b *testing.B) { |
||||||
|
defer Exit() |
||||||
|
Init(FIlE, "test", LevelDebug, true, BasePath) |
||||||
|
b.ResetTimer() |
||||||
|
for i := 0; i < b.N; i++ { |
||||||
|
Info("test " + strconv.FormatInt(int64(i), 10)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestInfo(t *testing.T) { |
||||||
|
defer Exit() |
||||||
|
fmt.Println("start") |
||||||
|
for i := 0; i < 10; i++ { |
||||||
|
go func(i int) { |
||||||
|
Info("test " + strconv.FormatInt(int64(i), 10)) |
||||||
|
}(i) |
||||||
|
} |
||||||
|
fmt.Println("end") |
||||||
|
time.Sleep(time.Second * 2) |
||||||
|
//
|
||||||
|
//for i := 0; i< 1000 ; i++ {
|
||||||
|
// Info("test "+ strconv.FormatInt(int64(i), 10))
|
||||||
|
//}
|
||||||
|
|
||||||
|
time.Sleep(time.Second * 1) |
||||||
|
Info("test 2") |
||||||
|
time.Sleep(time.Second * 1) |
||||||
|
Info("test 3") |
||||||
|
} |
||||||
|
|
||||||
|
func TestBuffer(t *testing.T) { |
||||||
|
defer Exit() |
||||||
|
|
||||||
|
//for i := 0 ; i < 1024; i++ {
|
||||||
|
// str += strconv.FormatInt(int64(i),10)
|
||||||
|
//}
|
||||||
|
Infof("test %s --- %s", "1", "23") |
||||||
|
//Exit()
|
||||||
|
} |
||||||
|
|
||||||
|
func TestFlag(t *testing.T) { |
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
package logkit |
||||||
|
|
||||||
|
import ( |
||||||
|
"log/syslog" |
||||||
|
) |
||||||
|
|
||||||
|
type SyslogWriter struct { |
||||||
|
network string |
||||||
|
raddr string |
||||||
|
priority syslog.Priority |
||||||
|
tag string |
||||||
|
writer *syslog.Writer |
||||||
|
} |
||||||
|
|
||||||
|
func NewSyslogWriter(network, raddr string, level Level, tag string) (Writer, error) { |
||||||
|
var priority syslog.Priority |
||||||
|
switch level { |
||||||
|
case LevelDebug: |
||||||
|
priority = syslog.LOG_DEBUG |
||||||
|
break |
||||||
|
case LevelInfo: |
||||||
|
priority = syslog.LOG_INFO |
||||||
|
break |
||||||
|
case LevelWarn: |
||||||
|
priority = syslog.LOG_WARNING |
||||||
|
break |
||||||
|
case LevelError: |
||||||
|
priority = syslog.LOG_ERR |
||||||
|
break |
||||||
|
case LevelFatal: |
||||||
|
priority = syslog.LOG_ALERT |
||||||
|
break |
||||||
|
default: |
||||||
|
priority = syslog.LOG_INFO |
||||||
|
} |
||||||
|
writer, err := syslog.Dial(network, raddr, priority, tag) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
object := &SyslogWriter{ |
||||||
|
network: network, |
||||||
|
raddr: raddr, |
||||||
|
priority: priority, |
||||||
|
tag: tag, |
||||||
|
writer: writer, |
||||||
|
} |
||||||
|
return object, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (self *SyslogWriter) Write(msg []byte) (int, error) { |
||||||
|
return self.writer.Write([]byte(msg)) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *SyslogWriter) Close() error{ |
||||||
|
// ignore the error return code
|
||||||
|
return self.writer.Close() |
||||||
|
} |
Loading…
Reference in new issue