http://blog.lightxue.com/how-logrotate-works/

日志实在是太有用了,它记录了程序运行时各种信息。通过日志可以分析用户行为,记录运行轨迹,查找程序问题。可惜磁盘的空间是有限的,就像飞机里的黑匣子,记录的信息再重要也只能记录最后一段时间发生的事。为了节省空间和整理方便,日志文件经常需要按时间或大小等维度分成多份,删除时间久远的日志文件。这就是通常说的日志滚动(log rotation)。

最近整理nginx日志,用了一个类Unix系统上的古老工具——logrotate,发现意外的好用。想了解这个工具的用法推荐看这里。我了解了一下这个工具的运行机制和原理,觉得挺有趣的。

运行机制

logrotate在很多Linux发行版上都是默认安装的。系统会定时运行logrotate,一般是每天一次。系统是这么实现按天执行的。crontab会每天定时执行/etc/cron.daily目录下的脚本,而这个目录下有个文件叫logrotate。在centos上脚本内容是这样的:

/etc/cron.daily/logrotate

1
2
3
4
5
6
/usr/sbin/logrotate /etc/logrotate.conf >/dev/null 2>&1
EXITVALUE=$?
if [ $EXITVALUE != 0 ]; then
/usr/bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]"
fi
exit 0

可以看到这个脚本主要做的事就是以/etc/logrotate.conf为配置文件执行了logrotate。就是这样实现了每天执行一次logrotate。

因为我的系统执行/etc/cron.daily目录下的脚本不是我想滚动日志的时间,所以我把/etc/cron.daily/logrotate拷了出来,改了一下logrotate配置文件的路径,然后在crontab里加上一条指定时间执行这个脚本的记录,自定义周期滚动日志就大功告成了。这种自定义的方式有两点要注意:

  1. 配置文件里一定要配置rotate 文件数目这个参数。如果不配置默认是0个,也就是只允许存在一份日志,刚切分出来的日志会马上被删除。多么痛的领悟,说多了都是泪。

  2. 执行logrotate命令最好加-f参数,不然有时候配置文件修改的内容不生效。

很多程序的会用到logrotate滚动日志,比如nginx。它们安装后,会在/etc/logrotate.d这个目录下增加自己的logrotate的配置文件。logrotate什么时候执行/etc/logrotate.d下的配置呢?看到/etc/logrotate.conf里这行,一切就不言而喻了。

1
include /etc/logrotate.d

原理

logrotate是怎么做到滚动日志时不影响程序正常的日志输出呢?logrotate提供了两种解决方案。

Linux文件操作机制

介绍一下相关的Linux下的文件操作机制。

Linux文件系统里文件和文件名的关系如下图。

目录也是文件,文件里存着文件名和对应的inode编号。通过这个inode编号可以查到文件的元数据和文件内容。文件的元数据有引用计数、操作权限、拥有者ID、创建时间、最后修改时间等等。文件件名并不在元数据里而是在目录文件中。因此文件改名、移动,都不会修改文件,而是修改目录文件。

借《UNIX环境高级编程》里的图说一下进程打开文件的机制。

进程每新打开一个文件,系统会分配一个新的文件描述符给这个文件。文件描述符对应着一个文件表。表里面存着文件的状态信息(O_APPEND/O_CREAT/O_DIRECT…)、当前文件位置和文件的inode信息。系统会为每个进程创建独立的文件描述符和文件表,不同进程是不会共用同一个文件表。正因为如此,不同进程可以同时用不同的状态操作同一个文件的不同位置。文件表中存的是inode信息而不是文件路径,所以文件路径发生改变不会影响文件操作。

方案1:create

默认方案没有名字,姑且叫它create吧。因为这个方案会创建一个新的日志文件给程序输出日志,而且第二个方案名copytruncate是个配置项,与create配置项是互斥的。

这个方案的思路是重命名原日志文件,创建新的日志文件。详细步骤如下:

  1. 重命名程序当前正在输出日志的程序。因为重命名只会修改目录文件的内容,而进程操作文件靠的是inode编号,所以并不影响程序继续输出日志。

  2. 创建新的日志文件,文件名和原来日志文件一样。虽然新的日志文件和原来日志文件的名字一样,但是inode编号不一样,所以程序输出的日志还是往原日志文件输出。

  3. 通过某些方式通知程序,重新打开日志文件。程序重新打开日志文件,靠的是文件路径而不是inode编号,所以打开的是新的日志文件。

什么方式通知程序我重新打开日志呢,简单粗暴的方法是杀死进程重新打开。很多场景这种作法会影响在线的服务,于是有些程序提供了重新打开日志的接口,比如可以通过信号通知nginx。各种IPC方式都可以,前提是程序自身要支持这个功能。

有个地方值得一提,一个程序可能输出了多个需要滚动的日志文件。每滚动一个就通知程序重新打开所有日志文件不太划得来。有个sharedscripts的参数,让程序把所有日志都重命名了以后,只通知一次。

方案2:copytruncate

如果程序不支持重新打开日志的功能,又不能粗暴地重启程序,怎么滚动日志呢?copytruncate的方案出场了。

这个方案的思路是把正在输出的日志拷(copy)一份出来,再清空(trucate)原来的日志。详细步骤如下:

  1. 拷贝程序当前正在输出的日志文件,保存文件名为滚动结果文件名。这期间程序照常输出日志到原来的文件中,原来的文件名也没有变。

  2. 清空程序正在输出的日志文件。清空后程序输出的日志还是输出到这个日志文件中,因为清空文件只是把文件的内容删除了,文件的inode编号并没有发生变化,变化的是元信息中文件内容的信息。

结果上看,旧的日志内容存在滚动的文件里,新的日志输出到空的文件里。实现了日志的滚动。

