log4go的日志滚动处理——适应生产环境的需要
日志处理有三类使用环境,开发环境DE,测试环境TE,生产环境PE。
前两类可以看成是一类,重要的是屏幕显示——termlog。生产环境中主要用的是socklog 和 filelog,即网络传输日志和文件日志。
基本框架
网络和文件日志的基本框架非常简单:
- Open file 
- Write log message 
- Close file 
golang log 都支持。
// New creates a new Logger. The out variable sets the
// destination to which log data will be written.
// The prefix appears at the beginning of each generated log line.
// The flag argument defines the logging properties.
func New(out io.Writer, prefix string, flag int) *Logger {
	return &Logger{out: out, prefix: prefix, flag: flag}
}
设置不同的io.Writer而已。
type FileWriter struct {
	filename string
	fileflush  int
	file   *os.File
	bufWriter *bufio.Writer
	writer io.Writer
}
func (fw *FileWriter) openFile(flag int) (*os.File, error) {
	fd, err := os.OpenFile(fw.filename, flag, DefaultFilePerm)
	if err != nil {
		return nil, err
	}
	fw.file = fd
	fw.writer = fw.file
	if fw.fileflush > 0 {
		fw.bufWriter = bufio.NewWriterSize(fw.file, fw.fileflush)
		fw.writer = fw.bufWriter
	}
	return fd, nil
}
当然,带缓冲写文件的 bufio,一次写入4k或者8k字节,效率更高。在另一篇文章中已经讨论过了。详见:
原来的日志滚动处理
接下来要考虑是日志文件内容日积月累,越来越大怎么办?
在开发和测试环境中,这不是问题。因此常常被忽略,结果进入生产环境后磁盘满溢,系统瘫痪。
记得还是上世纪94年的时候,半夜坐火车到客户那里,在 Novell 服务器上的执行 purge 命令,运行了半个多小时……
所以,日志的滚动处理非常重要。假设滚动日志文件数为1。
- 日志文件超过一定的大小,触发滚动处理。 
- 将原来存在的文件 - log.1删除
- 将当前文件重命名为 - log.1
- 等写入新的日志时,再判断文件状态,建立并打开新的日志文件。 
于是,日志文件的大小被自动控制在一定范围内。使服务器的磁盘自动保持清洁高效的状态。
log4go v4 的触发滚动处理如下:
func (w *FileLogWriter) LogWrite(rec *LogRecord) {
	now := time.Now()
	if (w.maxlines > 0 && w.maxlines_curlines >= w.maxlines) ||
		(w.maxsize > 0 && w.maxsize_cursize >= w.maxsize) ||
		(w.daily && now.Day() != w.daily_opendate.Day()) {
		// open the file for the first time
		if err := w.intRotate(); err != nil {
			fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
			return
		}
	}
}
这意味着:
- 精确控制日志文件的大小 
- 每次写入日志信息都要做一系列复杂判断 
- 随时可能进行滚动日志处理 
- 当 - rotate = 0时,不进行日志滚动处理。这可能是个坑。开发和测试的时候,日志文件的滚动处理可能被忽略。
其实,在生产环境中,什么时候进行滚动处理,才是真正重要的。通常都会选择每天凌晨系统较为空闲的时候。
如果是一个24小时满载的系统,或者对系统稳定性要求特别高,或者对日志的可靠性要求特别高,建议用socklog。
将日志信息发送给专门的日志服务程序进行处理。参照log4go的示例程序,SimpleNetLogServer.go
按照生产环境的要求重写
- 始终进行日志滚动处理。当日志滚动数为0时,超过缺省的文件大小,关闭并删除当前的日志文件。 
- 按照生产环境的要求设置缺省文件名,目录,大小,滚动处理的时间和间隔。 
- 精确控制滚动处理的时间。详见日志:http://www.cnblogs.com/ccpaging/p/7203431.html 
- 支持任意的时间间隔检查日志文件大小。可以每次都转存日志。 
- 平时写入日志时不再判断文件大小。 
- 简化处理流程。写信息时判断并建立打开当前日志文件。进行滚动处理时,关闭当前日志文件。 
- 继续写新的日志与滚动日志文件处理,可并行。为将来压缩日志文件提供了可能。 
保证只启动一个日志滚动处理例程
调用日志处理就一句话:
func (f *FileLogWriter) intRotate() {
	f.Lock()
	defer f.Unlock()
	if n, _ := f.SeekFile(0, os.SEEK_CUR); n <= f.maxsize {
		return
	}
	// File existed and File size > maxsize
	if len(f.footer) > 0 { // Append footer
		f.WriteString(FormatLogRecord(f.footer, &LogRecord{Created: time.Now()}))
	}
	f.CloseFile() 
	if f.rotate <= 0 {
		os.Remove(f.filename)
		return
	}
	// File existed. File size > maxsize. Rotate
	newLog := f.filename + time.Now().Format(".20060102-150405")
	err := os.Rename(f.filename, newLog)
	if err != nil {
		fmt.Fprintf(os.Stderr, "FileLogWriter(%q): Rename to %s. %v\n", f.filename, newLog, err)
		return
	}
	go FileRotate(f.filename, f.rotate, newLog)  // 调用日志滚动处理
}
总觉得哪里不对?如果滚动日志检查的时间间隔短,处理的时间意外地长,就有可能出现同时调用两个例程的情况。
这种情况肯定很少发生。一旦发生,就是个深坑。运维的童鞋要骂娘了……此处省略若干字。
好吧。赶紧做一段程序压压惊。
package main
import (
	"fmt"
	"time"
)
type FileRotate struct {
	rotCount int
	rotFiles chan string
}
var (
	DefaultRotateLen = 5
)
func (r *FileRotate) InitRot() {
	r.rotCount = 0
	r.rotFiles = make(chan string, DefaultRotateLen)
}
func (r *FileRotate) RotFile(filename string, rotate int, newLog string) {
	r.rotFiles <- newLog
	if r.rotCount > 0 {
		fmt.Println("queued", newLog)
		return
	}
	r.rotCount++
	fmt.Println("start")
	for len(r.rotFiles) > 0 {
		file, _ := <- r.rotFiles
		fmt.Println("handle", file)
		time.Sleep(2 * time.Second)
	}
	fmt.Println("quit")
	r.rotCount--
}
func (r *FileRotate) CloseRot() {
	for i := 10; i > 0; i-- {
		if r.rotCount <= 0 {
			break
		}
		time.Sleep(1 * time.Second)
	}
	close(r.rotFiles)
	// drain the files not rotated
	for file := range r.rotFiles {
		fmt.Println(file)
	}
}
func main() {
	var r FileRotate;
	r.InitRot()
	for i := 0; i < 5; i++ {
		go r.RotFile("filename", 10, fmt.Sprintf("file%d", i))
		time.Sleep(1 * time.Second)
	}
	time.Sleep(5 * time.Second)
	for i := 5; i < 10; i++ {
		go r.RotFile("filename", 10, fmt.Sprintf("file%d", i))
		time.Sleep(1 * time.Second)
	}
	r.CloseRot()
}
希望这段程序能达到以下目的:
- 需要时启动例程。 
- 只有一个启动例程。 
- 退出系统时,等待例程结束,最多10秒。 
newLog的格式为:
newLog := f.filename + time.Now().Format(".20060102-150405")
即使滚动日志处理出现问题,日志也能保存下来。
那么,最后的问题是,log4go可以进入生产环境吗?不试一试?
https://github.com/ccpaging/log4go
log4go的日志滚动处理——适应生产环境的需要的更多相关文章
- log4go的日志滚动处理——生产环境的适配
		日志处理有三类使用环境,开发环境DE,测试环境TE,生产环境PE. 前两类可以看成是一类,重要的是屏幕显示--termlog.生产环境中主要用的是socklog 和 filelog,即网络传输日志和文 ... 
