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