日志处理有三类使用环境,开发环境DE,测试环境TE,生产环境PE。

前两类可以看成是一类,重要的是屏幕显示——termlog。生产环境中主要用的是socklog 和 filelog,即网络传输日志和文件日志。

基本框架

网络和文件日志的基本框架非常简单:

  1. Open file

  2. Write log message

  3. 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字节,效率更高。在另一篇文章中已经讨论过了。详见:

log4go的一些改进设想

原来的日志滚动处理

接下来要考虑是日志文件内容日积月累,越来越大怎么办?

在开发和测试环境中,这不是问题。因此常常被忽略,结果进入生产环境后磁盘满溢,系统瘫痪。

记得还是上世纪94年的时候,半夜坐火车到客户那里,在 Novell 服务器上的执行 purge 命令,运行了半个多小时……

所以,日志的滚动处理非常重要。假设滚动日志文件数为1。

  1. 日志文件超过一定的大小,触发滚动处理。

  2. 将原来存在的文件log.1删除

  3. 将当前文件重命名为log.1

  4. 等写入新的日志时,再判断文件状态,建立并打开新的日志文件。

于是,日志文件的大小被自动控制在一定范围内。使服务器的磁盘自动保持清洁高效的状态。

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
}
}
}

这意味着:

  1. 精确控制日志文件的大小

  2. 每次写入日志信息都要做一系列复杂判断

  3. 随时可能进行滚动日志处理

  4. 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()
}

希望这段程序能达到以下目的:

  1. 需要时启动例程。

  2. 只有一个启动例程。

  3. 退出系统时,等待例程结束,最多10秒。

newLog的格式为:

newLog := f.filename + time.Now().Format(".20060102-150405")

即使滚动日志处理出现问题,日志也能保存下来。


那么,最后的问题是,log4go可以进入生产环境吗?不试一试?

https://github.com/ccpaging/log4go

log4go的日志滚动处理——适应生产环境的需要的更多相关文章

  1. log4go的日志滚动处理——生产环境的适配

    日志处理有三类使用环境,开发环境DE,测试环境TE,生产环境PE. 前两类可以看成是一类,重要的是屏幕显示--termlog.生产环境中主要用的是socklog 和 filelog,即网络传输日志和文 ...

  2. 分布式日志框架Exceptionless之生产环境部署步骤

    Exceptionless 是一个开源的实时的日志收集框架,它将日志收集变得简单易用并且不需要了解太多的相关技术细节及配置.本篇基于我的上一篇<基于Exceptionless实现分布式日志> ...

  3. JVM(五) 生产环境内存溢出调优

    1.gc配置参数 1.1 控制台打印gc日志 -verbose:gc -XX:+PrintGCDetails -XX:+PrintHeapAtGC(详细的gc信息) 1.2 输出gc日志到指定文件 - ...

  4. Python开发程序:生产环境下实时统计网站访问日志信息

    日志实时分析系统 生产环境下有需求:要每搁五分钟统计下这段时间内的网站访问量.UV.独立IP等信息,用直观的数据表格表现出来 环境描述: 网站为Nginx服务,系统每日凌晨会对日志进行分割,拷贝到其他 ...

  5. asp.net生产环境和开发环境的错误日志包装策略

    对于错误日志的输出,我们借助web.config的两个标志位: <!--全局包装异常处理页面,只有在PageError和Application_Error做清除错误操作才可不跳转--> & ...

  6. 教你50招提升ASP.NET性能(十二):在生产环境,仔细考虑你需要记录哪些日志

    (18)When in production, carefully consider what you need to log 招数18: 在生产环境,仔细考虑你需要记录哪些日志 Many peopl ...

  7. logminer实战之生产环境写入数据字典,dg环境查询拷贝日志,测试环境进行挖掘,输出结果

    应客户需要,对某一天的日志进行挖掘,分析日均归档日志切换数量20增长至40的原因,是什么表的dml操作导致的日志量剧增,最终定位某个应用(需要客户自己进行甄别) 操作说明及介绍: 1.客户10.2.0 ...

  8. log4net 本地环境没问题 生产环境无法输出日志

    log4net输出日志大概分两块 1.程序代码编写问题 2.配置文件问题 当程序本地可以正常输出日志.配置文件也都正常可用的情况下,发布到生产环境后,有的程序可以输出日志,有的程序不可以输出,程序无问 ...

  9. Python开发【项目】:生产环境下实时统计网站访问日志信息

    日志实时分析系统 生产环境下有需求:要每搁五分钟统计下这段时间内的网站访问量.UV.独立IP等信息,用直观的数据表格表现出来 环境描述: 网站为Nginx服务,系统每日凌晨会对日志进行分割,拷贝到其他 ...

随机推荐

  1. Android的UI调优

    对于一个App的UI而言,在流畅性上的改进目标其实就是降低屏幕绘制的延迟,创建流畅和稳定的帧率以避免卡顿. 在理想情况下,全部的测量.布局和绘制的时间最好在16ms以内,这样才能保证屏幕运行的顺畅性. ...

  2. Cordova各个插件使用介绍系列(二)—$cordovaBarcodeScanner扫描二维码与生成二维码

    详情链接地址:http://www.ncloud.hk/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/cordova-2-cordovabarcodescanner/ 这是 ...

  3. Log4j中配置日志文件相对路径

    方法一. 解决的办法自然是用相对路径代替绝对路径,其实log4j的FileAppender本身就有这样的机制,如:log4j.appender.logfile.File=${WORKDIR}/logs ...

  4. jq和js插件的各个文件夹里放置的内容

    1. demo文件夹,存放各种实例. 2. dist文件夹,全称是distribution.在某些框架中,因为开发和发布的内容或者代码形式是不一样的(比如利用Grunt压缩等等),这时候就需要一个存放 ...

  5. ECMAScript 6 中的快捷语法汇总及代码示例

    对于每个 JavaScript 开发人员,快捷语法都是必备技能之一,下面就集中介绍这些快捷语法. 三元运算符 传统写法 const x = 20; let answer; if (x > 10) ...

  6. SQL手动注入解析

    作者:震灵 注入环境:DVWA 探测步骤: 1.首先探测是否可以注入以及注入方式 原SQL语句为 SELECT * FROM xxx WHERE a=''; 注入后为 SELECT * FROM xx ...

  7. 怎么利用composer创建laravel项目

    前提:已经安装了composer的电脑 创建laravel项目: 第一步: 找到你要创建文件的地方 然后打开doc,输入:composer create_project laravel/laravel ...

  8. 第二章(jQuery选择器)

    2.1jQuery选择器是什么 1.CSS选择器 选择器 示例 选择器 示例 标签选择器 a{ } p{ } ul{ } ID选择器 #ID{ } 类选择器 .class{ } 群组选择器 td,p, ...

  9. 在ASP.NET CORE 2.0使用SignalR技术

    一.前言 上次讲SignalR还是在<在ASP.NET Core下使用SignalR技术>文章中提到,ASP.NET Core 1.x.x 版本发布中并没有包含SignalR技术和开发计划 ...

  10. JAVA程序打包成exe小程序的过程

    编程软件:myeclipse2014 打包exe软件:exe4j 1:在myeclipse2014新建java项目编写程序 2:打包成jar,分两种情况(有无外部依赖包) 无外部依赖包:点击项目--- ...