Golang日志加速需采用异步写入与缓冲机制,通过goroutine+channel实现,选择zap等高性能日志库,合理设置缓冲大小,结合日志切割与sync.WaitGroup优雅关闭,确保性能与数据安全。

Golang日志输出加速的关键在于将同步写入磁盘的操作改为异步,并利用缓冲机制减少I/O次数。简单来说,就是先攒着,再一起写。
异步写入与缓冲日志方案
标准库
log
logrus
zap
zerolog
zap
logrus
zerolog
zap
实现异步写入,最常用的方法就是使用 goroutine 和 channel。主 goroutine 将日志消息发送到 channel,另一个专门的 goroutine 从 channel 中读取消息并写入文件。这样,主 goroutine 就不需要等待磁盘 I/O 完成,从而提高了性能。
立即学习“go语言免费学习笔记(深入)”;
package main
import (
"fmt"
"log"
"os"
"time"
)
var (
logChan = make(chan string, 1000) // Buffered channel
logFile *os.File
)
func init() {
var err error
logFile, err = os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
log.Fatal("Failed to open log file:", err)
}
go writeLogsToFile()
}
func writeLogsToFile() {
defer logFile.Close()
for logMsg := range logChan {
_, err := logFile.WriteString(logMsg + "\n")
if err != nil {
fmt.Println("Error writing to log file:", err) // 打印到控制台,避免无限循环
}
}
}
func Log(message string) {
logChan <- fmt.Sprintf("%s: %s", time.Now().Format(time.RFC3339), message)
}
func main() {
for i := 0; i < 100; i++ {
Log(fmt.Sprintf("This is log message number %d", i))
// Simulate some work
time.Sleep(time.Millisecond * 10)
}
close(logChan) // Signal the logger goroutine to exit
// Wait for the logger goroutine to finish processing all logs
time.Sleep(time.Second * 2)
fmt.Println("Done!")
}这个例子中,
logChan
缓冲大小的选择是一个权衡。太小,起不到缓冲的作用;太大,占用内存,而且如果程序崩溃,可能会丢失大量未写入磁盘的日志。一般来说,可以根据日志产生的频率和磁盘 I/O 性能来调整。可以先设置一个初始值,例如 1000,然后通过监控程序运行时的内存占用和磁盘 I/O 情况,逐步调整到最佳值。更好的做法是根据实际业务场景进行压测,找到一个平衡点。
日志文件会随着时间推移变得越来越大,不利于管理和分析。因此,需要定期对日志文件进行切割,例如每天、每周或每月生成一个新的日志文件。日志切割的实现方式有很多,可以自己编写代码实现,也可以使用现成的工具,例如
logrotate
rotator
使用 Go 实现日志切割的一个简单示例:
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"time"
)
var (
logFile *os.File
logPath string
)
func init() {
logPath = "app.log"
rotateLogFile() // Initial rotation
}
func rotateLogFile() {
if logFile != nil {
logFile.Close()
}
newLogPath := filepath.Join(filepath.Dir(logPath), fmt.Sprintf("%s.%s", filepath.Base(logPath), time.Now().Format("20060102150405")))
err := os.Rename(logPath, newLogPath)
if err != nil && !os.IsNotExist(err) {
fmt.Println("Error rotating log file:", err)
}
var err2 error
logFile, err2 = os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err2 != nil {
log.Fatal("Failed to open log file:", err2)
}
log.SetOutput(logFile)
}
func Log(message string) {
log.Println(message)
// Check if rotation is needed (e.g., every minute for testing)
now := time.Now()
if now.Second() == 0 { // Rotate every minute
rotateLogFile()
}
}
func main() {
for i := 0; i < 5; i++ {
Log(fmt.Sprintf("This is log message number %d", i))
time.Sleep(time.Second * 10)
}
}这个例子中,
rotateLogFile
程序退出时,需要确保所有缓冲中的日志都写入磁盘。一种方法是在
main
defer
func main() {
defer func() {
close(logChan)
time.Sleep(time.Second * 2) // Wait for logger goroutine to finish
if logFile != nil {
logFile.Close()
}
}()
// ... your main logic ...
}另一种更优雅的方法是使用
sync.WaitGroup
package main
import (
"fmt"
"log"
"os"
"sync"
"time"
)
var (
logChan = make(chan string, 1000) // Buffered channel
logFile *os.File
wg sync.WaitGroup
)
func init() {
var err error
logFile, err = os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
log.Fatal("Failed to open log file:", err)
}
wg.Add(1) // Increment the WaitGroup counter
go writeLogsToFile()
}
func writeLogsToFile() {
defer wg.Done() // Decrement the WaitGroup counter when the goroutine finishes
defer logFile.Close()
for logMsg := range logChan {
_, err := logFile.WriteString(logMsg + "\n")
if err != nil {
fmt.Println("Error writing to log file:", err) // 打印到控制台,避免无限循环
}
}
}
func Log(message string) {
logChan <- fmt.Sprintf("%s: %s", time.Now().Format(time.RFC3339), message)
}
func main() {
defer close(logChan) // Signal the logger goroutine to exit
for i := 0; i < 100; i++ {
Log(fmt.Sprintf("This is log message number %d", i))
// Simulate some work
time.Sleep(time.Millisecond * 10)
}
wg.Wait() // Wait for the logger goroutine to finish processing all logs
fmt.Println("Done!")
}使用
sync.WaitGroup
总而言之,Golang 日志加速的核心在于异步和缓冲。选择合适的日志库,合理设置缓冲大小,实现日志切割,并优雅地处理程序退出时的日志刷新,可以显著提高日志输出的性能。
以上就是Golang的日志输出如何加速 异步写入与缓冲日志方案的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号