日志处理有三类使用环境,开发环境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. Python开发程序:生产环境下实时统计网站访问日志信息

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

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

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

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

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

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

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

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

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

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

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

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

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

  9. ruby on rails 生产环境调试项目日志查看

    1.项目目录:log/production.log 2.nginx日志:/opt/nginx/logs 生产环境下做的任何更改都要重启服务器 重启 sudo kill $(cat /opt/nginx ...

随机推荐

  1. HDU - 1427 / UESTC - 1252 经典dfs

    很好奇为什么hzwer那种稍改一下还是无法过样例,代码我没看出问题 换了一种用桶组合挑取两个数不断回溯的做法 这是HDU1427的代码,后者改一改就行了 #include<bits/stdc++ ...

  2. ASP.NET Core文件上传、下载与删除

    首先我们需要创建一个form表单如下: <form method="post" enctype="multipart/form-data" asp-con ...

  3. mysql 查询 TOP N 问题

    Q:有一个学生成绩表,表名 stu(学生表),字段有:id(主键),name(学生姓名),subject(学科),score(分数) 1.查询该表中,所有科目都及格的学生 ; 说明:都及格的话,就是最 ...

  4. shell 函数与内置变量

    1,特殊shell变量 $# 传递到脚本的参数个数 $* 以一个单字符串显示所有向脚本传递的参数 $$ 脚本运行的当前进程ID号 $! 后台运行的最后一个进程的ID号 $@ 与$*相同,但是使用时加引 ...

  5. PyCharm常见用法

    1.设置python运行版本: File-->Setting-->Project-->Project Interpreter 2.代码批量左移/右移一个tab: 鼠标选中行,Tab右 ...

  6. ckeditor和ckfinder

    ckeditor是一个所见即所得的富文本编辑器,用来代替drupal自带的编辑器. 但是从drupal.com下载的ckeditor模块本身没有实现功能,它指向了由cdn.ckeditor.com所提 ...

  7. RefulApi自动化测试~Hitchhiker的部署

    Hitchhiker是一个在github上开源的项目,被善友大哥收录到了它的微服务工具包里<开源的 Restful Api 集成测试工具 Hitchhiker>,同时源代码也开源到了git ...

  8. Ural 1260 Nudnik Photographer

    Problem Description If two people were born one after another with one second difference and one of ...

  9. TOJ 3660 家庭关系

    描述 给定若干家庭成员之间的关系,判断2个人是否属于同一家庭,即2个人之间均可以通过这些关系直接或者间接联系. 输入 输入数据有多组,每组数据的第一行为一个正整数n(1<=n<=100), ...

  10. Nginx图片服务器

    最近总项目因为需要显示图片,就使用了Nginx,使用很简单,下面简单介绍一下怎么用 压缩包下载地址,解压即用 http://files.cnblogs.com/files/sanduweiliangx ...