python实现tail(考虑到几种特殊情况)

1. 网上有很多种使用python实现tail的方法,大体思想都一样,典型的一种如下:

#!/usr/bin/env python
#!-*- coding: utf-8 -*-

################################################################################
#
# Copyright (c) 2015 XX.com, Inc. All Rights Reserved
#
################################################################################

################################################################################
# This module provide ...
# Third libs those depend on:
################################################################################

"""
Compiler Python 2.7.10
Authors: XX([email protected])
Date: 2015-09-09 12:57:41
Todo: nothing $XX$2015-09-09
"""

"""SYS LIBS
"""

import os
import re
import sys
import time

import traceback

"""THIRD LIBS
"""

try:
    # import the third libs there.
    pass
except ImportError as e:
    print e
    os._exit(-1)

"""CUSTOM libs
Strongly recommend using abs path when using custmo libs.
"""

# Good behaviors.
# It means refusing called like from xxx import *
# When `__all__` is []
__all__ = []

reload(sys)
sys.setdefaultencoding(‘utf-8‘)

def send_error(msg):
    """ Send error to email.
    """

    print msg

#********************************************************
#* Global defines start.                                *
#********************************************************

#********************************************************
#* Global defines end.                                  *
#********************************************************

class Tail(object):
    """
    Python-Tail - Unix tail follow implementation in Python.

    python-tail can be used to monitor changes to a file.

    Example:
        import tail

        # Create a tail instance
        t = tail.Tail(‘file-to-be-followed‘)

        # Register a callback function to be called when a new line is found in the followed file.
        # If no callback function is registerd, new lines would be printed to standard out.
        t.register_callback(callback_function)

        # Follow the file with 5 seconds as sleep time between iterations.
        # If sleep time is not provided 1 second is used as the default time.
        t.follow(s=5)
    """

    ‘‘‘ Represents a tail command. ‘‘‘
    def __init__(self, tailed_file):
        ‘‘‘ Initiate a Tail instance.
            Check for file validity, assigns callback function to standard out.

            Arguments:
                tailed_file - File to be followed. ‘‘‘
        self.check_file_validity(tailed_file)
        self.tailed_file = tailed_file
        self.callback = sys.stdout.write

    def follow(self, s=0.01):
        """ Do a tail follow. If a callback function is registered it is called with every new line.
        Else printed to standard out.

        Arguments:
            s - Number of seconds to wait between each iteration; Defaults to 1. """

        with open(self.tailed_file) as file_:
            # Go to the end of file
            file_.seek(0, 2)
            while True:
                curr_position = file_.tell()
                line = file_.readline()
                if not line:
                    file_.seek(curr_position)
                else:
                    self.callback(line)
                time.sleep(s)

    def register_callback(self, func):
        """ Overrides default callback function to provided function. """
        self.callback = func

    def check_file_validity(self, file_):
        """ Check whether the a given file exists, readable and is a file """
        if not os.access(file_, os.F_OK):
            raise TailError("File ‘%s‘ does not exist" % (file_))
        if not os.access(file_, os.R_OK):
            raise TailError("File ‘%s‘ not readable" % (file_))
        if os.path.isdir(file_):
            raise TailError("File ‘%s‘ is a directory" % (file_))

class TailError(Exception):
    """ Custom error type.
    """

    def __init__(self, msg):
        """ Init.
        """
        self.message = msg

    def __str__(self):
        """ str.
        """
        return self.message

if __name__ == ‘__main__‘:
    pass

""" vim: set ts=4 sw=4 sts=4 tw=100 et: """

2. 但是考虑到集中情况,上面的方法会失效(shell中tail命令甚至也会失效)

(1) 正在tail的文件被清空,例如 echo "" > test.log (这种情况下tail依然可以打印,上面程序无法打印)

(2) 日志被logrotate以copy形式切割时,这种情况下tail依然可以打印,上面程序无法打印

(3) 日志被logrotate以新建文件形式切割时,上面两种方法都会失效。

我的应用场景是需要兼容上面的情况的,由于时间匆忙,简单的改良了一下,可以兼容上面的两种情况,同事可以避免由于logrotate切割导致的日志丢失,代码如下:

#!/usr/bin/env python
#!-*- coding: utf-8 -*-

################################################################################
#
# Copyright (c) 2015 XX.com, Inc. All Rights Reserved
#
################################################################################

################################################################################
# This module provide ...
# Third libs those depend on:
################################################################################

