python 日志收集服务器

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 起因 python 的日志收集服务是线程安全的(对同一个文件的写入,使用了锁),但是对于多进程的情况,它是无法处理的。python 官方文档推荐的做法是,使用tcp 服务器专门用于日志的收集,以确保对的文件的写入是安全的。这里提供了日志收集服务器基于twisted的实现,可供参考,程序在centos上进行了测试,并可用于生产环境 当client 和server 在一台机器上时,每秒可以处理6000条日志记录 #!/usr/bin/env python # -*- coding:utf-8 -*- ################################## # logserver # 通过socket接收并存储日志信息 # 注:测试程序为test_logserver.py # 修改时间:2013-11-22 ################################### # standard lib import os, sys import time import twisted import logging import traceback import struct import cPickle # use cPickle for speed from logging.handlers import TimedRotatingFileHandler from twisted.internet.protocol import Protocol, Factory # our own code import settings from log_record import initlog # 根据logger name生成一个 def gen_log_name(name): name = name.replace('.', '_') return name + '.log' class ReceiveProtocol(Protocol): HEADER_LENGTH = 4 def __init__(self): self.buf = '' self.state = 'init' self.require_length = ReceiveProtocol.HEADER_LENGTH # 记录当前完成初始化的logger名称 self.logger_dict = {} def connectionMade(self): logger = logging.getLogger('log_server') logger.info('[makeConnection]与logger_client建立socket连接。') def dataReceived(self, data): logger = logging.getLogger('log_server') try: if len(self.buf + data) >= self.require_length: self.buf = self.buf + data # 数据就绪进行相应动作 data = self.buf[0:self.require_length] # 把数据从缓冲区中取走 self.buf = self.buf[self.require_length:] self.solve(data) # 可能一次读到了多条日志记录 if self.buf: self.dataReceived('') else: self.buf += data except BaseException, e: logger.error('处理记录失败' + str(e) + '\n' + traceback.format_exc()) def solve(self, data): logger = logging.getLogger('log_server') statehandler = None try: pto = 'proto_' + self.state statehandler = getattr(self,pto) except AttributeError: logger.error('callback',self.state,'not found') self.transport.loseConnection() statehandler(data) if self.state == 'done': self.transport.loseConnection() def connectionLost(self, reason): logger = logging.getLogger('log_server') logger.info('[connectionLost]与logger_client的socket连接关闭。') # 记录日志 def proto_record(self, data): logRecord = logging.makeLogRecord(cPickle.loads(data)) if logRecord.name not in self.logger_dict: logger = initlog(logRecord.name, settings.PATH, gen_log_name(logRecord.name), logLevel=logging.DEBUG) self.logger_dict[logRecord.name] = logger self.logger_dict[logRecord.name].handle(logRecord) # 修改下一步动作以及所需长度 self.state = 'init' self.require_length = ReceiveProtocol.HEADER_LENGTH # 处理头部信息 def proto_init(self, data): length = struct.unpack('!I', data[0:ReceiveProtocol.HEADER_LENGTH])[0] # 修改下一步动作以及所需长度 self.state = 'record' self.require_length = length if len(self.buf) >= self.require_length: data = self.buf[0:self.require_length] # 把数据从缓冲区中取走 self.buf = self.buf[self.require_length:] self.solve(data) class ReceiveFactory(Factory): def buildProtocol(self, addr): return ReceiveProtocol() def main(): print 'logserver has started.' logger = logging.getLogger('log_server') logger.info('logserver has started.') from twisted.internet import epollreactor epollreactor.install() reactor = twisted.internet.reactor reactor.listenTCP(settings.PORT, ReceiveFactory()) reactor.run() if __name__ == '__main__': main() 完整程序: https://github.com/vearne/log_server/tree/master ...

January 2, 2018 · 2 min

使用logging模块来记录异常

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 在使用Java的时候,用log4j记录异常很简单,只要把Exception对象传递给log.error方法就可以了,但是在Python中就不行了,如果直接传递异常对象给log.error,那么只会在log里面出现一行异常对象的值。 在Python中正确的记录Log方式应该是这样的: logging.exception(ex) # 指名输出栈踪迹, logging.exception的内部也是包了一层此做法 logging.error(ex, exc_info=1) # 更加严重的错误级别 logging.critical(ex, exc_info=1)

January 2, 2018 · 1 min

python logging 最佳实践

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 起因 我经常跟同事开玩笑,我说在一家公司里面,能把日志这个功能搞清楚的都没有几个。所以写篇文章把我知道的部分知识分享一下。 在我目前看到的日志的文档中,Python的官方的文章是最清晰明了,推荐大家都来阅读下 https://docs.python.org/2/howto/logging.html 这个流程图非常重要,希望朋友们能仔细看看。 1. 适用场景 1.1 一般场景 在一般情况下,我们最常用的的handler有两个: RotatingFileHandler 按设定的文件大小切分日志 TimedRotatingFileHandler 按时间切分日志 以上2个handler都是线程安全的,可以用于多线程的场景,对于多进程则需要考虑其它方法 1.2 多进程 对于多进程的场景,python官方文档推荐我们使用 SocketHandler 使用TCP数据传输日志 可以参考我的文章 python 日志收集服务器 DatagramHandler 使用UDP数据传输日志 在单机上,即日志发送client 和 日志收集server 在同一台机器上,压测结果 type record/second TCP 6000 UDP 9000 除此之外还可以考虑一下两种方法: python-logstash 使用logstash,打开logstash的UDP或者TCP的服务端口直接接受数据,收到的数据可以入ElasticSearch 或者直接输出到文件中 自定义新的handler 将日志记录存入redis 的某个队列中,再额外启动一个进程,从redis中把数据读出入到文件中 2. 日志的级别 Level Numeric value CRITICAL 50 ERROR 40 WARNING 30 INFO 20 DEBUG 10 NOTSET 0 一般情况下日志文件只需要分成两个即可 all.log 存储 >= INFO 级别的日志 测试环境可以开到DEBUG 级别 可以跟踪业务流程等等 error.log 存储 >= ERROR 级别的日志 便于快速排查故障 一个常见的日志初始化模块可以这样书写 logger_helper.py ...

January 2, 2018 · 1 min