并行日志 python-mpi-logger

上一篇中我们介绍了在 IPython 中使用 mpi4py,下面我们将介绍一个使用 mpi4py 实现的并行日志工具 —— python-mpi-logger。

在介绍 python-mpi-logger 之前,我们先简要地介绍一下日志的概念和其作用以及 Python 标准库提供的日志纪录 logging 模块,因为 python-mpi-logger 也是处于 logging 模块框架之下的。

日志(log)

日志(log)是一种可以追踪某些软件运行时所发生事件的方法。软件开发人员可以向他们的代码中调用日志记录相关的方法来表明发生了某些事情。一个事件可以用一个包含可选变量数据的消息来描述。此外,事件也有重要性的概念,这个重要性也可以被称为严重性级别(level)。

通过日志的分析,可以方便用户了解系统或软件、应用的运行情况;如果你的应用日志足够丰富,也可以分析以往用户的操作行为、类型喜好、地域分布或其他更多信息;如果一个应用的日志同时也分了多个级别,那么可以很轻易地分析得到该应用的健康状况,及时发现问题并快速定位、解决问题,补救损失。简单来讲就是,我们通过记录和分析日志可以了解一个系统或软件程序运行情况是否正常,也可以在应用程序出现故障时快速定位问题。

日志的作用可以简单总结为以下3点:

  • 程序调试;
  • 了解软件程序运行情况,是否正常;
  • 软件程序运行故障分析与问题定位。

Python logging 模块

Python 标准库中的 logging 模块定义的函数和类为应用程序和库的开发实现了一个灵活的事件日志系统。logging 模块是 Python 的一个标准库模块,由标准库模块提供日志记录 API 的好处是所有 Python 模块都可以使用这个日志记录功能。所以,你的应用日志可以将你自己的日志信息与来自第三方模块的信息整合起来。

logging 模块包含的一些基本类有:

  • Logger 对象:提供应用直接使用的日志接口;
  • Handler 对象:发送日志纪录(由 Logger 对象产生)到合适的目的地;
  • Filter 对象:精细地控制哪些日志纪录会最终被输出;
  • Formatter 对象:控制最终输出的日志纪录的格式。

下面给出 logging 模块中主要类和模块级别函数的使用接口,更详细的介绍请参考其文档

Logger 类

注意:一般并不直接初始化一个 Logger 类对象,而是使用模块级别函数 logging.getLogger(name) 来产生一个新的或者返回一个已经存在的 Logger 对象。

下面是其主要方法接口:

Logger.setLevel(level)

设定日志等级为 level。那些等级比 level 低的日志消息将会被忽略掉。下面是预定义的日志等级及其对应的数值:

等级 数值
CRITICAL 50
ERROR 40
WARNING 30
INFO 20
DEBUG 10
NOTSET 0
Logger.debug(msg, *args, **kwargs)

以 DEBUG 等级纪录一条日志。msg 是日志消息的格式化字符串,args 是将会被合并到 msg 中的相关参数。一个简单的例程如下:

FORMAT = '%(asctime)-15s %(clientip)s %(user)-8s %(message)s'
logging.basicConfig(format=FORMAT)
d = {'clientip': '192.168.0.1', 'user': 'fbloggs'}
logger = logging.getLogger('tcpserver')
logger.debug('Protocol problem: %s', 'connection reset', extra=d)

产生的输出如下:
2006-02-08 22:20:02,165 192.168.0.1 fbloggs Protocol problem: connection reset

Logger.info(msg, *args, **kwargs)

以 INFO 等级纪录一条日志。参数同 debug()。

Logger.warning(msg, *args, **kwargs)

以 WARNING 等级纪录一条日志。参数同 debug()。

Logger.error(msg, *args, **kwargs)

以 ERROR 等级纪录一条日志。参数同 debug()。

Logger.critical(msg, *args, **kwargs)

以 CRITICAL 等级纪录一条日志。参数同 debug()。

Logger.log(lvl, msg, *args, **kwargs)

以整数值 lvl 作为等级纪录一条日志。其它参数同 debug()。

Logger.addFilter(filter)

向该 Logger 添加一个 Filter 对象 filter

Logger.removeFilter(filter)

从该 Logger 中删除一个 Filter 对象 filter

Logger.addHandler(hdlr)

向该 Logger 添加一个 Handler 对象 hdlr

Logger.removeHandler(hdlr)

从该 Logger 中删除一个 Handler 对象 hdlr

Handler 类

注意:一般不会直接初始化一个 Handler 类对象。Handler 类的目的是为了作为更加有用的子类的基类,在其子类的 __init__() 方法中需要调用 Handler.__init__()。

下面是其主要方法接口:

Handler.__init__(level=NOTSET)

以等级 level 初始化一个 Handler 对象。

Handler.setLevel(level)

将该 Handler 对象的日志等级设定为 level。那些等级比 level 低的日志消息将会被忽略掉。

Handler.setFormatter(fmt)

将该 Handler 对象的 Formatter 设置为 fmt

Handler.addFilter(filter)

向该 Handler 对象添加一个 Filter 对象 filter

Handler.removeFilter(filter)

从该 Handler 对象中删除一个 Filter 对象 filter

Handler.emit(record)

