Initial commit
This commit is contained in:
81
tlog/clog.go
Normal file
81
tlog/clog.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package tlog
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
func Cdebug(ctx context.Context, args ...interface{}) {
|
||||
id := fmt.Sprintf("trace_id=%s", getTraceIdFromCtx(ctx))
|
||||
data := append([]interface{}{id}, args...)
|
||||
l.p(DEBUG, data...)
|
||||
}
|
||||
|
||||
func Cdebugf(ctx context.Context, format string, args ...interface{}) {
|
||||
f := "trace_id=%s||" + format
|
||||
data := append([]interface{}{getTraceIdFromCtx(ctx)}, args...)
|
||||
l.pf(DEBUG, f, data...)
|
||||
}
|
||||
|
||||
func Cinfo(ctx context.Context, args ...interface{}) {
|
||||
id := fmt.Sprintf("trace_id=%s", getTraceIdFromCtx(ctx))
|
||||
data := append([]interface{}{id}, args...)
|
||||
l.p(INFO, data...)
|
||||
}
|
||||
|
||||
func Cinfof(ctx context.Context, format string, args ...interface{}) {
|
||||
f := "trace_id=%s||" + format
|
||||
data := append([]interface{}{getTraceIdFromCtx(ctx)}, args...)
|
||||
l.pf(INFO, f, data...)
|
||||
}
|
||||
|
||||
func Cerror(ctx context.Context, args ...interface{}) {
|
||||
id := fmt.Sprintf("trace_id=%s", getTraceIdFromCtx(ctx))
|
||||
data := append([]interface{}{id}, args...)
|
||||
l.p(ERROR, data...)
|
||||
}
|
||||
|
||||
func Cerrorf(ctx context.Context, format string, args ...interface{}) {
|
||||
f := "trace_id=%s||" + format
|
||||
data := append([]interface{}{getTraceIdFromCtx(ctx)}, args...)
|
||||
l.pf(ERROR, f, data...)
|
||||
}
|
||||
|
||||
func Cwarning(ctx context.Context, args ...interface{}) {
|
||||
id := fmt.Sprintf("trace_id=%s", getTraceIdFromCtx(ctx))
|
||||
data := append([]interface{}{id}, args...)
|
||||
l.p(WARNING, data...)
|
||||
}
|
||||
|
||||
func Cwarningf(ctx context.Context, format string, args ...interface{}) {
|
||||
f := "trace_id=%s||" + format
|
||||
data := append([]interface{}{getTraceIdFromCtx(ctx)}, args...)
|
||||
l.pf(WARNING, f, data...)
|
||||
}
|
||||
|
||||
func Cfatal(ctx context.Context, args ...interface{}) {
|
||||
id := fmt.Sprintf("trace_id=%s", getTraceIdFromCtx(ctx))
|
||||
data := append([]interface{}{id}, args...)
|
||||
l.p(FATAL, data...)
|
||||
}
|
||||
|
||||
func Cfatalf(ctx context.Context, format string, args ...interface{}) {
|
||||
f := "trace_id=%s||" + format
|
||||
data := append([]interface{}{getTraceIdFromCtx(ctx)}, args...)
|
||||
l.pf(FATAL, f, data...)
|
||||
}
|
||||
|
||||
func getTraceIdFromCtx(ctx context.Context) string {
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
if !ok {
|
||||
return "0"
|
||||
}
|
||||
v, ok := md["trace_id"]
|
||||
if !ok || len(v) < 1 {
|
||||
return "0"
|
||||
}
|
||||
|
||||
return v[0]
|
||||
}
|
||||
38
tlog/level.go
Normal file
38
tlog/level.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package tlog
|
||||
|
||||
type LEVEL byte
|
||||
|
||||
const (
|
||||
ALL LEVEL = iota
|
||||
DEBUG
|
||||
INFO
|
||||
WARNING
|
||||
ERROR
|
||||
FATAL
|
||||
)
|
||||
|
||||
var levelText = map[LEVEL]string{
|
||||
ALL: "ALL",
|
||||
DEBUG: "DEBUG",
|
||||
INFO: "INFO",
|
||||
WARNING: "WARNING",
|
||||
ERROR: "ERROR",
|
||||
FATAL: "FATAL",
|
||||
}
|
||||
|
||||
func getLevel(level string) LEVEL {
|
||||
switch level {
|
||||
case "DEBUG":
|
||||
return DEBUG
|
||||
case "INFO":
|
||||
return INFO
|
||||
case "WARNING":
|
||||
return WARNING
|
||||
case "ERROR":
|
||||
return ERROR
|
||||
case "FATAL":
|
||||
return FATAL
|
||||
default:
|
||||
return ALL
|
||||
}
|
||||
}
|
||||
334
tlog/logger.go
Normal file
334
tlog/logger.go
Normal file
@@ -0,0 +1,334 @@
|
||||
package tlog
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
//"encoding/json"
|
||||
"fmt"
|
||||
//"github.com/getsentry/raven-go"
|
||||
"log/syslog"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var l *Logger
|
||||
var mu sync.Mutex
|
||||
|
||||
type Logger struct {
|
||||
fileSize int64
|
||||
fileNum int
|
||||
fileName string
|
||||
host string
|
||||
debug bool
|
||||
tag string
|
||||
level LEVEL
|
||||
dir string
|
||||
ch chan *Atom
|
||||
f *os.File
|
||||
w *bufio.Writer
|
||||
useSyslog bool
|
||||
bytePool *sync.Pool
|
||||
syslogW *syslog.Writer
|
||||
//jsonEncoder *json.Encoder
|
||||
//sentryCh chan *raven.Packet
|
||||
//client *raven.Client
|
||||
}
|
||||
|
||||
type Atom struct {
|
||||
//isJson bool
|
||||
line int
|
||||
file string
|
||||
format string
|
||||
level LEVEL
|
||||
args []interface{}
|
||||
data map[string]interface{}
|
||||
}
|
||||
|
||||
func newLogger(config Config) {
|
||||
l = &Logger{
|
||||
dir: config.Dir,
|
||||
fileSize: int64(config.FileSize * 1024 * 1024),
|
||||
fileNum: config.FileNum,
|
||||
fileName: path.Join(config.Dir, config.FileName+".log"),
|
||||
tag: config.Tag,
|
||||
debug: config.Debug,
|
||||
level: getLevel(config.Level),
|
||||
ch: make(chan *Atom, 102400),
|
||||
bytePool: &sync.Pool{New: func() interface{} { return new(bytes.Buffer) }},
|
||||
}
|
||||
host, _ := os.Hostname()
|
||||
ss := strings.Split(host, "-")
|
||||
if len(ss) < 2 {
|
||||
l.host = host
|
||||
} else {
|
||||
l.host = ss[len(ss)-2] + ss[len(ss)-1]
|
||||
}
|
||||
|
||||
if l.debug {
|
||||
return
|
||||
}
|
||||
|
||||
os.MkdirAll(l.dir, 0755)
|
||||
l.f, _ = os.OpenFile(l.fileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
l.w = bufio.NewWriterSize(l.f, 1024*1024)
|
||||
/*
|
||||
l.jsonEncoder = json.NewEncoder(l.w)
|
||||
if config.SentryUrl != "" {
|
||||
l.sentryCh = make(chan *raven.Packet, 1024)
|
||||
l.client, _ = raven.New(config.SentryUrl)
|
||||
}
|
||||
*/
|
||||
if config.UseSyslog {
|
||||
var err error
|
||||
l.syslogW, err = syslog.New(syslog.LOG_LOCAL3|syslog.LOG_INFO, config.SyslogTag)
|
||||
if err == nil {
|
||||
l.useSyslog = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) start() {
|
||||
/*
|
||||
if l.client != nil && l.sentryCh != nil {
|
||||
go l.sentry()
|
||||
}
|
||||
*/
|
||||
for {
|
||||
a := <-l.ch
|
||||
if a == nil {
|
||||
l.w.Flush()
|
||||
fileInfo, err := os.Stat(l.fileName)
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
l.f.Close()
|
||||
l.f, _ = os.OpenFile(l.fileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
l.w.Reset(l.f)
|
||||
}
|
||||
if fileInfo.Size() > l.fileSize {
|
||||
l.f.Close()
|
||||
os.Rename(l.fileName, l.logname())
|
||||
l.f, _ = os.OpenFile(l.fileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
l.w.Reset(l.f)
|
||||
l.rm()
|
||||
}
|
||||
continue
|
||||
}
|
||||
b := l.bytes(a)
|
||||
l.w.Write(b)
|
||||
if l.useSyslog {
|
||||
l.syslogW.Write(b)
|
||||
}
|
||||
//发送sentry
|
||||
//l.toSentryCh(a)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func (l *Logger) sentry() {
|
||||
for {
|
||||
p := <-l.sentryCh
|
||||
_, ch := l.client.Capture(p, nil)
|
||||
if ch != nil {
|
||||
if err := <-ch; err != nil {
|
||||
}
|
||||
close(ch)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
func (l *Logger) toSentryCh(a *Atom) {
|
||||
if a.level >= ERROR {
|
||||
packet := l.formatSentryPacket(a)
|
||||
select {
|
||||
case l.sentryCh <- packet:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) formatSentryPacket(a *Atom) *raven.Packet {
|
||||
packet := &raven.Packet{Message: l.fileName}
|
||||
if a.level == ERROR {
|
||||
packet.Level = raven.ERROR
|
||||
}
|
||||
if a.level == FATAL {
|
||||
packet.Level = raven.FATAL
|
||||
}
|
||||
if a.isJson {
|
||||
packet.Extra = a.data
|
||||
packet.Extra["fileloc"] = fmt.Sprintf("%s:%d", a.file, a.line)
|
||||
return packet
|
||||
}
|
||||
packet.Extra = map[string]interface{}{"fileloc": fmt.Sprintf("%s:%d", a.file, a.line)}
|
||||
if a.format == "" {
|
||||
packet.Culprit = fmt.Sprint(a.args...)
|
||||
} else {
|
||||
packet.Culprit = fmt.Sprintf(a.format, a.args...)
|
||||
}
|
||||
return packet
|
||||
}
|
||||
*/
|
||||
|
||||
func (l *Logger) run() {
|
||||
if l.debug {
|
||||
return
|
||||
}
|
||||
go l.flush()
|
||||
go l.start()
|
||||
}
|
||||
|
||||
func (l *Logger) stop() {
|
||||
if l != nil && l.w != nil {
|
||||
l.w.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
var bytePool *sync.Pool = &sync.Pool{New: func() interface{} { return new(bytes.Buffer) }}
|
||||
|
||||
func (l *Logger) bytes(a *Atom) []byte {
|
||||
w := l.bytePool.Get().(*bytes.Buffer)
|
||||
defer func() {
|
||||
recover()
|
||||
w.Reset()
|
||||
l.bytePool.Put(w)
|
||||
}()
|
||||
w.Write(l.genTime())
|
||||
fmt.Fprintf(w, "%s %s %s %s:%d ", l.host, l.tag, levelText[a.level], a.file, a.line)
|
||||
if len(a.format) < 1 {
|
||||
for _, arg := range a.args {
|
||||
w.WriteByte(' ')
|
||||
fmt.Fprint(w, arg)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(w, a.format, a.args...)
|
||||
}
|
||||
w.WriteByte(10)
|
||||
b := make([]byte, w.Len())
|
||||
copy(b, w.Bytes())
|
||||
return b
|
||||
}
|
||||
|
||||
func (l *Logger) rm() {
|
||||
if out, err := exec.Command("ls", l.dir).Output(); err == nil {
|
||||
files := bytes.Split(out, []byte("\n"))
|
||||
totol, idx := len(files)-1, 0
|
||||
for i := totol; i >= 0; i-- {
|
||||
file := path.Join(l.dir, string(files[i]))
|
||||
if strings.HasPrefix(file, l.fileName) && file != l.fileName {
|
||||
idx++
|
||||
if idx > l.fileNum {
|
||||
os.Remove(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) flush() {
|
||||
for range time.NewTicker(time.Second).C {
|
||||
l.ch <- nil
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) logname() string {
|
||||
t := fmt.Sprintf("%s", time.Now())[:19]
|
||||
tt := strings.Replace(
|
||||
strings.Replace(
|
||||
strings.Replace(t, "-", "", -1),
|
||||
" ", "", -1),
|
||||
":", "", -1)
|
||||
return fmt.Sprintf("%s.%s", l.fileName, tt)
|
||||
}
|
||||
|
||||
func (l *Logger) genTime() []byte {
|
||||
now := time.Now()
|
||||
_, month, day := now.Date()
|
||||
hour, minute, second := now.Clock()
|
||||
return []byte{byte(month/10) + 48, byte(month%10) + 48, '-', byte(day/10) + 48, byte(day%10) + 48, ' ',
|
||||
byte(hour/10) + 48, byte(hour%10) + 48, ':', byte(minute/10) + 48, byte(minute%10) + 48, ':',
|
||||
byte(second/10) + 48, byte(second%10) + 48, ' '}
|
||||
}
|
||||
|
||||
func (l *Logger) getTag() string {
|
||||
if l != nil {
|
||||
return l.tag
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (l *Logger) p(level LEVEL, args ...interface{}) {
|
||||
file, line := l.getFileNameAndLine()
|
||||
if l == nil || l.debug {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
fmt.Printf("%s%s %s %s:%d ", l.genTime(), l.getTag(), levelText[level], file, line)
|
||||
fmt.Println(args...)
|
||||
return
|
||||
}
|
||||
if level >= l.level {
|
||||
select {
|
||||
case l.ch <- &Atom{file: file, line: line, level: level, args: args}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) pf(level LEVEL, format string, args ...interface{}) {
|
||||
file, line := l.getFileNameAndLine()
|
||||
if l == nil || l.debug {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
fmt.Printf("%s%s %s %s:%d ", l.genTime(), l.getTag(), levelText[level], file, line)
|
||||
fmt.Printf(format, args...)
|
||||
fmt.Println()
|
||||
return
|
||||
}
|
||||
if level >= l.level {
|
||||
select {
|
||||
case l.ch <- &Atom{file: file, line: line, format: format, level: level, args: args}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//暂不支持json
|
||||
func (l *Logger) pj(level LEVEL, m map[string]interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
file, line := l.getFileNameAndLine()
|
||||
if l == nil || l.debug {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
m["time"] = string(l.genTime())
|
||||
m["fileloc"] = fmt.Sprintf("%s:%d", file, line)
|
||||
m["level"] = levelText[level]
|
||||
json.NewEncoder(os.Stdout).Encode(m)
|
||||
return
|
||||
}
|
||||
if level >= l.level {
|
||||
select {
|
||||
case l.ch <- &Atom{file: file, line: line, data: m, level: level, isJson: true}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func (l *Logger) getFileNameAndLine() (string, int) {
|
||||
_, file, line, ok := runtime.Caller(3)
|
||||
if !ok {
|
||||
return "???", 1
|
||||
}
|
||||
dirs := strings.Split(file, "/")
|
||||
if len(dirs) >= 2 {
|
||||
return dirs[len(dirs)-2] + "/" + dirs[len(dirs)-1], line
|
||||
}
|
||||
return file, line
|
||||
}
|
||||
101
tlog/tlog.go
Normal file
101
tlog/tlog.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package tlog
|
||||
|
||||
type Config struct {
|
||||
FileSize int `toml:"filesize" json:"filesize"`
|
||||
FileNum int `toml:"filenum" json:"filenum"`
|
||||
FileName string `toml:"filename" json:"filename"`
|
||||
Tag string `toml:"tag" json:"tag"`
|
||||
Level string `toml:"level" json:"level"`
|
||||
Debug bool `toml:"debug" json:"debug"`
|
||||
Dir string `toml:"dir" json:"dir"`
|
||||
SentryUrl string `toml:"sentry_url" json:"sentry_url"`
|
||||
UseSyslog bool `toml:"use_syslog" json:"use_syslog"`
|
||||
SyslogTag string `toml:"syslog_tag" json:"syslog_tag"`
|
||||
}
|
||||
|
||||
func (c *Config) check() {
|
||||
if c.FileSize == 0 {
|
||||
c.FileSize = 128
|
||||
}
|
||||
if c.FileNum == 0 {
|
||||
c.FileNum = 10
|
||||
}
|
||||
if c.FileName == "" {
|
||||
c.FileName = "INFO"
|
||||
}
|
||||
if c.Dir == "" {
|
||||
c.Dir = "./logs"
|
||||
}
|
||||
if c.Level == "" {
|
||||
c.Level = "DEBUG"
|
||||
}
|
||||
}
|
||||
|
||||
func Init(c Config) {
|
||||
c.check()
|
||||
newLogger(c)
|
||||
l.run()
|
||||
}
|
||||
|
||||
func Close() {
|
||||
l.stop()
|
||||
}
|
||||
|
||||
func Debug(args ...interface{}) {
|
||||
l.p(DEBUG, args...)
|
||||
}
|
||||
|
||||
func Debugf(format string, args ...interface{}) {
|
||||
l.pf(DEBUG, format, args...)
|
||||
}
|
||||
|
||||
func DebugJson(m map[string]interface{}) {
|
||||
l.pj(DEBUG, m)
|
||||
}
|
||||
|
||||
func Info(args ...interface{}) {
|
||||
l.p(INFO, args...)
|
||||
}
|
||||
|
||||
func Infof(format string, args ...interface{}) {
|
||||
l.pf(INFO, format, args...)
|
||||
}
|
||||
func InfoJson(m map[string]interface{}) {
|
||||
l.pj(INFO, m)
|
||||
}
|
||||
|
||||
func Warning(args ...interface{}) {
|
||||
l.p(WARNING, args...)
|
||||
}
|
||||
|
||||
func Warningf(format string, args ...interface{}) {
|
||||
l.pf(WARNING, format, args...)
|
||||
}
|
||||
|
||||
func WarningJson(m map[string]interface{}) {
|
||||
l.pj(WARNING, m)
|
||||
}
|
||||
|
||||
func Error(args ...interface{}) {
|
||||
l.p(ERROR, args...)
|
||||
}
|
||||
|
||||
func Errorf(format string, args ...interface{}) {
|
||||
l.pf(ERROR, format, args...)
|
||||
}
|
||||
|
||||
func ErrorJson(m map[string]interface{}) {
|
||||
l.pj(ERROR, m)
|
||||
}
|
||||
|
||||
func Fatal(args ...interface{}) {
|
||||
l.p(FATAL, args...)
|
||||
}
|
||||
|
||||
func Fatalf(format string, args ...interface{}) {
|
||||
l.pf(FATAL, format, args...)
|
||||
}
|
||||
|
||||
func FatalJson(m map[string]interface{}) {
|
||||
l.pj(FATAL, m)
|
||||
}
|
||||
30
tlog/tlog_test.go
Normal file
30
tlog/tlog_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package tlog
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestTlog(t *testing.T) {
|
||||
Init(Config{
|
||||
FileSize: 128,
|
||||
FileNum: 5,
|
||||
FileName: "test",
|
||||
Level: "DEBUG",
|
||||
Dir: "./logs",
|
||||
Tag: "test",
|
||||
//Debug: true,
|
||||
//SentryUrl: "http://d25c96785a184d6f8d051712a209a3a8:7f4a22c48897423caa9a0aec6bb93416@172.20.0.84:9000/5",
|
||||
UseSyslog: true,
|
||||
SyslogTag: "test",
|
||||
})
|
||||
for i := 0; i < 10; i++ {
|
||||
Info("xxxxxxxxxxasfsadjflasjfdlasjdfsajdfsadfjasjfdafjsfsa")
|
||||
Infof("%s", "xxxxxxxxxxasfsadjflasjfdlasjdfsajdfsadfjasjfdafjsfsa")
|
||||
InfoJson(map[string]interface{}{"test": time.Now().UnixNano()})
|
||||
Error("xxxxxxxxxxasfsadjflasjfdlasjdfsajdfsadfjasjfdafjsfsa")
|
||||
Errorf("%s", "xxxxxxxxxxasfsadjflasjfdlasjdfsajdfsadfjasjfdafjsfsa")
|
||||
ErrorJson(map[string]interface{}{"test": time.Now().UnixNano()})
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
Reference in New Issue
Block a user