Python: how to make a lazy debug log

I have python like this:

def foo(): logger = logging.getLogger() # do something here logger.debug('blah blah {}'.format(expensive_func())) foo() 

where expensive_func() is a function that returns a string and its expensive operation.

During development, the log level is set to DEBUG, and expensive_func() is executed, the message is logged, everything is in order.

The problem is that when I set the log level strictly more than DEBUG, say WARNING, in the production env, it is obvious that the returned value of expensive_func() will not be logged, but the expensive function itself will still be executed.

My question is: how to prevent python due to an expensive function when the logging level is WARNING?

I do not want to delete this debug line or add something like if level > DEBUG: return in an expensive function.

Thank.

EDIT

I visited the Lazy logring message string just now, but am not satisfied with this, mainly because:

  • This is kind of ugly;
  • Even if I wrap an expensive function with some Lazy class, what do I do when I have two expensive functions? (shown below).
 class Lazy: def __init__(self, func, *a, **ka): self.func= func self.a = a self.ka= ka def __str__(self): return str(self.func(*self.a, **self.ka)) # Though this is ugly, it works logger.debug('Message: %s', Lazy(expensive_func)) # What if I wanted to do this? # logger.debug('Message: {}'.format(expf_1(expf_2(some_arg)))) # Maybe I can modify class Lazy to make something like this to work # but it really doesn't feel right # logger.debug('Message: {}', Lazy(expf_1, Lazy(expf_2, some_arg))) 
+4
python logging lazy-evaluation
Jan 27 '14 at 9:21
source share
3 answers

Check out this piece of documentation.

Update: Logging already supports lazy evaluation, but is slightly different from the method described in your application. For example, see the following script:

 import logging def expensive_func(*args): print('Expensive func called: %s' % (args,)) return sum(args) class DeferredMessage(object): def __init__(self, func, *args): self.func = func self.args = args def __str__(self): return 'Message {0}'.format(self.func(*self.args)) if __name__ == '__main__': logging.basicConfig() logging.info(DeferredMessage(expensive_func, 1, 2)) logging.warning(DeferredMessage(expensive_func, 3, 4)) logging.error(DeferredMessage(expensive_func, 5, 6)) 

When the above script is executed, it should print

 Expensive func called: (3, 4) WARNING:root:Message 7 Expensive func called: (5, 6) ERROR:root:Message 11 

which shows that a potentially expensive function is only called when necessary. The above example, of course, can be generalized to allow the format string to be passed to DeferredMessage and use kwargs , etc.

+4
Jan 28 '14 at 7:34
source share

As Vinay Sajip suggests, you can do the following:

 def foo(): logger = logging.getLogger() if logger.isEnabledFor(logging.DEBUG): logger.debug('blah blah {}'.format(expensive_func())) logger.debug('Message: {}'.format(expf_1(expf_2(some_arg)))) logger.debug('Message: {}', Lazy(expf_1, Lazy(expf_2, some_arg))) foo() 

What is laziness!

This is because then-expression

  logger.debug('blah blah {}'.format(expensive_func())) logger.debug('Message: {}'.format(expf_1(expf_2(some_arg)))) logger.debug('Message: {}', Lazy(expf_1, Lazy(expf_2, some_arg))) 

are evaluated only if and only if logger.isEnabledFor(logging.DEBUG) returns True , that is, if and only if an evaluation is required.




Even more

 logging.info(DeferredMessage(expensive_func, 1, 2)) 

not as lazy as you might think : DeferredMessage(expensive_func, 1, 2) needs to be evaluated impatiently. Which is further slower than rating:

  if logger.isEnabledFor(logging.DEBUG): 
+5
Mar 05 '14 at 16:50
source share

You can use stringlike library to add laziness to messages

eg:.

 logger.debug( 'blah blah {value}' .format( value=LazyString(expensive_func) ) ) 

Lib link: https://github.com/CovenantEyes/py_stringlike

0
Jun 30 '17 at 2:43 on
source share



All Articles