在该方法中完成实际纪录日志的工作。子类应该具体实现该方法。

Python 标准库中包含了若干 handler 可供直接使用,这些 handler 的列表见 logging.handlers

模块级别函数

下面是部分主要的模块级别函数接口:

logging.getLogger([name])

如果 name 给定,返回一个名字为 name 的 logger,否则返回所在 logger 等级中的根 logger。用同样的 name 调用该函数会返回同一个 logger 对象。

logging.debug(msg[, *args[, **kwargs]])

以 DEBUG 等级纪录一条日志。msg 是日志消息的格式化字符串,args 是将会被合并到 msg 中的相关参数。

logging.info(msg[, *args[, **kwargs]])

以 INFO 等级纪录一条日志。参数同 debug()。

logging.warning(msg[, *args[, **kwargs]])

以 WARNING 等级纪录一条日志。参数同 debug()。

logging.error(msg[, *args[, **kwargs]])

以 ERROR 等级纪录一条日志。参数同 debug()。

logging.critical(msg[, *args[, **kwargs]])

以 CRITICAL 等级纪录一条日志。参数同 debug()。

logging.log(level, msg[, *args[, **kwargs]])

以整数值 lvl 作为等级纪录一条日志。其它参数同 debug()。

logging.basicConfig([**kwargs])

该函数会创建一个 Streamhandler 和一个默认的 Formatter 并将其加入到根 logger 上,并对此 logger 做一些基本的设置工作。如果根 logger 没有指定一个 handler,则函数 debug(), info(), warning(), error() 和 critical() 会自动调用 basicConfig()。

python-mpi-logger

python-mpi-logger 是一个使用 mpi4py 实现的并行日志工具,它提供了一个 MPILogHandler 类,该类是继承自标准库中的 logging.Handler 类的,可以在 logging 模块中使用该 handler 安全且高效地纪录 MPI 并行程序的日志。MPILogHandler 类的基本工作原理是执行程序的 MPI 进程会生成一个子 MPI 进程(使用 MPI.Intracomm.Spawn 方法),该子进程会收集所有父 MPI 进程的 logging 模块所产生的消息,并将收集到的消息写入指定的日志文件。

可以在这里下载然后安装 python-mpi-logger。下载和安装的命令如下:

$ git clone https://github.com/jrs65/python-mpi-logger.git
$ cd python-mpi-logger
$ python setup.py install [--user]

下面是 mpilogger.MPILogHandler 类的定义及方法接口:

class MPILogHandler(logging.Handler)

MPILogHandler 类,继承自标准库中的 logging.Handler。

def __init__(self, logfile, comm=None, *args, **kwargs)

初始化方法,logfile 为日志文件,comm 为一个 mpi4py.MPI.Intracomm 通信子,默认值为 None,表示使用 MPI.COMM_WORLD。在此方法中会调用 comm.Spawn 方法生成一个新的子进程。

def emit(self, record):

重载 logging.Handler 类中的同名方法,在该方法中完成实际纪录日志的工作。具体来说,所有父 MPI 进程都将自己的日志发送给新生成的子进程,子进程接收到后会将这些日志写入到 logfile 中。record 为 LogRecord 对象,该方法将初始化该类时所用的通信子的 rank 和 size 赋值给了 record 的同名属性,因此可以在 formatter 中使用这两个属性。

例程

使用 python-mpi-logger 进行并行日志纪录和使用标准库中的 logging 模块进行日志纪录的方法基本一样,因为 python-mpi-logger 只是提供了一个额外的 handler 而已,我们只需初始化一个 MPILogHandler 对象,然后将其加入到 logger 对象中就行了。下面是一个简单的使用例程:

# mpi_logger.py

"""
Demonstrates the usage of python-mpi-logger package.

Run this with 2 processes like:
$ mpiexec -n 2 python mpi_logger.py
"""

import logging
from mpi4py import MPI
import mpilogger


# the communicator
comm = MPI.COMM_WORLD

# get the logger
logger = logging.getLogger('mpitestlogger')

# initialize a MPILogHandler object
mh = mpilogger.MPILogHandler('testlog.log', comm=comm)

# construct an MPI formatter which prints out the rank and size
mpifmt = logging.Formatter(fmt='[rank %(rank)s/%(size)s] %(asctime)s : %(message)s')
mh.setFormatter(mpifmt)

# add the handler to the logger
logger.addHandler(mh)

# iterate through and log some messages
for i in range(2):
    logger.warning("Hello (times %i) from rank %i (of %i)" % (i+1, comm.rank, comm.size))

产生的日志文件 testlog.log 中的内容如下:

[rank 0/2] 2018-11-01 21:21:02,138 : Hello (times 1) from rank 0 (of 2)
[rank 1/2] 2018-11-01 21:21:02,138 : Hello (times 1) from rank 1 (of 2)
[rank 0/2] 2018-11-01 21:21:02,187 : Hello (times 2) from rank 0 (of 2)
[rank 1/2] 2018-11-01 21:21:02,187 : Hello (times 2) from rank 1 (of 2)

以上介绍了一个并行日志工具 —— python-mpi-logger,在下一篇中我们将介绍一个并行傅里叶变换软件包 —— mpi4py-fft。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容