- 分布式日志框架Exceptionless之生产环境部署步骤
		Exceptionless 是一个开源的实时的日志收集框架,它将日志收集变得简单易用并且不需要了解太多的相关技术细节及配置.本篇基于我的上一篇<基于Exceptionless实现分布式日志> ... 
- JVM(五) 生产环境内存溢出调优
		1.gc配置参数 1.1 控制台打印gc日志 -verbose:gc -XX:+PrintGCDetails -XX:+PrintHeapAtGC(详细的gc信息) 1.2 输出gc日志到指定文件 - ... 
- Python开发程序:生产环境下实时统计网站访问日志信息
		日志实时分析系统 生产环境下有需求:要每搁五分钟统计下这段时间内的网站访问量.UV.独立IP等信息,用直观的数据表格表现出来 环境描述: 网站为Nginx服务,系统每日凌晨会对日志进行分割,拷贝到其他 ... 
- asp.net生产环境和开发环境的错误日志包装策略
		对于错误日志的输出,我们借助web.config的两个标志位: <!--全局包装异常处理页面,只有在PageError和Application_Error做清除错误操作才可不跳转--> & ... 
- 教你50招提升ASP.NET性能(十二):在生产环境,仔细考虑你需要记录哪些日志
		(18)When in production, carefully consider what you need to log 招数18: 在生产环境,仔细考虑你需要记录哪些日志 Many peopl ... 
