Files

337 lines
6.8 KiB
Go
Raw Permalink Normal View History

2025-07-21 19:24:01 +08:00
// +build !windows
2020-04-06 19:57:19 +08:00
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
}