使用具有MapReduce映射器/缩减器功能的装饰器?

时间:2014-05-01 08:29:16

标签: python google-app-engine mapreduce

我试图用以下内容包装我的mapper / reducer函数:

def log_exceptions_to_sentry(sentry_id, raise_exception):
    def decorator(fn):
        def wrapper(*args, **kwargs):
            try:
                return fn(*args, **kwargs)
            except Exception, e:
                client = Client(sentry_id)
                client.captureException(
                    exc_info=sys.exc_info())
                if raise_exception:
                    raise e
        return wrapper
    return decorator

所以我的mapper / reducer函数看起来像:

@log_exceptions_to_sentry(SENTRY_ID, False)
def my_mapper_fn(item):
    logging.info(item)

但它似乎不起作用。没有装饰器,我会找到INFO item的日志。但是,如果我放置装饰器,看起来映射器/缩减器功能根本不会被调用。

我希望能够轻松记录我的功能可能存在的任何错误,以便我可以修复它们,因为尝试通过AppEngine的日志追踪MapReduce几乎是不可能的。

我可以使用try ... except块包装整个函数体,但装饰器会更干净。

2 个答案:

答案 0 :(得分:0)

我相信装饰器结构存在问题。在partuclar,我想你想要替换

try:
    return fn(*args, **kwargs)

try:
    fn(*args, **kwargs)

我缺少一些测试它的函数,但如果你想运行一个函数,你可以在这里看到简化的装饰器示例:http://simeonfranklin.com/blog/2012/jul/1/python-decorators-in-12-steps/

尝试这样的方法以确保您的代码有效,然后在以下情况下尝试更复杂的参数化版本:

sentry_id = id
raise_exception = 1

def basic_decorator(function):
    global sentry_id, raise_exception
    def wrapper(*args,**kwargs):
        try:
            function(*args,**kwargs)
        except Exception, e:
            client = Client(sentry_id)
            client.captureException(exc_info=sys.exc_info())
            if raise_exception:
                raise
    return wrapper

@basic_decorator
def my_mapper_fn(item):
    logging.info(item)

要参数化sentry_idraise_exception,请将装饰器包装在另一个装饰器中。我们的想法是,在定义基本装饰器时,sentry_idraise_exceptionfunction将提前定义并包含在其范围内。这应该看起来像

def log_exceptions_to_sentry(sentry_id,raise_exception=1):
    def basic_decorator(function):
        def wrapper(*args, **kwargs):
            try:
                function(*args,**kwargs)
            except Exception, e:
                client = Client(sentry_id)
                client.captureException(exc_info=sys.exc_info())
                if raise_exception:
                    raise

        return wrapper
    return basic_decorator

@log_exceptions_to_sentry(SENTRY_ID,RAISE_EXCEPTION)
def my_mapper_fn(item):
    logging.info(item)

答案 1 :(得分:0)

我不知道SENTRY_IDClient是什么,因为您没有发布它。所以我自己做了。完全使用您的代码,一切似乎都按预期工作。我不确定你所看到的是什么行不通。

SENTRY_ID = 1
class Client(object):
    def __init__(self, sentry_id): pass
    def captureException(self, **kwargs):
        print('captureException, ', kwargs['exc_info'])

def log_exceptions_to_sentry(sentry_id, raise_exception):
    def decorator(fn):
        def wrapper(*args, **kwargs):
            try:
                return fn(*args, **kwargs)
            except Exception as e:
                client = Client(sentry_id)
                client.captureException(
                    exc_info=sys.exc_info())
                if raise_exception:
                    raise e
        return wrapper
    return decorator

def fn(item):
    logging.debug(item)
    logging.info(item)
    logging.error(item)

@log_exceptions_to_sentry(SENTRY_ID, False)
def my_mapper_fn(item):
    logging.debug(item)
    logging.info(item)
    logging.error(item)
    return 1

@log_exceptions_to_sentry(SENTRY_ID, False)
def my_mapper_fn2(item):
    raise Exception()

logging.basicConfig(
    level = logging.INFO,
    format = '%(levelname)s:%(name)s:%(message)s',
    #format = '%(message)s',
)

x = fn({'a':1})
print(x)
x = my_mapper_fn({'b':2})
print(x)
x = my_mapper_fn2({'c':3})
print(x)

输出:

INFO:root:{'a': 1}
ERROR:root:{'a': 1}
None
INFO:root:{'b': 2}
ERROR:root:{'b': 2}
1
captureException,  (<type 'exceptions.Exception'>, Exception(), <traceback object at 0x1813cf8>)
None