用tornado搭建日志服务器
对于一个分布式应用,一个计算节点上可能产生一些日志,而为了便于将这些节点日志收集并分析,我们可以选择除了在本地生成日志外,还将某些级别的日志发送到日志服务器。
Python的logging模块可以用来对日志进行一些操作,其接口和著名的Java日志库log4j类似。logging.handlers支持一系列的日志处理方式,其中发送到日志服务器可以通过SocketHandler或者HttpHandler,不过本文既然选择了tornado,还是使用HttpHandler的方式。
首先,是分布式节点的部分。首先,我们通过get_logger方法得到logger对象,并向其添加HttpHandler。
import logging logging_host = '127.0.0.1' logging_port = 8888 logging_add_url = '/log/add/' def get_logger(): logger = logging.getLogger() http_handler = logging.handlers.HTTPHandler( '%s:%s' % (logging_host, logging_port), logging_add_url, method='POST' ) http_handler.setLevel(logging.ERROR) logger.addHandler(http_handler) return logger
代码还是很简单的,HTTPHandler接受三个参数,一个是服务器的host,日志发送到的url,以及是GET还是POST方式。这里,省去了添加本地日志的过程。
对于服务器端来说,我们同样需要一个logger对象来接收通过Http获取到的日志。这里,我们将获取的日志记录在本地文件中,所以用到了FileHandler。代码如下:
def get_logger(filename): logger = logging.getLogger('monitor') filename = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'monitor.log') handler = logging.FileHandler(filename) formatter = logging.Formatter( '%(asctime)s - %(module)s.%(funcName)s.%(lineno)d - %(levelname)s - %(message)s') handler.setFormatter(formatter) handler.setLevel(logging.ERROR) logger.addHandler(handler) return logger
值得注意的是,Python的logging模块对于每个要记录的日志,实际上是生成了一个LogRecord对象,而我们可以使用makeLogRecord方法来手动生成这个对象。因此,tornado处理日志的handler代码如下:
class LogAddHandler(tornado.web.RequestHandler): tuple_reg = re.compile("^\([^\(\)]*\)$") float_reg = re.compile("^\d*\.\d+$") int_reg = re.compile("^\d+$") def _extract(self, string): ''' 由于通过request.arguments的值join起来的仍然是个字符串,这里我们需要将其转化为Python对象 通过分析,我们可以知道这个对象只能是tuple、float和int 简单的来说,这个地方可以使用eval方法,但是通常情况下,"eval is evil" 所以这里通过正则的方法进行解析 ''' if self.tuple_reg.match(string): # 这里用json.loads来加载一个JS的数组方式来解析Python元组,将前后的括号专为方括号 # JS里的None为null,这样得到一个Python list,再转化为元组 return tuple(json.loads('[%s]' % string[1: -1].replace('None', 'null'))) elif self.float_reg.match(string): return float(string) elif self.int_reg.match(string): return int(string) return string def post(self): # tornado通过request.arguments可以得到一个字典 # 每个字典的key是一个参数,而字典的value确是一个list,列表里是这些值 # 例如POST过来a=1,则request.arguments得到的是{'a': ['1']} # 所以这里用join方法来把这些值连接起来 args = dict( [(k, self._extract(''.join(v))) for (k, v) in self.request.arguments.iteritems()] ) # makeLogRecord接受一个字典作为参数 record = logging.makeLogRecord(args) # logger通过get_logger方法获取 logger.handle(record)
代码里有对应的注释来解释代码。这样,我们只要在tornado里将“/log/add/”这个url对应这个handler即可。
至此搭建tornado日志服务就算完成了。当然我们还可以将其显示到网页上,方便远程浏览器查看。
本文的最后,推荐几个开源的日志项目,它们来自微博里的讨论。感谢@LJ_数据挖掘与社交网络计算等人提供:fluentd,sentry,mongodb-log。
可以试试 Sentry
分析的很透彻,学习啦,加油!
楼主试过自动扫描的方式.
请问把服务器端获取到的日志写到数据库 要怎么实现呢
代码在完美的地方,您可以将它用于您的主要功能。我也打算用它。