日志系统基本概念
日志系统必要的因素
- 日志级别:FATAL,WARNING,NOTICE,DEBUG,ALL …
- 调用栈:文件,函数,行数,日期
php可以通过debug_backtrace()获取,go通过runtime.Caller()获取 - 日志信息:自定义
日志系统性能考量
我们知道日志系统是需要写入磁盘的,在大并发量下,写磁盘是一个昂贵的操作.那么如何避免写入磁盘呢
- 缓冲然后写入
- 通过本机起一个udp服务收集日志.每次写入时通过往127.0.0.1:udpport发送日志
各种不同的写入日志方式
正常写入
如下为代码示例1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34package logger
import (
"log"
"os"
"sync"
)
var fileName string
var fileHandle *os.File
var err error
var mu sync.Mutex
func init() {
fileName = "/tmp/logger.log"
fileHandle, err = os.OpenFile(fileName, os.O_RDWR|os.O_CREATE, 0755)
if err != nil {
log.Fatal(err)
}
}
//Logger direct logger
func Logger(log string) {
//fmt.Fprint(fileHandle, log)
defer mu.Unlock()
mu.Lock()
fileHandle.WriteString(log)
}
//Close close logger filehandle
func Close() {
fileHandle.Close()
}缓冲写入
如下为代码示例1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46package logger
import (
"bufio"
"log"
"os"
)
var maxBufferSize int
var fileNameBuffer string
var fileHandleBuffer *os.File
var writer *bufio.Writer
func init() {
maxBufferSize = 1 * 1024 * 1024
fileNameBuffer = "/tmp/loggerbuffer.log"
var err error
fileHandleBuffer, err = os.OpenFile(fileNameBuffer, os.O_RDWR|os.O_CREATE, 0755)
if err != nil {
log.Fatal(err)
}
writer = bufio.NewWriterSize(fileHandleBuffer, maxBufferSize)
}
//BufferLogger buffer logger
func BufferLogger(log string) {
defer mu.Unlock()
mu.Lock()
if writer.Available() < len(log) {
writer.Flush()
}
//fmt.Fprint(writer, log)
writer.WriteString(log)
}
//BufferFlush destruct buffer logger
func BufferFlush() {
writer.Flush()
}
//BufferClose close buffer logger filehandle
func BufferClose() {
fileHandleBuffer.Close()
}起一个udp服务,然后发送日志到udp服务
如下为代码示例1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27package logger
import (
"log"
"net"
)
var conn *net.UDPConn
func init() {
sip := net.ParseIP("127.0.0.1")
srcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
dstAddr := &net.UDPAddr{IP: sip, Port: 9981}
var err error
conn, err = net.DialUDP("udp", srcAddr, dstAddr)
if err != nil {
log.Fatal(err)
}
}
//UDPLogger buffer logger
func UDPLogger(log string) {
defer mu.Unlock()
mu.Lock()
conn.Write([]byte(log))
}
udp server的代码如下:
1 | package main |
三种方法的压测函数如下:
1 | package logger |
压测结果如下:
1 | go test -bench=. |
再次执行,如下:
1 | go test -bench=. |
结论
缓冲写入>udp写入>直接写入
观察测试结果可以看到,随着写入数据的增加,直接写入会有一个寻址时间导致逐步变慢.而缓冲写入和udp写入不受影响.并且缓冲写入几乎等价于内存操作,但缺点是系统崩溃时可能会丢失部分日志数据