"""
Compiler Python 2.7.10
Authors: XX([email protected])
Date: 2015-09-09 12:57:41
Todo: nothing $XX$2015-09-09
"""

"""SYS LIBS
"""

import os
import re
import sys
import time

import traceback

"""THIRD LIBS
"""

try:
    # import the third libs there.
    pass
except ImportError as e:
    print e
    os._exit(-1)

"""CUSTOM libs
Strongly recommend using abs path when using custmo libs.
"""

# Good behaviors.
# It means refusing called like from xxx import *
# When `__all__` is []
__all__ = []

reload(sys)
sys.setdefaultencoding(‘utf-8‘)

def send_error(msg):
    """ Send error to email.
    """

    print msg

#********************************************************
#* Global defines start.                                *
#********************************************************

#********************************************************
#* Global defines end.                                  *
#********************************************************

class Tail(object):
    """
    Python-Tail - Unix tail follow implementation in Python.

    python-tail can be used to monitor changes to a file.

    Example:
        import tail

        # Create a tail instance
        t = tail.Tail(‘file-to-be-followed‘)

        # Register a callback function to be called when a new line is found in the followed file.
        # If no callback function is registerd, new lines would be printed to standard out.
        t.register_callback(callback_function)

        # Follow the file with 5 seconds as sleep time between iterations.
        # If sleep time is not provided 1 second is used as the default time.
        t.follow(s=5)
    """

    ‘‘‘ Represents a tail command. ‘‘‘
    def __init__(self, tailed_file):
        ‘‘‘ Initiate a Tail instance.
            Check for file validity, assigns callback function to standard out.

            Arguments:
                tailed_file - File to be followed. ‘‘‘
        self.check_file_validity(tailed_file)
        self.tailed_file = tailed_file
        self.callback = sys.stdout.write

        self.try_count = 0

        try:
            self.file_ = open(self.tailed_file, "r")
            self.size = os.path.getsize(self.tailed_file)

            # Go to the end of file
            self.file_.seek(0, 2)
        except:
            raise

    def reload_tailed_file(self):
        """ Reload tailed file when it be empty be `echo "" > tailed file`, or
            segmentated by logrotate.
        """
        try:
            self.file_ = open(self.tailed_file, "r")
            self.size = os.path.getsize(self.tailed_file)

            # Go to the head of file
            self.file_.seek(0, 1)

            return True
        except:
            return False

    def follow(self, s=0.01):
        """ Do a tail follow. If a callback function is registered it is called with every new line.
        Else printed to standard out.

        Arguments:
            s - Number of seconds to wait between each iteration; Defaults to 1. """

        while True:
            _size = os.path.getsize(self.tailed_file)
            if _size < self.size:
                while self.try_count < 10:
                    if not self.reload_tailed_file():
                        self.try_count += 1
                    else:
                        self.try_count = 0
                        self.size = os.path.getsize(self.tailed_file)
                        break
                    time.sleep(0.1)

                if self.try_count == 10:
                    raise Exception("Open %s failed after try 10 times" % self.tailed_file)
            else:
                self.size = _size

            curr_position = self.file_.tell()
            line = self.file_.readline()
            if not line:
                self.file_.seek(curr_position)
            else:
                self.callback(line)
            time.sleep(s)

    def register_callback(self, func):
        """ Overrides default callback function to provided function. """
        self.callback = func

    def check_file_validity(self, file_):
        """ Check whether the a given file exists, readable and is a file """
        if not os.access(file_, os.F_OK):
            raise TailError("File ‘%s‘ does not exist" % (file_))
        if not os.access(file_, os.R_OK):
            raise TailError("File ‘%s‘ not readable" % (file_))
        if os.path.isdir(file_):
            raise TailError("File ‘%s‘ is a directory" % (file_))

class TailError(Exception):
    """ Custom error type.
    """

    def __init__(self, msg):
        """ Init.
        """
        self.message = msg

    def __str__(self):
        """ str.
        """
        return self.message

if __name__ == ‘__main__‘:
    t = Tail("/home/syslog/switch.log")
    def print_msg(msg):
        print msg

    t.register_callback(print_msg)

    t.follow()

""" vim: set ts=4 sw=4 sts=4 tw=100 et: """

上面程序通过简单的测试,可以解决以上几种情况,由于比较匆忙,没有仔细考虑,也希望大家有什么想法尽量贡献哈。

时间: 2024-07-28 21:33:18

python实现tail(考虑到几种特殊情况)的相关文章

10min手写(五):面试题解析丨Python实现tail -f功能

