2014-05-01 25 views
4

我试图总结我的映射器/减速功能的东西,如:在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 

所以我的映射器/减速功能样子:

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

但它似乎并没有工作。没有装饰者,我会发现INFO日志item。但是,如果我把装饰器,似乎映射器/减速器功能根本不会被调用。

我希望能够轻松记录我的函数可能有的任何错误,所以我可以修复它们,因为试图通过AppEngine的日志追踪MapReduce几乎是不可能的。

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

+0

不要说'养e'只说'raise',否则抛出异常一个新的回溯。 – Daniel

+1

不要在decorator-call后面加上:这是一个语法错误 – Daniel

+0

糟糕,谢谢Daniel。 – john2x

回答

0

我相信你有一个装饰器结构的问题。在partuclar,我想你想与

try: 
    fn(*args, **kwargs) 

我错过了一些功能来测试它来取代

try: 
    return 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_id,raise_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) 
+0

因此,在你的例子中,你如何从'x = my_mapper_fn(item)'得到返回值?在你的情况下,x将永远是None,因为你没有返回'fn(* args,** kwargs)'的值。 – snapshoe

+0

@snapshoe,你是对的。我试图从问题中显示'logging.info'示例,该示例不使用返回值。并非Python中的所有函数都使用return语句。 无论如何,装饰器结构的工作原理是让你可以定义你想要的任何基础函数。 – statueofmike

+0

@ john2x,我的例子是否适合你? – statueofmike

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 
+0

嗯我试过这个,它也可以在控制台上运行,它只是在通过AppEngine MapReduce库运行时无法正常工作。此外,mapper/reducer功能“yield”值,而不是“返回”它们。 – john2x