这个方案有两个有趣的地方。

  1. 文件清空并不影响到输出日志的程序的文件表里的文件位置信息,因为各进程的文件表是独立的。那么文件清空后,程序输出的日志应该接着之前日志的偏移位置输出,这个位置之前会被\0填充才对。但实际上logroate清空日志文件后,程序输出的日志都是从文件开始处开始写的。这是怎么做到的?这个问题让我纠结了很久,直到某天灵光一闪,这不是logrotate做的,而是成熟的写日志的方式,都是用O_APPEND的方式写的。如果程序没有用O_APPEND方式打开日志文件,变会出现copytruncate后日志文件前面会被一堆\0填充的情况。

  2. 日志在拷贝完到清空文件这段时间内,程序输出的日志没有备份就清空了,这些日志不是丢了吗?是的,copytruncate有丢失部分日志内容的风险。所以能用create的方案就别用copytruncate。所以很多程序提供了通知我更新打开日志文件的功能来支持create方案,或者自己做了日志滚动,不依赖logrotate。

总结

logrotate是个优秀的日志滚动工具,它是用蜂蜜,川贝,桔梗,加上天山雪莲配制而成,不须冷藏,也没有防腐剂,除了毒性猛烈之外,味道还很好吃。实在是居家旅行、杀人灭口必备良药!

logrotate机制与原理[转载]的更多相关文章

  1. 彻底弄懂HTTP缓存机制及原理-转载

    首先附上原文地址,非常感谢博主大神的分享彻底弄懂HTTP缓存机制及原理 前言     Http 缓存机制作为 web 性能优化的重要手段,对于从事 Web 开发的同学们来说,应该是知识体系库中的一个基 ...

  2. logrotate机制&原理

    logrotate机制&原理 centos系统中默认安装logrotate,logrotate主配置文件:/etc/logrotate.conf,其中定义了系统默认的logrotate规则,当 ...

  3. Session执行机制与原理

    Session执行机制与原理 作者:Stanley 罗昊 [转载请注明出处和署名,谢谢!] 什么是Session 首先了解一下Session的中文意思:一次会话,什么是一次会话呢?我举个例子:就我们现 ...

  4. Redis数据持久化机制AOF原理分析一---转

    http://blog.csdn.net/acceptedxukai/article/details/18136903 http://blog.csdn.net/acceptedxukai/artic ...

  5. 深度剖析Spring Boot自动装配机制实现原理

    在前面的分析中,Spring Framework一直在致力于解决一个问题,就是如何让bean的管理变得更简单,如何让开发者尽可能的少关注一些基础化的bean的配置,从而实现自动装配.所以,所谓的自动装 ...

  6. 【面试普通人VS高手系列】Spring Boot中自动装配机制的原理

    最近一个粉丝说,他面试了4个公司,有三个公司问他:"Spring Boot 中自动装配机制的原理" 他回答了,感觉没回答错误,但是怎么就没给offer呢? 对于这个问题,看看普通人 ...

  7. Android(java)学习笔记202:Handler消息机制的原理和实现

     联合学习 Android 异步消息处理机制 让你深入理解 Looper.Handler.Message三者关系   1. 首先我们通过一个实例案例来引出一个异常: (1)布局文件activity_m ...

  8. Java序列化的机制和原理

    Java序列化的机制和原理 本文讲解了Java序列化的机制和原理.从文中你可以了解如何序列化一个对象,什么时候需要序列化以及Java序列化的算法. 有关Java对象的序列化和反序列化也算是Java基础 ...

  9. Java 反射 设计模式 动态代理机制详解 [ 转载 ]

    Java 反射 设计模式 动态代理机制详解 [ 转载 ] @author 亦山 原文链接:http://blog.csdn.net/luanlouis/article/details/24589193 ...

随机推荐

  1. Hibernate入门与简谈

    Hibernate jdbc Java Databases Connectivity, 他是提供了一组Java API来访问关系数据库的Java程序.这些Java API 可以使Java应用程序执行S ...

  2. php复习第一章—1.1php 简介

    php语言是构建互联网应用的后端最佳选择 跨平台 性能优越,稳定性强 语法简单,易于上手 丰富完善的技术扩展 LAMP平台成熟稳定,低成本构建 回顾 web 网页本质是一个文本文件,由浏览器解释文件内 ...

  3. Android 2016新技术

    Android 2016新技术 版权声明:本文为博主原创文章,未经博主允许不得转载. 2016你需要了解Android有以下新兴的技术与框架,有些也许还不成熟,但是你应该去了解下,也许就是未来的方向. ...

  4. 硬件抽象层——HAL

    (一)为什么要在android中加入HAL Linux系统中Linux驱动有两种类型的代码:访问硬件寄存器的代码——调用的Linux内核的标准函数进行的标准操作  业务逻辑代码——有些企业或个人并不想 ...

  5. 366. Find Leaves of Binary Tree

    Given a binary tree, collect a tree's nodes as if you were doing this: Collect and remove all leaves ...

  6. 将IIS6.0中的某页设置成首页

  7. label标签

  8. linux装JDK

    一.安装sun java升 级到ubuntu 11.10之后.ubuntu 默认自带的是openjdkjava.默认情况这个是能够使用的,但是如果你想搭建专业的开发环境,还是需要使用sun的靠谱.另外 ...

  9. (zhuan)Python 虚拟环境:Virtualenv

    Python 虚拟环境:Virtualenv zhuanzi: http://liuzhijun.iteye.com/blog/1872241 virtualenv virtualenv用于创建独立的 ...

  10. 用Unity代码通过Xml配置生成GameObject之——前两天掉的坑

    1. Resources.Load(path),path不是绝对路径,而是相对"Resources/"的相对路径!如: 要想Instantiate则代码应该如下: string m ...