- logminer实战之生产环境写入数据字典,dg环境查询拷贝日志,测试环境进行挖掘,输出结果
		应客户需要,对某一天的日志进行挖掘,分析日均归档日志切换数量20增长至40的原因,是什么表的dml操作导致的日志量剧增,最终定位某个应用(需要客户自己进行甄别) 操作说明及介绍: 1.客户10.2.0 ... 
- log4net 本地环境没问题 生产环境无法输出日志
		log4net输出日志大概分两块 1.程序代码编写问题 2.配置文件问题 当程序本地可以正常输出日志.配置文件也都正常可用的情况下,发布到生产环境后,有的程序可以输出日志,有的程序不可以输出,程序无问 ... 
- Python开发【项目】:生产环境下实时统计网站访问日志信息
		日志实时分析系统 生产环境下有需求:要每搁五分钟统计下这段时间内的网站访问量.UV.独立IP等信息,用直观的数据表格表现出来 环境描述: 网站为Nginx服务,系统每日凌晨会对日志进行分割,拷贝到其他 ... 
随机推荐
- ubuntu忽然不能登录,输入密码正确一直返回登录界面
			问题描述 由于配置eclipse命令启动,我修改了 /etc/environment 文件的内容,用命令 shutdown -r -now 重启后,输入密码正确一直返回登录界面. 查了下网上资料:系统 ... 
- python 文件操作(pickle)
			>>> with open('text.txt','wb') as data:pickle.dump(['a','b',2],data) 保存到文件 >>> wit ... 
- 关于对WEB标准以及W3C的理解与认识问题
			web标准简单来说可以分为结构.表现和行为.其中结构主要是有HTML标签组成.或许通俗点说,在页面body里面我们写入的标签都是为了页面的结构.表现即指css样式表,通过css可以是页面的结构标签更具 ... 
- 来吧学学.Net Core之登录认证与跨域资源使用
			序言 学习core登录认证与跨域资源共享是越不过的砍,所以我在学习中同样也遇到啦这两个问题,今天我们就用示例来演示下使用下这2个技术点吧. 本篇主要内容如下: 1.展示一个登录认证的简单示例 2.跨域 ... 
- Linux操作数据库基本
			连接数据库MySQL中每个命令后都要以分号;结尾1: mysql -h 192.168.10.250 -u root -p2:Enter password //要求你输入密码cug313@com3:s ... 
- group by和count联合使用问题
			要根据用户发布的产品数量来排序做分页,使用group ) FROM( SELECT uid,COU 工作中要根据用户发布的产品数量来排序做分页,使用group by uid 用count(uid) 来 ... 
- GC机制总结
			一.为什么需要GC 应用程序对资源操作,通常简单分为以下几个步骤: 1.为对应的资源分配内存 2.初始化内存 3.使用资源 4.清理资源 5.释放内存 应用程序对资源(内存使用)管理的方式,常见的一般 ... 
- 【Android Developers Training】 47. 序言:拍摄照片
			注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ... 
- Example006为弹出窗口加入关闭按钮
			<!-- 实例006为弹出的窗口加入关闭按钮 --> <head> <meta charset="UTF-8"> </head> & ... 
- 富文本编辑器嵌入指定html代码
			先把内容放入一个input中 <input id="detail" type="hidden" value="${sysCarousel.det ... 
