Skip to content

Commit

Permalink
add log hook
Browse files Browse the repository at this point in the history
  • Loading branch information
MacBookAirM2 committed May 9, 2023
1 parent 1771e84 commit 9acf52b
Show file tree
Hide file tree
Showing 3 changed files with 290 additions and 9 deletions.
5 changes: 5 additions & 0 deletions examples/zinx_logger/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,14 @@ func (t *TestRouter) PostHandle(req ziface.IRequest) {
}
}

func onLog(a []byte) {

}

func main() {
s := znet.NewServer()
s.AddRouter(1, &TestRouter{})
zlog.SetLogger(new(MyLogger))
zlog.StdZinxLog.SetOnLogListen(&onLog)
s.Serve()
}
28 changes: 19 additions & 9 deletions zlog/logger_core.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,16 @@ type ZinxLoggerCore struct {
// file swap lock
// (文件交换锁)
fsLock sync.Mutex

onLogHook func([]byte)
}

/*
NewZinxLog Create a new log
NewZinxLog Create a new log
out: The file io for standard output
prefix: The prefix of the log
flag: The flag of the log header information
out: The file io for standard output
prefix: The prefix of the log
flag: The flag of the log header information
*/
func NewZinxLog(out io.Writer, prefix string, flag int) *ZinxLoggerCore {

Expand All @@ -135,13 +137,17 @@ func CleanZinxLog(log *ZinxLoggerCore) {
log.closeFile()
}

func (log *ZinxLoggerCore) SetLogHook(f func([]byte)) {
log.onLogHook = f
}

/*
formatHeader generates the header information for a log entry.
formatHeader generates the header information for a log entry.
t: The current time.
file: The file name of the source code invoking the log function.
line: The line number of the source code invoking the log function.
level: The log level of the current log entry.
t: The current time.
file: The file name of the source code invoking the log function.
line: The line number of the source code invoking the log function.
level: The log level of the current log entry.
*/
func (log *ZinxLoggerCore) formatHeader(t time.Time, file string, line int, level int) {
var buf *bytes.Buffer = &log.buf
Expand Down Expand Up @@ -250,6 +256,10 @@ func (log *ZinxLoggerCore) OutPut(level int, s string) error {
_, err = log.out.Write(log.buf.Bytes())
}

if log.onLogHook != nil {
log.onLogHook(log.buf.Bytes())
}

return err

}
Expand Down
266 changes: 266 additions & 0 deletions zutils/witer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
package zutils

import (
"bufio"
"bytes"
"io"
"os"
"path/filepath"
"strings"
"sync"
"time"
)

const (
sizeMiB = 1024 * 1024
defMaxAge = 31
defMaxSize = 64 //MiB
)

var _ io.WriteCloser = (*Writer)(nil)

type Writer struct {
maxAge int // 最大保留天数
maxSize int64 // 单个日志最大容量 默认 64MB
size int64 // 累计大小
fpath string // 文件目录 完整路径 fpath=fdir+fname+fsuffix
fdir string //
fname string // 文件名
fsuffix string // 文件后缀名 默认 .log
created time.Time // 文件创建日期
creates []byte // 文件创建日期
cons bool // 标准输出 默认 false
file *os.File
bw *bufio.Writer
mu sync.Mutex
}

func New(path string) *Writer {
w := &Writer{
fpath: path, //dir1/dir2/app.log
mu: sync.Mutex{},
}
w.fdir = filepath.Dir(w.fpath) //dir1/dir2
w.fsuffix = filepath.Ext(w.fpath) //.log
w.fname = strings.TrimSuffix(filepath.Base(w.fpath), w.fsuffix) //app
if w.fsuffix == "" {
w.fsuffix = ".log"
}
w.maxSize = sizeMiB * defMaxSize
w.maxAge = defMaxAge
os.MkdirAll(filepath.Dir(w.fpath), 0755)
go w.daemon()
return w
}
func (w *Writer) daemon() {
for range time.NewTicker(time.Second * 5).C {
w.flush()
}
}

// SetMaxAge 最大保留天数
func (w *Writer) SetMaxAge(ma int) {
w.mu.Lock()
w.maxAge = ma
w.mu.Unlock()
}

// SetMaxSize 单个日志最大容量
func (w *Writer) SetMaxSize(ms int64) {
if ms < 1 {
return
}
w.mu.Lock()
w.maxSize = ms
w.mu.Unlock()
}

// SetCons 同时输出控制台
func (w *Writer) SetCons(b bool) {
w.mu.Lock()
w.cons = b
w.mu.Unlock()
}

func (w *Writer) Write(p []byte) (n int, err error) {
w.mu.Lock()
defer w.mu.Unlock()
if w.cons {
os.Stderr.Write(p)
}
if w.file == nil {
if err := w.rotate(); err != nil {
os.Stderr.Write(p)
return 0, err
}
}

t := time.Now()
var b []byte
year, month, day := t.Date()
b = appendInt(b, year, 4)
b = append(b, '-')
b = appendInt(b, int(month), 2)
b = append(b, '-')
b = appendInt(b, day, 2)

// 按天切割
if !bytes.Equal(w.creates[:10], b) { //2023-04-05
go w.delete() // 每天检测一次旧文件
if err := w.rotate(); err != nil {
return 0, err
}
}
// 按大小切割
if w.size+int64(len(p)) >= w.maxSize {
if err := w.rotate(); err != nil {
return 0, err
}
}
// n, err = w.file.Write(p)
n, err = w.bw.Write(p)
w.size += int64(n)
if err != nil {
return n, err
}
return
}

// rotate 切割文件
func (w *Writer) rotate() error {
now := time.Now()
if w.file != nil {
w.bw.Flush()
w.file.Sync()
w.file.Close()
// 保存
fbak := w.fname + w.time2name(w.created) + w.fsuffix
os.Rename(w.fpath, filepath.Join(w.fdir, fbak))
w.size = 0
}
finfo, err := os.Stat(w.fpath)
w.created = now
if err == nil {
w.size = finfo.Size()
w.created = finfo.ModTime()
}
w.creates = w.created.AppendFormat(nil, time.RFC3339)
fout, err := os.OpenFile(w.fpath, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
if err != nil {
return err
}
w.file = fout
w.bw = bufio.NewWriter(w.file)
return nil
}

// 删除旧日志
func (w *Writer) delete() {
if w.maxAge <= 0 {
return
}
dir := filepath.Dir(w.fpath)
fakeNow := time.Now().AddDate(0, 0, -w.maxAge)
dirs, err := os.ReadDir(dir)
if err != nil {
return
}
for _, path := range dirs {
name := path.Name()
if path.IsDir() {
continue
}
t, err := w.name2time(name)
// 只删除满足格式的文件
if err == nil && t.Before(fakeNow) {
os.Remove(filepath.Join(dir, name))
}
}
}
func (w *Writer) name2time(name string) (time.Time, error) {
name = strings.TrimPrefix(name, filepath.Base(w.fname))
name = strings.TrimSuffix(name, w.fsuffix)
return time.Parse(".2006-01-02-150405", name)
}
func (w *Writer) time2name(t time.Time) string {
return t.Format(".2006-01-02-150405")
}

func (w *Writer) Close() error {
w.flush()
return w.close()
}

// close closes the file if it is open.
func (w *Writer) close() error {
w.mu.Lock()
defer w.mu.Unlock()
if w.file == nil {
return nil
}
w.file.Sync()
err := w.file.Close()
w.file = nil
return err
}

func (w *Writer) flush() error {
w.mu.Lock()
defer w.mu.Unlock()
if w.bw == nil {
return nil
}
return w.bw.Flush()
}

// appendInt appends the decimal form of x to b and returns the result.
// If the decimal form (excluding sign) is shorter than width, the result is padded with leading 0's.
// Duplicates functionality in strconv, but avoids dependency.
func appendInt(b []byte, x int, width int) []byte {
u := uint(x)
if x < 0 {
b = append(b, '-')
u = uint(-x)
}

// 2-digit and 4-digit fields are the most common in time formats.
utod := func(u uint) byte { return '0' + byte(u) }
switch {
case width == 2 && u < 1e2:
return append(b, utod(u/1e1), utod(u%1e1))
case width == 4 && u < 1e4:
return append(b, utod(u/1e3), utod(u/1e2%1e1), utod(u/1e1%1e1), utod(u%1e1))
}

// Compute the number of decimal digits.
var n int
if u == 0 {
n = 1
}
for u2 := u; u2 > 0; u2 /= 10 {
n++
}

// Add 0-padding.
for pad := width - n; pad > 0; pad-- {
b = append(b, '0')
}

// Ensure capacity.
if len(b)+n <= cap(b) {
b = b[:len(b)+n]
} else {
b = append(b, make([]byte, n)...)
}

// Assemble decimal in reverse order.
i := len(b) - 1
for u >= 10 && i > 0 {
q := u / 10
b[i] = utod(u - q*10)
u = q
i--
}
b[i] = utod(u)
return b
}

0 comments on commit 9acf52b

Please sign in to comment.