作者:蜗牛 shengxinjing (woniuppp) · GitHub 写这篇文章的初衷是有人去面试遇到了这个笔试题,不知道怎么做,没有什么思路,就发到了Reboot 的交流群里,让大家一起讨论讨论. 关于这道题,简单说一下我的想法吧.当然,也有很好用的 pyinotify 模块专门监听文件变化,不过我更想介绍的,是解决的思路.毕竟作为面试官,还是想看到一下解决问题的思路,而且我觉得这一题的难点不在于监控文件增量,而在于怎么打印最后面10行. 希望大家在读这篇文章前,对 Python 基础

面试宝典_Python.运维开发.0004.用Python实现tail实时输出新增日志?

面试题目: 1. 用PYTHON实现tail -f功能,默认显示最后15行,实时输出新增行? 解题思路: 1. 此需求在很多场景中都有遇到,而且在各大群中也被讨论过,虽然有现成的模版如pyinotify等模块实现,但面试更想通过你的解题思路来判断这场面试,具体到tail需要实现2个功能,一个实时输出新增内容,一个默认输出前15行,前者直接循环打开文件读取,全局变量中记录上次读取的位置,下一次循环seek到上次的位置读取即可,而对于默认显示前15行的做法是假设一行1000个字节,循环读取,当文件总

Python编程中常用的12种基础知识总结

原地址:http://blog.jobbole.com/48541/ Python编程中常用的12种基础知识总结:正则表达式替换,遍历目录方法,列表按列排序.去重,字典排序,字典.列表.字符串互转,时间对象操作,命令行参数解析(getopt),print 格式化输出,进制转换,Python调用系统命令或者脚本,Python 读写文件. 1.正则表达式替换目标: 将字符串line中的 overview.gif 替换成其他字符串 1 2 3 4 5 6 7 8 9 10 11 >>> lin

Python 编程中常用的 12 种基础知识总结

Python 编程中常用的 12 种基础知识总结:正则表达式替换,遍历目录方法,列表按列排序.去重,字典排序,字典.列表.字符串互转,时间对象操作,命令行参数解析(getopt),print 格式化输出,进制转换,Python调用系统命令或者脚本,Python 读写文件. 1.正则表达式替换 目标:将字符串line中的 overview.gif 替换成其他字符串 >>> line = '<IMG ALIGN="middle" SRC=\'#\'" /s

Python爬取网页的三种方法

# Python爬取网页的三种方法之一:  使用urllib或者urllib2模块的getparam方法 import urllib fopen1 = urllib.urlopen('http://www.baidu.com').info() fopen2 = urllib2.urlopen('http://www.sina.com').info() print fopen1.getparam('charset') print fopen2.getparam('charset') #----有些

Python调用API接口的几种方式

相信做过自动化运维的同学都用过API接口来完成某些动作.API是一套成熟系统所必需的接口,可以被其他系统或脚本来调用,这也是自动化运维的必修课. 本文主要介绍python中调用API的几种方式,下面是python中会用到的库. - urllib2 - httplib2 - pycurl - requests urllib2 import urllib2, urllib github_url = 'https://api.github.com/user/repos' password_manage

「python」: arp脚本的两种方法

「python」: arp脚本的两种方法 第一种是使用arping工具: #!/usr/bin/env python import subprocess import sys import re def arping(ipaddress = "192.168.1.1"): p = subprocess.Popen("/usr/sbin/arping -c 2 %s" % ipaddress, shell = True, stdout = subprocess.PIP

Python -- 值转换为字符串的两种机制

可以通过以下两个函数来使用这两种机制:一是通过str函数,它会把值转换为合理形式的字符串,以便用户可以理解:而repr会创建一个字符串,它以合法的Python表达式的形式来表示值.下面是一些例子: >>> print repr("Hello, world!") 'Hello, world!' >>> print repr(10000L) 10000L >>> print str("Hello, world!")

python用requests和urllib2两种方式调用图灵机器人接口

最近从网上看见个有意思的图灵机器人,可以根据不同的信息智能回复,比如你发送一个"讲个笑话",它就会给你回复一个笑话,或者"北京天气"就可以回复天气情况,或者英文单词然后给你回复中文释义.官方文档中有php和java的调用方式,我就弄个python的吧. 注册获取API KEY 这一步很简单,直接注册一个账号就可以看到你的API KEY.这个KEY我们以后发送get请求的时候需要用到. Pythoh调用示例 掉用也比较简单,主要是模拟post 请求.然后解析 json