日志处理有三类使用环境,开发环境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. 初玩RAC

    之前因为项目的原因以及ReactiveCocoa框架导入到项目老是报错的原因,导致我这边一直没有能好好的将ReactiveCocoa运行起来,最近看了Hank老师的视频,而且项目中我们使用的就是OC, ...

  2. 关于MATLAB处理大数据坐标文件2017528

    第一次提交数据 增加了部分特征 3000数据测试中得分99 但是10万数据出现过拟化现象,正确率下降 总结:1.某些特征数据本身波动不大应该考虑放弃 2.一些特征虽然表面觉得差异显而易见,但是数据表达 ...

  3. node.js 发送http 请求

    自己研究了一下 node.js 的 http模块  下面为想服务器发送请求的代码 ,通过学习了解http 请求的过程 ,node.js 对http请求的原始封装比较低,以前php 可以用$_GET , ...

  4. js将字符串转化成函数:eval(logOutCallbackFun+"()");

    js将字符串转化成函数:eval(logOutCallbackFun+"()");

  5. UML简要

    一 概述 1.什么是UML? Unified Modeling Language,统一建模语言,用图形化的语言展示事物的结构,为交流与开发提供了便利. 2.UML分类 UML图形主要有用例图.类图.顺 ...

  6. 并发编程(四):ThreadLocal从源码分析总结到内存泄漏

    一.目录      1.ThreadLocal是什么?有什么用?      2.ThreadLocal源码简要总结?      3.ThreadLocal为什么会导致内存泄漏? 二.ThreadLoc ...

  7. javascript之ProtoBuf在websocket中的使用

    因为ProtoBuf的序列化效率和大小都非常好,所以它在网络通信上面应用越来越多:而webosocket也随着web3.0应用越来越广泛,而将这两个结合在一起的也会慢慢形成一种趋势:本人是为了测试自已 ...

  8. 真机调试方法- IOS/Android移动设备

    真机调试 调试安卓 方法一 开启手机的USB调试 安装运行项目 使用chrome步骤如下图 打开开发者工具 打开设备管理 选择设备进行debug 方法二: 直接在地址栏输入chrome://inspe ...

  9. Agile&DevOps究竟谁是魔法棒

    天下没有神奇的配方 很抱歉,文章的开头我就要说出这个残酷的事实 - 世界上没有任何工具可以魔法般的让你实现敏捷,精益,DevOps.如果只是依赖了工具的自动化,实现了自动化Dev或者Ops,那么别忘了 ...

  10. NewsDaoImpl

    package com.pb.news.dao.impl; import java.sql.CallableStatement;import java.sql.Connection;import ja ...