Package ipapython :: Module log_manager
[hide private]
[frames] | no frames]

Source Code for Module ipapython.log_manager

   1  # Authors: John Dennis <jdennis@redhat.com> 
   2  # 
   3  # Copyright (C) 2011  Red Hat 
   4  # see file 'COPYING' for use and warranty information 
   5  # 
   6  # This program is free software; you can redistribute it and/or modify 
   7  # it under the terms of the GNU General Public License as published by 
   8  # the Free Software Foundation, either version 3 of the License, or 
   9  # (at your option) any later version. 
  10  # 
  11  # This program is distributed in the hope that it will be useful, 
  12  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
  13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  14  # GNU General Public License for more details. 
  15  # 
  16  # You should have received a copy of the GNU General Public License 
  17  # along with this program.  If not, see <http://www.gnu.org/licenses/>. 
  18   
  19  ''' 
  20   
  21  Quick Start Guide For Using This Module 
  22  ======================================= 
  23   
  24  This module implements a Log Manager class which wraps the Python 
  25  logging module and provides some utility functions for use with 
  26  logging. All logging operations should be done through the 
  27  `LogManager` where available. *DO NOT create objects using the 
  28  Python logging module, the log manager will be unaware of them.* 
  29   
  30  This module was designed for ease of use while preserving advanced 
  31  functionality and performance. You must perform the following steps. 
  32   
  33  1. Import the log_manger module and instantiate *one* `LogManager` 
  34     instance for your application or library. The `LogManager` is 
  35     configured via `LogManager.configure()` whose values are 
  36     easily populated from command line options or a config file. You 
  37     can modify the configuration again at any point. 
  38   
  39  2. Create one or more output handlers via 
  40     `LogManager.create_log_handlers()` an easy to use yet powerful 
  41     interface. 
  42   
  43  3. In your code create loggers via `LogManager.get_logger()`. Since 
  44     loggers are normally bound to a class this method is optimized for 
  45     that case, all you need to do in the call ``__init__()`` is:: 
  46   
  47       log_mgr.get_logger(self, True) 
  48   
  49     Then emitting messages is as simple as ``self.debug()`` or ``self.error()`` 
  50   
  51  Example: 
  52  -------- 
  53   
  54  :: 
  55   
  56    # Step 1, Create log manager and configure it 
  57    prog_name = 'my_app' 
  58    log_mgr = LogManager(prog_name) 
  59    log_mgr.configure(dict(verbose=True)) 
  60   
  61    # Step 2, Create handlers 
  62    log_mgr.create_log_handlers([dict(name='my_app stdout', 
  63                                      stream=sys.stdout, 
  64                                      level=logging.INFO), 
  65                                 dict(name='my_app file', 
  66                                      filename='my_app.log', 
  67                                      level=logging.DEBUG)]) 
  68   
  69    # Step 3, Create and use a logger in your code 
  70    class FooBar: 
  71        def __init__(self, name): 
  72            log_mgr.get_logger(self, True) 
  73            self.info("I'm alive! %s", name) 
  74   
  75    foobar = FooBar('Dr. Frankenstein') 
  76   
  77    # Dump the log manager state for illustration 
  78    print 
  79    print log_mgr 
  80   
  81   
  82  Running the above code would produce:: 
  83   
  84    <INFO>: I'm alive! Dr. Frankenstein 
  85   
  86    root_logger_name: my_app 
  87    configure_state: None 
  88    default_level: INFO 
  89    debug: False 
  90    verbose: True 
  91    number of loggers: 2 
  92        "my_app" [level=INFO] 
  93        "my_app.__main__.FooBar" [level=INFO] 
  94    number of handlers: 2 
  95        "my_app file" [level=DEBUG] 
  96        "my_app stdout" [level=INFO] 
  97    number of logger regexps: 0 
  98   
  99  *Note, Steps 1 & 2 were broken out for expository purposes.* You can 
 100  pass your handler configuration into `LogManager.configure()`. The above 
 101  could have been simpler and more compact.:: 
 102   
 103    # Step 1 & 2, Create log manager, and configure it and handlers 
 104    prog_name = 'my_app' 
 105    log_mgr = LogManager(prog_name) 
 106    log_mgr.configure(dict(verbose=True, 
 107                           handlers = [dict(name='my_app stdout', 
 108                                            stream=sys.stdout, 
 109                                            level=logging.INFO), 
 110                                       dict(name='my_app file', 
 111                                            filename='my_app.log', 
 112                                            level=logging.DEBUG)]) 
 113   
 114   
 115  FAQ (Frequently Asked Questions) 
 116  ================================ 
 117   
 118  #. **Why is this better than logging.basicConfig? The short example 
 119     for the LogManager doesn't seem much different in complexity from 
 120     basicConfig?** 
 121   
 122     * You get independent logging namespaces. You can instantiate 
 123       multiple logging namespaces. If you use this module you'll be 
 124       isolated from other users of the Python logging module avoiding 
 125       conflicts. 
 126   
 127     * Creating and initializing loggers for classes is trivial. One 
 128       simple call creates the logger, configures it, and sets logging 
 129       methods on the class instance. 
 130   
 131     * You can easily configure individual loggers to different 
 132       levels. For example turn on debuging for just the part of the 
 133       code you're working on. 
 134   
 135     * The configuration is both simple and powerful. You get many more 
 136       options than with basicConfig. 
 137   
 138     * You can dynamically reset the logging configuration during 
 139       execution, you're not forced to live with the config established 
 140       during program initialization. 
 141   
 142     * The manager optimizes the use of the logging objects, you'll 
 143       spend less time executing pointless logging code for messages 
 144       that won't be emitted. 
 145   
 146     * You can see the state of all the logging objects in your 
 147       namespace from one centrally managed location. 
 148   
 149     * You can configure a LogManager to use the standard logging root 
 150       logger and get all the benefits of this API. 
 151   
 152  #. **How do I turn on debug logging for a specific class without 
 153     affecting the rest of the logging configuration?** 
 154   
 155     Use a logger regular expression to bind a custom level to loggers 
 156     whose name matches the regexp. See `LogManager.configure()` 
 157     for details. 
 158   
 159     Lets say you want to set your Foo.Bar class to debug, then do 
 160     this:: 
 161   
 162       log_mgr.configure(dict(logger_regexps=[(r'Foo\.Bar', 'debug')])) 
 163   
 164  #. **I set the default_level but all my loggers are configured 
 165     with a higher level, what happened?** 
 166   
 167     You probably don't have any handlers defined at or below the 
 168     default_level. The level set on a logger will never be 
 169     lower than the lowest level handler available to that logger. 
 170   
 171  #. **My logger's all have their level set to a huge integer, why?** 
 172   
 173     See above. Logger's will never have a level less than the level of 
 174     the handlers visible to the logger. If there are no handlers then 
 175     loggers can't output anything so their level is set to maxint. 
 176   
 177  #. **I set the default_level but all the loggers are configured 
 178     at INFO or DEBUG, what happened?** 
 179   
 180     The verbose and debug config flags set the default_level to 
 181     INFO and DEBUG respectively as a convenience. 
 182   
 183  #. **I'm not seeing messages output when I expect them to be, what's 
 184     wrong?** 
 185   
 186     For a message to be emitted the following 3 conditions must hold: 
 187   
 188     * Message level >= logger's level 
 189     * Message level >= handler's level 
 190     * The message was not elided by a filter 
 191   
 192     To verify the above conditions hold print out the log manager state 
 193     (e.g. print log_mgr). Locate your logger, what level is at? Locate 
 194     the handler you expected to see the message appear on, what level 
 195     is it? 
 196   
 197  A General Discussion of Python Logging 
 198  ====================================== 
 199   
 200  The design of this module is driven by how the Python logging module 
 201  works. The following discussion complements the Python Logging Howto, 
 202  fills in some missing information and covers strategies for 
 203  implementing different functionality along with the trade-offs 
 204  involved. 
 205   
 206  Understanding when & how log messages are emitted: 
 207  -------------------------------------------------- 
 208   
 209  Loggers provide the application interface for logging. Every logger 
 210  object has the following methods debug(), info(), warning(), error(), 
 211  critical(), exception() and log() all of which can accept a format 
 212  string and arguments. Applications generate logging messages by 
 213  calling one of these methods to produce a formatted message. 
 214   
 215  A logger's effective level is the first explicitly set level found 
 216  when searching from the logger through it's ancestors terminating at 
 217  the root logger. The root logger always has an explicit level 
 218  (defaults to WARNING). 
 219   
 220  For a message to be emitted by a handler the following must be true: 
 221   
 222  The logger's effective level must >= message level and it must not 
 223  be filtered by a filter attached to the logger, otherwise the 
 224  message is discarded. 
 225   
 226  If the message survives the logger check it is passed to a list of 
 227  handlers. A handler will emit the message if the handler's level >= 
 228  message level and its not filtered by a filter attached to the 
 229  handler. 
 230   
 231  The list of handlers is determined thusly: Each logger has a list of 
 232  handlers (which may be empty). Starting with the logger the message 
 233  was bound to the message is passed to each of it's handlers. Then 
 234  the process repeats itself by traversing the chain of loggers 
 235  through all of it's ancestors until it reaches the root logger. The 
 236  logger traversal will be terminated if the propagate flag on a logger 
 237  is False (by default propagate is True). 
 238   
 239  Let's look at a hypothetical logger hierarchy (tree):: 
 240   
 241                              A 
 242                             / \\ 
 243                            B   D 
 244                           / 
 245                          C 
 246   
 247   
 248  There are 4 loggers and 3 handlers 
 249   
 250  Loggers: 
 251   
 252  +-------+---------+---------+-----------+----------+ 
 253  |Logger | Level   | Filters | Propagate | Handlers | 
 254  +=======+=========+=========+===========+==========+ 
 255  | A     | WARNING | []      | False     | [h1,h2]  | 
 256  +-------+---------+---------+-----------+----------+ 
 257  | A.B   | ERROR   | []      | False     | [h3]     | 
 258  +-------+---------+---------+-----------+----------+ 
 259  | A.B.C | DEBUG   | []      | True      |          | 
 260  +-------+---------+---------+-----------+----------+ 
 261  | A.D   |         | []      | True      |          | 
 262  +-------+---------+---------+-----------+----------+ 
 263   
 264  Handlers: 
 265   
 266  +---------+---------+---------+ 
 267  | Handler | Level   | Filters | 
 268  +=========+=========+=========+ 
 269  | h1      | ERROR   | []      | 
 270  +---------+---------+---------+ 
 271  | h2      | WARNING | []      | 
 272  +---------+---------+---------+ 
 273  | h3      | DEBUG   | []      | 
 274  +---------+---------+---------+ 
 275   
 276  Each of the loggers and handlers have empty filter lists in this 
 277  example thus the filter checks will always pass. 
 278   
 279  If a debug message is posted logger A.B.C the following would 
 280  happen. The effective level is determined. Since it does not have a 
 281  level set it's parent (A.B) is examined which has ERROR set, 
 282  therefore the effective level of A.B.C is ERROR. Processing 
 283  immediately stops because the logger's level of ERROR does not 
 284  permit debug messages. 
 285   
 286  If an error message is posted on logger A.B.C it passes the logger 
 287  level check and filter check therefore the message is passed along 
 288  to the handlers. The list of handlers on A.B.C is empty so no 
 289  handlers are called at this position in the logging hierarchy. Logger 
 290  A.B.C's propagate flag is True so parent logger A.B handlers are 
 291  invoked. Handler h3's level is DEBUG, it passes both the level and 
 292  filter check thus h3 emits the message. Processing now stops because 
 293  logger A.B's propagate flag is False. 
 294   
 295  Now let's see what would happen if a warning message was posted on 
 296  logger A.D. It's effective level is WARNING because logger A.D does 
 297  not have a level set, it's only ancestor is logger A, the root 
 298  logger which has a level of WARNING, thus logger's A.D effective 
 299  level is WARNING. Logger A.D has no handlers, it's propagate flag is 
 300  True so the message is passed to it's parent logger A, the root 
 301  logger. Logger A has two handlers h1 and h2. The level of h1 is 
 302  ERROR so the warning message is discarded by h1, nothing is emitted 
 303  by h1. Next handler h2 is invoked, it's level is WARNING so it 
 304  passes both the level check and the filter check, thus h2 emits the 
 305  warning message. 
 306   
 307  How to configure independent logging spaces: 
 308  -------------------------------------------- 
 309   
 310  A common idiom is to hang all handlers off the root logger and set 
 311  the root loggers level to the desired verbosity. But this simplistic 
 312  approach runs afoul of several problems, in particular who controls 
 313  logging (accomplished by configuring the root logger). The usual 
 314  advice is to check and see if the root logger has any handlers set, 
 315  if so someone before you has configured logging and you should 
 316  inherit their configuration, all you do is add your own loggers 
 317  without any explicitly set level. If the root logger doesn't have 
 318  handlers set then you go ahead and configure the root logger to your 
 319  preference. The idea here is if your code is being loaded by another 
 320  application you want to defer to that applications logging 
 321  configuration but if your code is running stand-alone you need to 
 322  set up logging yourself. 
 323   
 324  But sometimes your code really wants it's own logging configuration 
 325  managed only by yourself completely independent of any logging 
 326  configuration by someone who may have loaded your code. Even if you 
 327  code is not designed to be loaded as a package or module you may be 
 328  faced with this problem. A trivial example of this is running your 
 329  code under a unit test framework which itself uses the logging 
 330  facility (remember there is only ever one root logger in any Python 
 331  process). 
 332   
 333  Fortunately there is a simple way to accommodate this. All you need 
 334  to do is create a "fake" root in the logging hierarchy which belongs 
 335  to you. You set your fake root's propagate flag to False, set a 
 336  level on it and you'll hang your handlers off this fake root. Then 
 337  when you create your loggers each should be a descendant of this 
 338  fake root. Now you've completely isolated yourself in the logging 
 339  hierarchy and won't be influenced by any other logging 
 340  configuration. As an example let's say your your code is called 
 341  'foo' and so you name your fake root logger 'foo'.:: 
 342   
 343    my_root = logging.getLogger('foo') # child of the root logger 
 344    my_root.propagate = False 
 345    my_root.setLevel(logging.DEBUG) 
 346    my_root.addHandler(my_handler) 
 347   
 348  Then every logger you create should have 'foo.' prepended to it's 
 349  name. If you're logging my module your module's logger would be 
 350  created like this:: 
 351   
 352    module_logger = logging.getLogger('foo.%s' % __module__) 
 353   
 354  If you're logging by class then your class logger would be:: 
 355   
 356    class_logger = logging.getLogger('foo.%s.%s' % (self.__module__,  self.__class__.__name__)) 
 357   
 358  How to set levels: 
 359  ------------------ 
 360   
 361  An instinctive or simplistic assumption is to set the root logger to a 
 362  high logging level, for example ERROR. After all you don't want to be 
 363  spamming users with debug and info messages. Let's also assume you've 
 364  got two handlers, one for a file and one for the console, both 
 365  attached to the root logger (a common configuration) and you haven't 
 366  set the level on either handler (in which case the handler will emit 
 367  all levels). 
 368   
 369  But now let's say you want to turn on debugging, but just to the file, 
 370  the console should continue to only emit error messages. 
 371   
 372  You set the root logger's level to DEBUG. The first thing you notice is 
 373  that you're getting debug message both in the file and on the console 
 374  because the console's handler does not have a level set. Not what you 
 375  want. 
 376   
 377  So you go back restore the root loggers level back to it's original 
 378  ERROR level and set the file handler's level to DEBUG and the console 
 379  handler's level to ERROR. Now you don't get any debug messages because 
 380  the root logger is blocking all messages below the level of ERROR and 
 381  doesn't invoke any handlers. The file handler attached to the root 
 382  logger even though it's level is set to DEBUG never gets a chance to 
 383  process the message. 
 384   
 385  *IMPORTANT:* You have to set the logger's level to the minimum of all 
 386  the attached handler's levels, otherwise the logger may block the 
 387  message from ever reaching any handler. 
 388   
 389  In this example the root logger's level must be set to DEBUG, the file 
 390  handler's level to DEBUG, and the console handler's level set to 
 391  ERROR. 
 392   
 393  Now let's take a more real world example which is a bit more 
 394  complicated. It's typical to assign loggers to every major class. In 
 395  fact this is the design strategy of Java logging from which the Python 
 396  logging is modeled. In a large complex application or library that 
 397  means dozens or possibly hundreds of loggers. Now lets say you need to 
 398  trace what is happening with one class. If you use the simplistic 
 399  configuration outlined above you'll set the log level of the root 
 400  logger and one of the handlers to debug. Now you're flooded with debug 
 401  message from every logger in the system when all you wanted was the 
 402  debug messages from just one class. 
 403   
 404  How can you get fine grained control over which loggers emit debug 
 405  messages? Here are some possibilities: 
 406   
 407  (1) Set a filter. 
 408  ................. 
 409   
 410  When a message is propagated to a logger in the hierarchy first the 
 411  loggers level is checked. If logger level passes then the logger 
 412  iterates over every handler attached to the logger first checking the 
 413  handler level. If the handler level check passes then the filters 
 414  attached to the handler are run. 
 415   
 416  Filters are passed the record (i.e. the message), it does not have 
 417  access to either the logger or handler it's executing within. You 
 418  can't just set the filter to only pass the records of the classes you 
 419  want to debug because that would block other important info, warning, 
 420  error and critical messages from other classes. The filter would have 
 421  to know about the "global" log level which is in effect and also pass 
 422  any messages at that level or higher. It's unfortunate the filter 
 423  cannot know the level of the logger or handler it's executing inside 
 424  of. 
 425   
 426  Also logger filters only are applied to the logger they are attached 
 427  to, i.e. the logger the message was generated on. They do not get 
 428  applied to any ancestor loggers. That means you can't just set a 
 429  filter on the root logger. You have to either set the filters on the 
 430  handlers or on every logger created. 
 431   
 432  The filter first checks the level of the message record. If it's 
 433  greater than debug it passes it. For debug messages it checks the set 
 434  of loggers which have debug messages enabled, if the message record 
 435  was generated on one of those loggers it passes the record, otherwise 
 436  it blocks it. 
 437   
 438  The only question is whether you attach the filter to every logger or 
 439  to a handful of handlers. The advantage of attaching the filter to 
 440  every logger is efficiency, the time spent handling the message can be 
 441  short circuited much sooner if the message is filtered earlier in the 
 442  process. The advantage of attaching the filter to a handler is 
 443  simplicity, you only have to do that when a handler is created, not 
 444  every place in the code where a logger is created. 
 445   
 446  (2) Conditionally set the level of each logger. 
 447  ............................................... 
 448   
 449  When loggers are created a check is performed to see if the logger is 
 450  in the set of loggers for which debug information is desired, if so 
 451  it's level is set to DEBUG, otherwise it's set to the global 
 452  level. One has to recall there really isn't a single global level if 
 453  you want some handlers to emit info and above, some handlers error and 
 454  above, etc. In this case if the logger is not in the set of logger's 
 455  emitting debug the logger level should be set to the next increment 
 456  above debug level. 
 457   
 458  A good question to ask would be why not just leave the logger's level 
 459  unset if it's not in the set of loggers to be debugged? After all it 
 460  will just inherit the root level right? There are two problems with 
 461  that. 1) It wold actually inherit the level any ancestor logger and if 
 462  an ancestor was set to debug you've effectively turned on debugging 
 463  for all children of that ancestor logger. There are times you might 
 464  want that behavior, where all your children inherit your level, but 
 465  there are many cases where that's not the behavior you want. 2) A more 
 466  pernicious problem exists. The logger your handlers are attached to 
 467  MUST be set to debug level, otherwise your debug messages will never 
 468  reach the handlers for output. Thus if you leave a loggers level unset 
 469  and let it inherit it's effective level from an ancestor it might very 
 470  well inherit the debug level from the root logger. That means you've 
 471  completely negated your attempt to selectively set debug logging on 
 472  specific loggers. Bottom line, you really have to set the level on 
 473  every logger created if you want fine grained control. 
 474   
 475  Approach 2 has some distinct performance advantages. First of all 
 476  filters are not used, this avoids a whole processing step and extra 
 477  filter function calls on every message. Secondly a logger level check 
 478  is a simple integer compare which is very efficient. Thirdly the 
 479  processing of a message can be short circuited very early in the 
 480  processing pipeline, no ancestor loggers will be invoked and no 
 481  handlers will be invoked. 
 482   
 483  The downside is some added complexity at logger creation time. But 
 484  this is easily mitigated by using a utility function or method to 
 485  create the logger instead of just calling logger.getLogger(). 
 486   
 487  Like every thing else in computer science which approach you take boils 
 488  down to a series of trade offs, most around how your code is 
 489  organized. You might find it easier to set a filter on just one or two 
 490  handlers. It might be easier to modify the configuration during 
 491  execution if the logic is centralized in just a filter function, but 
 492  don't let that sway you too much because it's trivial to iterate over 
 493  every logger and dynamically reset it's log level. 
 494   
 495  Now at least you've got a basic understanding of how this stuff hangs 
 496  together and what your options are. That's not insignificant, when I 
 497  was first introduced to logging in Java and Python I found it 
 498  bewildering difficult to get it do what I wanted. 
 499   
 500  John Dennis <jdennis@redhat.com> 
 501   
 502  ''' 
 503   
 504  #------------------------------------------------------------------------------- 
 505  import sys 
 506  import os 
 507  import pwd 
 508  import logging 
 509  import re 
 510  import time 
 511   
 512  #------------------------------------------------------------------------------- 
 513  # Default format 
 514  LOGGING_DEFAULT_FORMAT = '%(levelname)s %(message)s' 
 515   
 516  # Maps a logging level name to it's numeric value 
 517  log_level_name_map = { 
 518      'notset'   : logging.NOTSET, 
 519      'debug'    : logging.DEBUG, 
 520      'info'     : logging.INFO, 
 521      'warn'     : logging.WARNING, 
 522      'warning'  : logging.WARNING, 
 523      'error'    : logging.ERROR, 
 524      'critical' : logging.CRITICAL 
 525  } 
 526   
 527  log_levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL) 
 528   
 529  logger_method_names = ('debug', 'info', 'warning', 'error', 'exception', 'critical') 
 530   
 531  #------------------------------------------------------------------------------- 
 532   
533 -def get_unique_levels(iterable):
534 ''' 535 Given a iterable of objects containing a logging level return a 536 ordered list (min to max) of unique levels. 537 538 :parameters: 539 iterable 540 Iterable yielding objects with a logging level attribute. 541 :returns: 542 Ordered list (min to max) of unique levels. 543 ''' 544 levels = set() 545 546 for obj in iterable: 547 level = getattr(obj, 'level', sys.maxint) 548 if level != logging.NOTSET: 549 levels.add(level) 550 levels = list(levels) 551 levels.sort() 552 return levels
553
554 -def get_minimum_level(iterable):
555 ''' 556 Given a iterable of objects containing a logging level return the 557 minimum level. If no levels are defined return maxint. 558 set of unique levels. 559 560 :parameters: 561 iterable 562 Iterable yielding objects with a logging level attribute. 563 :returns: 564 Ordered list (min to max) of unique levels. 565 ''' 566 min_level = sys.maxint 567 568 for obj in iterable: 569 level = getattr(obj, 'level', sys.maxint) 570 if level != logging.NOTSET: 571 if level < min_level: 572 min_level = level 573 return min_level
574
575 -def parse_log_level(level):
576 ''' 577 Given a log level either as a string or integer 578 return a numeric logging level. The following case insensitive 579 names are recognized:: 580 581 * notset 582 * debug 583 * info 584 * warn 585 * warning 586 * error 587 * critical 588 589 A string containing an integer is also recognized, for example 590 ``"10"`` would map to ``logging.DEBUG`` 591 592 The integer value must be the range [``logging.NOTSET``, 593 ``logging.CRITICAL``] otherwise a value exception will be raised. 594 595 :parameters: 596 level 597 basestring or integer, level value to convert 598 :returns: 599 integer level value 600 ''' 601 # Is it a string representation of an integer? 602 # If so convert to an int. 603 if isinstance(level, basestring): 604 try: 605 level = int(level) 606 except: 607 pass 608 609 # If it's a string lookup it's name and map to logging level 610 # otherwise validate the integer value is in range. 611 if isinstance(level, basestring): 612 result = log_level_name_map.get(level.lower()) #pylint: disable=E1103 613 if result is None: 614 raise ValueError('unknown log level (%s)' % level) 615 return result 616 elif isinstance(level, int): 617 if level < logging.NOTSET or level > logging.CRITICAL: 618 raise ValueError('log level (%d) out of range' % level) 619 return level 620 else: 621 raise TypeError('log level must be basestring or int, got (%s)' % type(level))
622 623 #-------------------------------------------------------------------------------
624 -def logging_obj_str(obj):
625 ''' 626 Unfortunately the logging Logger and Handler classes do not have a 627 custom __str__() function which converts the object into a human 628 readable string representation. This function takes any object 629 with a level attribute and outputs the objects name with it's 630 associated level. If a name was never set for the object then it's 631 repr is used instead. 632 633 :parameters: 634 obj 635 Object with a logging level attribute 636 :returns: 637 string describing the object 638 ''' 639 name = getattr(obj, 'name', repr(obj)) 640 text = '"%s" [level=%s]' % (name, logging.getLevelName(obj.level)) 641 if isinstance(obj, logging.FileHandler): 642 text += ' filename="%s"' % obj.baseFilename 643 return text
644 #-------------------------------------------------------------------------------
645 -class LogManager(object):
646 ''' 647 This class wraps the functionality in the logging module to 648 provide an easier to use API for logging while providing advanced 649 features including a independent namespace. Each application or 650 library wishing to have it's own logging namespace should instantiate 651 exactly one instance of this class and use it to manage all it's 652 logging. 653 654 Traditionally (or simplistically) logging was set up with a single 655 global root logger with output handlers bound to it. The global 656 root logger (whose name is the empty string) was shared by all 657 code in a loaded process. The only the global unamed root logger 658 had a level set on it, all other loggers created inherited this 659 global level. This can cause conflicts in more complex scenarios 660 where loaded code wants to maintain it's own logging configuration 661 independent of whomever loaded it's code. By using only a single 662 logger level set on the global root logger it was not possible to 663 have fine grained control over individual logger output. The 664 pattern seen with this simplistic setup has been frequently copied 665 despite being clumsy and awkward. The logging module has the tools 666 available to support a more sophisitcated and useful model, but it 667 requires an overarching framework to manage. This class provides 668 such a framework. 669 670 The features of this logging manager are: 671 672 * Independent logging namespace. 673 674 * Simplifed method to create handlers. 675 676 * Simple setup for applications with command line args. 677 678 * Sophisitcated handler configuration 679 (e.g. file ownership & permissions) 680 681 * Easy fine grained control of logger output 682 (e.g. turning on debug for just 1 or 2 loggers) 683 684 * Holistic management of the interrelationships between 685 logging components. 686 687 * Ability to dynamically adjust logging configuration in 688 a running process. 689 690 An independent namespace is established by creating a independent 691 root logger for this manager (root_logger_name). This root logger 692 is a direct child of the global unamed root logger. All loggers 693 created by this manager will be descendants of this managers root 694 logger. The managers root logger has it's propagate flag set 695 to False which means all loggers and handlers created by this 696 manager will be isolated in the global logging tree. 697 698 Log level management: 699 --------------------- 700 701 Traditionally loggers inherited their logging level from the root 702 logger. This was simple but made it impossible to independently 703 control logging output from different loggers. If you set the root 704 level to DEBUG you got DEBUG output from every logger in the 705 system, often overwhelming in it's voluminous output. Many times 706 you want to turn on debug for just one class (a common idom is to 707 have one logger per class). To achieve the fine grained control 708 you can either use filters or set a logging level on every logger 709 (see the module documentation for the pros and cons). This manager 710 sets a log level on every logger instead of using level 711 inheritence because it's more efficient at run time. 712 713 Global levels are supported via the verbose and debug flags 714 setting every logger level to INFO and DEBUG respectively. Fine 715 grained level control is provided via regular expression matching 716 on logger names (see `configure()` for the details. For 717 example if you want to set a debug level for the foo.bar logger 718 set a regular expression to match it and bind it to the debug 719 level. Note, the global verbose and debug flags always override 720 the regular expression level configuration. Do not set these 721 global flags if you want fine grained control. 722 723 The manager maintains the minimum level for all loggers under it's 724 control and the minimum level for all handlers under it's 725 control. The reason it does this is because there is no point in 726 generating debug messages on a logger if there is no handler 727 defined which will output a debug message. Thus when the level is 728 set on a logger it takes into consideration the set of handlers 729 that logger can emit to. 730 731 IMPORTANT: Because the manager maintains knowledge about all the 732 loggers and handlers under it's control it is essential you use 733 only the managers interface to modify a logger or handler and not 734 set levels on the objects directly, otherwise the manger will not 735 know to visit every object under it's control when a configuraiton 736 changes (see '`LogManager.apply_configuration()`). 737 738 Example Usage:: 739 740 # Create a log managers for use by 'my_app' 741 log_mgr = LogManager('my_app') 742 743 # Create a handler to send error messages to stderr 744 log_mgr.create_log_handlers([dict(stream=sys.stdout, 745 level=logging.ERROR)]) 746 747 # Create logger for a class 748 class Foo(object): 749 def __init__(self): 750 self.log = log_mgr.get_logger(self) 751 752 '''
753 - def __init__(self, root_logger_name='', configure_state=None):
754 ''' 755 Create a new LogManager instance using root_logger_name as the 756 parent of all loggers maintained by the manager. 757 758 Only one log manger should be created for each logging namespace. 759 760 :parameters: 761 root_logger_name 762 The name of the root logger. All loggers will be prefixed 763 by this name. 764 configure_state 765 Used by clients of the log manager to track the 766 configuration state, may be any object. 767 768 :return: 769 LogManager instance 770 771 ''' 772 self.loggers = {} # dict, key is logger name, value is logger object 773 self.handlers = {} # dict, key is handler name, value is handler object 774 775 self.configure_state = configure_state 776 self.root_logger_name = root_logger_name 777 self.default_level = 'error' 778 self.debug = False 779 self.verbose = False 780 self.logger_regexps = [] 781 782 self.root_logger = self.get_logger(self.root_logger_name) 783 # Stop loggers and handlers from searching above our root 784 self.root_logger.propagate = False
785 786
787 - def _get_default_level(self):
788 return self._default_level
789
790 - def _set_default_level(self, value):
791 level = parse_log_level(value) 792 self._default_level = level 793 self.apply_configuration()
794 795 default_level = property(_get_default_level, _set_default_level, 796 doc='see log_manager.parse_log_level()` for details on how the level can be specified during assignement.') 797
798 - def set_default_level(self, level, configure_state=None):
799 ''' 800 Reset the default logger level, updates all loggers. 801 Note, the default_level may also be set by assigning to the 802 default_level attribute but that does not update the configure_state, 803 this method is provided as a convenience to simultaneously set the 804 configure_state if so desired. 805 806 :parameters: 807 level 808 The new default level for the log manager. See 809 `log_manager.parse_log_level()` for details on how the 810 level can be specified. 811 configure_state 812 If other than None update the log manger's configure_state 813 variable to this object. Clients of the log manager can 814 use configure_state to track the state of the log manager. 815 816 ''' 817 level = parse_log_level(level) 818 self._default_level = level 819 self.apply_configuration(configure_state)
820 821
822 - def __str__(self):
823 ''' 824 When str() is called on the LogManager output it's state. 825 ''' 826 text = '' 827 text += 'root_logger_name: %s\n' % (self.root_logger_name) 828 text += 'configure_state: %s\n' % (self.configure_state) 829 text += 'default_level: %s\n' % (logging.getLevelName(self.default_level)) 830 text += 'debug: %s\n' % (self.debug) 831 text += 'verbose: %s\n' % (self.verbose) 832 833 text += 'number of loggers: %d\n' % (len(self.loggers)) 834 loggers = [logging_obj_str(x) for x in self.loggers.values()] 835 loggers.sort() 836 for logger in loggers: 837 text += ' %s\n' % (logger) 838 839 text += 'number of handlers: %d\n' % (len(self.handlers)) 840 handlers = [logging_obj_str(x) for x in self.handlers.values()] 841 handlers.sort() 842 for handler in handlers: 843 text += ' %s\n' % (handler) 844 845 text += 'number of logger regexps: %d\n' % (len(self.logger_regexps)) 846 for regexp, level in self.logger_regexps: 847 text += ' "%s" => %s\n' % (regexp, logging.getLevelName(level)) 848 849 return text
850
851 - def configure(self, config, configure_state=None):
852 ''' 853 The log manager is initialized from key,value pairs in the 854 config dict. This may be called any time to modify the 855 logging configuration at run time. 856 857 The supported entries in the config dict are: 858 859 default_level 860 The default level applied to a logger when not indivdually 861 configured. The verbose and debug config items override 862 the default level. See `log_manager.parse_log_level()` for 863 details on how the level can be specified. 864 verbose 865 Boolean, if True sets default_level to INFO. 866 debug 867 Boolean, if True sets default_level to DEBUG. 868 logger_regexps 869 List of (regexp, level) tuples. This is a an ordered list 870 regular expressions used to match against a logger name to 871 configure the logger's level. The first regexp in the 872 sequence which matches the logger name will use the the 873 level bound to that regexp to set the logger's level. If 874 no regexp matches the logger name then the logger will be 875 assigned the default_level. 876 877 The regular expression comparision is performed with the 878 re.search() function which means the match can be located 879 anywhere in the name string (as opposed to the start of 880 the the string). Do not forget to escape regular 881 expression metacharacters when appropriate. For example 882 dot ('.') is used to seperate loggers in a logging 883 hierarchy path (e.g. a.b.c) 884 885 Examples:: 886 887 # To match exactly the logger a.b.c and set it to DEBUG: 888 logger_regexps = [(r'^a\.b\.c$', 'debug')] 889 890 # To match any child of a.b and set it to INFO: 891 logger_regexps = [(r'^a\.b\..*', 'info')] 892 893 # To match any leaf logger with the name c and set it to level 5: 894 logger_regexps = [(r'\.c$', 5)] 895 handlers 896 List of handler config dicts or (config, logger) 897 tuples. See `create_log_handlers()` for details 898 of a hanlder config. 899 900 The simple form where handlers is a list of dicts each 901 handler is bound to the log mangers root logger (see 902 `create_log_handlers()` optional ``logger`` 903 parameter). If you want to bind each handler to a specific 904 logger other then root handler then group the handler config 905 with a logger in a (config, logger) tuple. The logger may be 906 either a logger name or a logger instance. The following are 907 all valid methods of passing handler configuration.:: 908 909 # List of 2 config dicts; both handlers bound to root logger 910 [{}, {}] 911 912 # List of 2 tuples; first handler bound to logger_name1 913 # by name, second bound to logger2 by object. 914 [({}, 'logger_name1'), ({}, logger2'] 915 916 # List of 1 dict, 1 tuple; first bound to root logger, 917 # second bound to logger_name by name 918 [{}, ({}, 'logger_name'] 919 920 :parameters: 921 config 922 Dict of <key,value> pairs describing the configuration. 923 configure_state 924 If other than None update the log manger's configure_state 925 variable to this object. Clients of the log manager can 926 use configure_state to track the state of the log manager. 927 928 ''' 929 for attr in ('debug', 'verbose', 'logger_regexps'): 930 value = config.get(attr) 931 if value is not None: 932 setattr(self, attr, value) 933 934 attr = 'default_level' 935 value = config.get(attr) 936 if value is not None: 937 try: 938 level = parse_log_level(value) 939 except Exception, e: 940 raise ValueError("could not set %s (%s)" % (attr, e)) 941 setattr(self, attr, level) 942 943 attr = 'handlers' 944 handlers = config.get(attr) 945 if handlers is not None: 946 for item in handlers: 947 logger = self.root_logger 948 config = None 949 if isinstance(item, dict): 950 config = item 951 elif isinstance(item, tuple): 952 if len(item) != 2: 953 raise ValueError('handler tuple must have exactly 2 items, got "%s"' % item) 954 config = item[0] 955 logger = item[1] 956 else: 957 raise TypeError('expected dict or tuple for handler item, got "%s", handlers=%s' % \ 958 type(item), value) 959 960 if not isinstance(config, dict): 961 raise TypeError('expected dict for handler config, got "%s"', type(config)) 962 if isinstance(logger, basestring): 963 logger = self.get_logger(logger) 964 else: 965 if not isinstance(logger, logging.Logger): 966 raise TypeError('expected logger name or logger object in %s' % item) 967 968 self.create_log_handlers([config], logger, configure_state) 969 970 if self.verbose: 971 self.default_level = logging.INFO 972 973 if self.debug: 974 self.default_level = logging.DEBUG 975 976 self.apply_configuration(configure_state)
977
978 - def create_log_handlers(self, configs, logger=None, configure_state=None):
979 ''' 980 Create new handlers and attach them to a logger (log mangers 981 root logger by default). 982 983 *Note, you may also pass the handler configs to `LogManager.configure()`.* 984 985 configs is an iterable yielding a dict. Each dict configures a 986 handler. Currently two types of handlers are supported: 987 988 * stream 989 * file 990 991 Which type of handler is created is determined by the presence of 992 the ``stream`` or ``filename`` in the dict. 993 994 Configuration keys: 995 =================== 996 997 Handler type keys: 998 ------------------ 999 1000 Exactly of the following must present in the config dict: 1001 1002 stream 1003 Use the specified stream to initialize the StreamHandler. 1004 1005 filename 1006 Specifies that a FileHandler be created, using the specified 1007 filename. 1008 1009 Common keys: 1010 ------------ 1011 1012 name 1013 Set the name of the handler. This is optional but can be 1014 useful when examining the logging configuration. 1015 For files defaults to ``'file:absolute_path'`` and for streams 1016 it defaults to ``'stream:stream_name'`` 1017 1018 format 1019 Use the specified format string for the handler. 1020 1021 time_zone_converter 1022 Log record timestamps are seconds since the epoch in the UTC 1023 time zone stored as floating point values. When the formatter 1024 inserts a timestamp via the %(asctime)s format substitution it 1025 calls a time zone converter on the timestamp which returns a 1026 time.struct_time value to pass to the time.strftime function 1027 along with the datefmt format conversion string. The time 1028 module provides two functions with this signature, 1029 time.localtime and time.gmtime which performs a conversion to 1030 local time and UTC respectively. time.localtime is the default 1031 converter. Setting the time zone converter to time.gmtime is 1032 appropriate for date/time strings in UTC. The 1033 time_zone_converter attribute may be any function with the 1034 correct signature. Or as a convenience you may also pass a 1035 string which will select either the time.localtime or the 1036 time.gmtime converter. The case insenstive string mappings 1037 are:: 1038 1039 'local' => time.localtime 1040 'localtime' => time.localtime 1041 'gmt' => time.gmtime 1042 'gmtime' => time.gmtime 1043 'utc' => time.gmtime 1044 1045 datefmt 1046 Use the specified time.strftime date/time format when 1047 formatting a timestamp via the %(asctime)s format 1048 substitution. The timestamp is first converted using the 1049 time_zone_converter to either local or UTC 1050 1051 level 1052 Set the handler logger level to the specified level. May be 1053 one of the following strings: 'debug', 'info', 'warn', 1054 'warning', 'error', 'critical' or any of the logging level 1055 constants. Thus level='debug' is equivalent to 1056 level=logging.DEBUG. Defaults to self.default_level. 1057 1058 1059 File handler keys: 1060 ------------------ 1061 1062 filemode 1063 Specifies the mode to open the file. Defaults to 'a' for 1064 append, use 'w' for write. 1065 1066 permission 1067 Set the permission bits on the file (i.e. chmod). 1068 Must be a valid integer (e.g. 0660 for rw-rw----) 1069 1070 user 1071 Set the user owning the file. May be either a numeric uid or a 1072 basestring with a user name in the passwd file. 1073 1074 group 1075 Set the group associated with the file, May be either a 1076 numeric gid or a basestring with a group name in the groups 1077 file. 1078 1079 Examples: 1080 --------- 1081 1082 The following shows how to set two handlers, one for a file 1083 (ipa.log) at the debug log level and a second handler set to 1084 stdout (e.g. console) at the info log level. (One handler sets it 1085 level with a simple name, the other with a logging constant just 1086 to illustrate the flexibility) :: 1087 1088 # Get a root logger 1089 log_mgr = LogManger('my_app') 1090 1091 # Create the handlers 1092 log_mgr.create_log_handlers([dict(filename='my_app.log', 1093 level='info', 1094 user='root', 1095 group='root', 1096 permission=0600, 1097 time_zone_converter='utc', 1098 datefmt='%Y-%m-%dT%H:%M:%SZ', # ISO 8601 1099 format='<%(levelname)s> [%(asctime)s] module=%(name)s "%(message)s"'), 1100 dict(stream=sys.stdout, 1101 level=logging.ERROR, 1102 format='%(levelname)s: %(message)s')]) 1103 1104 # Create a logger for my_app.foo.bar 1105 foo_bar_log = log_mgr.get_logger('foo.bar') 1106 1107 root_logger.info("Ready to process requests") 1108 foo_bar_log.error("something went boom") 1109 1110 In the file my_app.log you would see:: 1111 1112 <INFO> [2011-10-26T01:39:00Z] module=my_app "Ready to process requests" 1113 <ERROR> [2011-10-26T01:39:00Z] module=may_app.foo.bar "something went boom" 1114 1115 On the console you would see:: 1116 1117 ERROR: something went boom 1118 1119 :parameters: 1120 configs 1121 Sequence of dicts (any iterable yielding a dict). Each 1122 dict creates one handler and contains the configuration 1123 parameters used to create that handler. 1124 logger 1125 If unspecified the handlers will be attached to the 1126 LogManager.root_logger, otherwise the handlers will be 1127 attached to the specified logger. 1128 configure_state 1129 If other than None update the log manger's configure_state 1130 variable to this object. Clients of the log manager can 1131 use configure_state to track the state of the log manager. 1132 1133 :return: 1134 The list of created handers. 1135 ''' 1136 if logger is None: 1137 logger = self.root_logger 1138 1139 handlers = [] 1140 1141 # Iterate over handler configurations. 1142 for cfg in configs: 1143 # File or stream handler? 1144 filename = cfg.get('filename') 1145 if filename: 1146 if cfg.has_key("stream"): 1147 raise ValueError("both filename and stream are specified, must be one or the other, config: %s" % cfg) 1148 path = os.path.abspath(filename) 1149 filemode = cfg.get('filemode', 'a') 1150 handler = logging.FileHandler(path, filemode) 1151 1152 # Set the handler name 1153 name = cfg.get("name") 1154 if name is None: 1155 name = 'file:%s' % (path) 1156 handler.name = name 1157 1158 # Path should now exist, set ownership and permissions if requested. 1159 1160 # Set uid, gid (e.g. chmod) 1161 uid = gid = None 1162 user = cfg.get('user') 1163 group = cfg.get('group') 1164 if user is not None: 1165 if isinstance(user, basestring): 1166 pw = pwd.getpwnam(user) 1167 uid = pw.pw_uid 1168 elif isinstance(user, int): 1169 uid = user 1170 else: 1171 raise TypeError("user (%s) is not int or basestring" % user) 1172 if group is not None: 1173 if isinstance(group, basestring): 1174 pw = pwd.getpwnam(group) 1175 gid = pw.pw_gid 1176 elif isinstance(group, int): 1177 gid = group 1178 else: 1179 raise TypeError("group (%s) is not int or basestring" % group) 1180 if uid is not None or gid is not None: 1181 if uid is None: 1182 uid = -1 1183 if gid is None: 1184 gid = -1 1185 os.chown(path, uid, gid) 1186 1187 # Set file permissions (e.g. mode) 1188 permission = cfg.get('permission') 1189 if permission is not None: 1190 os.chmod(path, permission) 1191 else: 1192 stream = cfg.get("stream") 1193 if stream is None: 1194 raise ValueError("neither file nor stream specified in config: %s" % cfg) 1195 1196 handler = logging.StreamHandler(stream) 1197 1198 # Set the handler name 1199 name = cfg.get("name") 1200 if name is None: 1201 name = 'stream:%s' % (stream) 1202 handler.name = name 1203 1204 # Add the handler 1205 handlers.append(handler) 1206 1207 # Configure message formatting on the handler 1208 format = cfg.get("format", LOGGING_DEFAULT_FORMAT) 1209 datefmt = cfg.get("datefmt", None) 1210 formatter = logging.Formatter(format, datefmt) 1211 time_zone_converter = cfg.get('time_zone_converter', time.localtime) 1212 if isinstance(time_zone_converter, basestring): 1213 converter = {'local' : time.localtime, 1214 'localtime' : time.localtime, 1215 'gmt' : time.gmtime, 1216 'gmtime' : time.gmtime, 1217 'utc' : time.gmtime}.get(time_zone_converter.lower()) 1218 if converter is None: 1219 raise ValueError("invalid time_zone_converter name (%s)" % \ 1220 time_zone_converter) 1221 elif callable(time_zone_converter): 1222 converter = time_zone_converter 1223 else: 1224 raise ValueError("time_zone_converter must be basestring or callable, not %s" % \ 1225 type(time_zone_converter)) 1226 1227 formatter.converter = converter 1228 handler.setFormatter(formatter) 1229 1230 # Set the logging level 1231 level = cfg.get('level') 1232 if level is not None: 1233 try: 1234 level = parse_log_level(level) 1235 except Exception, e: 1236 print >>sys.stderr, 'could not set handler log level "%s" (%s)' % (level, e) 1237 level = None 1238 if level is None: 1239 level = self.default_level 1240 handler.setLevel(level) 1241 1242 for handler in handlers: 1243 if handler.name in self.handlers: 1244 raise ValueError('handler "%s" already exists' % handler.name) 1245 logger.addHandler(handler) 1246 self.handlers[handler.name] = handler 1247 self.apply_configuration(configure_state) 1248 return handlers
1249
1250 - def get_handler(self, handler_name):
1251 ''' 1252 Given a handler name return the handler object associated with 1253 it. 1254 1255 :parameters: 1256 handler_name 1257 Name of the handler to look-up. 1258 1259 :returns: 1260 The handler object associated with the handler name. 1261 ''' 1262 handler = self.handlers.get(handler_name) 1263 if handler is None: 1264 raise KeyError('handler "%s" is not defined' % handler_name) 1265 return handler
1266
1267 - def set_handler_level(self, handler_name, level, configure_state=None):
1268 ''' 1269 Given a handler name, set the handler's level, return previous level. 1270 1271 :parameters: 1272 handler_name 1273 Name of the handler to look-up. 1274 level 1275 The new level for the handler. See 1276 `log_manager.parse_log_level()` for details on how the 1277 level can be specified. 1278 configure_state 1279 If other than None update the log manger's configure_state 1280 variable to this object. Clients of the log manager can 1281 use configure_state to track the state of the log manager. 1282 1283 :returns: 1284 The handler's previous level 1285 ''' 1286 handler = self.get_handler(handler_name) 1287 level = parse_log_level(level) 1288 prev_level = handler.level 1289 handler.setLevel(level) 1290 self.apply_configuration(configure_state) 1291 return prev_level
1292
1293 - def get_loggers_with_handler(self, handler):
1294 ''' 1295 Given a handler return a list of loggers that hander is bound to. 1296 1297 1298 :parameters: 1299 handler 1300 The name of a handler or a handler object. 1301 1302 :returns: 1303 List of loggers with the handler is bound to. 1304 ''' 1305 1306 if isinstance(handler, basestring): 1307 handler = self.get_handler(handler) 1308 elif isinstance(handler, logging.Handler): 1309 if not handler in self.handlers.values(): 1310 raise ValueError('handler "%s" is not managed by this log manager' % \ 1311 logging_obj_str(handler)) 1312 else: 1313 raise TypeError('handler must be basestring or Handler object, got %s' % type(handler)) 1314 1315 loggers = [] 1316 for logger in self.loggers.values(): 1317 if handler in logger.handlers: 1318 loggers.append(logger) 1319 1320 return loggers
1321
1322 - def remove_handler(self, handler, logger=None, configure_state=None):
1323 ''' 1324 Remove the named handler. If logger is unspecified the handler 1325 will be removed from all managed loggers, otherwise it will be 1326 removed from only the specified logger. 1327 1328 :parameters: 1329 handler 1330 The name of the handler to be removed or the handler object. 1331 logger 1332 If unspecified the handler is removed from all loggers, 1333 otherwise the handler is removed from only this logger. 1334 configure_state 1335 If other than None update the log manger's configure_state 1336 variable to this object. Clients of the log manager can 1337 use configure_state to track the state of the log manager. 1338 ''' 1339 1340 if isinstance(handler, basestring): 1341 handler = self.get_handler(handler) 1342 elif not isinstance(handler, logging.Handler): 1343 raise TypeError('handler must be basestring or Handler object, got %s' % type(handler)) 1344 1345 handler_name = handler.name 1346 if handler_name is None: 1347 raise ValueError('handler "%s" does not have a name' % logging_obj_str(handler)) 1348 1349 loggers = self.get_loggers_with_handler(handler) 1350 1351 if logger is None: 1352 for logger in loggers: 1353 logger.removeHandler(handler) 1354 del self.handlers[handler_name] 1355 else: 1356 if not logger in loggers: 1357 raise ValueError('handler "%s" is not bound to logger "%s"' % \ 1358 (handler_name, logging_obj_str(logger))) 1359 logger.removeHandler(handler) 1360 if len(loggers) == 1: 1361 del self.handlers[handler_name] 1362 1363 self.apply_configuration(configure_state)
1364
1365 - def apply_configuration(self, configure_state=None):
1366 ''' 1367 Using the log manager's internal configuration state apply the 1368 configuration to all the objects managed by the log manager. 1369 1370 :parameters: 1371 configure_state 1372 If other than None update the log manger's configure_state 1373 variable to this object. Clients of the log manager can 1374 use configure_state to track the state of the log manager. 1375 1376 ''' 1377 if configure_state is not None: 1378 self.configure_state = configure_state 1379 for logger in self.loggers.values(): 1380 self._set_configured_logger_level(logger)
1381
1382 - def get_configured_logger_level(self, name):
1383 ''' 1384 Given a logger name return it's level as defined by the 1385 `LogManager` configuration. 1386 1387 :parameters: 1388 name 1389 logger name 1390 :returns: 1391 log level 1392 ''' 1393 level = self.default_level 1394 for regexp, config_level in self.logger_regexps: 1395 if re.search(regexp, name): 1396 level = config_level 1397 break 1398 1399 level = parse_log_level(level) 1400 return level
1401
1402 - def get_logger_handlers(self, logger):
1403 ''' 1404 Return the set of unique handlers visible to this logger. 1405 1406 :parameters: 1407 logger 1408 The logger whose visible and enabled handlers will be returned. 1409 1410 :return: 1411 Set of handlers 1412 ''' 1413 handlers = set() 1414 1415 while logger: 1416 for handler in logger.handlers: 1417 handlers.add(handler) 1418 if logger.propagate: 1419 logger = logger.parent 1420 else: 1421 logger = None 1422 return handlers
1423
1424 - def get_minimum_handler_level_for_logger(self, logger):
1425 ''' 1426 Return the minimum handler level of all the handlers the 1427 logger is exposed to. 1428 1429 :parameters: 1430 logger 1431 The logger whose handlers will be examined. 1432 1433 :return: 1434 The minimum of all the handler's levels. If no 1435 handlers are defined sys.maxint will be returned. 1436 ''' 1437 1438 handlers = self.get_logger_handlers(logger) 1439 min_level = get_minimum_level(handlers) 1440 return min_level
1441
1442 - def _set_configured_logger_level(self, logger):
1443 ''' 1444 Based on the current configuration maintained by the log 1445 manager set this logger's level. 1446 1447 If the level specified for this logger by the configuration is 1448 less than the minimum level supported by the output handlers 1449 the logger is exposed to then adjust the logger's level higher 1450 to the minimum handler level. This is a performance 1451 optimization, no point in emitting a log message if no 1452 handlers will ever output it. 1453 1454 :parameters: 1455 logger 1456 The logger whose level is being configured. 1457 1458 :return: 1459 The level actually set on the logger. 1460 ''' 1461 level = self.get_configured_logger_level(logger.name) 1462 minimum_handler_level = self.get_minimum_handler_level_for_logger(logger) 1463 if level < minimum_handler_level: 1464 level = minimum_handler_level 1465 logger.setLevel(level) 1466 return level
1467
1468 - def get_logger(self, who, bind_logger_names=False):
1469 ''' 1470 Return the logger for an object or a name. If the logger 1471 already exists return the existing instance otherwise create 1472 the logger. 1473 1474 The who parameter may be either a name or an object. 1475 Loggers are identified by a name but because loggers are 1476 usually bound to a class this method is optimized to handle 1477 that case. If who is an object: 1478 1479 * The name object's module name (dot seperated) and the 1480 object's class name. 1481 1482 * Optionally the logging output methods can be bound to the 1483 object if bind_logger_names is True. 1484 1485 Otherwise if who is a basestring it is used as the logger 1486 name. 1487 1488 In all instances the root_logger_name is prefixed to every 1489 logger created by the manager. 1490 1491 :parameters: 1492 who 1493 If a basestring then use this as the logger name, 1494 prefixed with the root_logger_name. Otherwise who is treated 1495 as a class instance. The logger name is formed by prepending 1496 the root_logger_name to the module name and then appending the 1497 class name. All name components are dot seperated. Thus if the 1498 root_logger_name is 'my_app', the class is ParseFileConfig 1499 living in the config.parsers module the logger name will be: 1500 ``my_app.config.parsers.ParseFileConfig``. 1501 bind_logger_names 1502 If true the class instance will have the following bound 1503 to it: ``log``, ``debug()``, ``info()``, ``warning()``, 1504 ``error()``, ``exception()``, ``critical()``. Where log is 1505 the logger object and the others are the loggers output 1506 methods. This is a convenience which allows you emit 1507 logging messages directly, for example:: 1508 1509 self.debug('%d names defined', self.num_names). 1510 1511 :return: 1512 The logger matching the name indicated by who. If the 1513 logger pre-existed return that instance otherwise create the 1514 named logger return it. 1515 ''' 1516 1517 is_object = False 1518 if isinstance(who, basestring): 1519 obj_name = who 1520 else: 1521 is_object = True 1522 obj_name = '%s.%s' % (who.__module__, who.__class__.__name__) 1523 1524 if obj_name == self.root_logger_name: 1525 logger_name = obj_name 1526 else: 1527 logger_name = self.root_logger_name + '.' + obj_name 1528 1529 # If logger not in our cache then create and initialize the logger. 1530 logger = self.loggers.get(logger_name) 1531 if logger is None: 1532 logger = logging.getLogger(logger_name) 1533 self.loggers[logger_name] = logger 1534 self._set_configured_logger_level(logger) 1535 1536 if bind_logger_names and is_object and getattr(who, '__log_manager', None) is None: 1537 setattr(who, '__log_manager', self) 1538 method = 'log' 1539 if hasattr(who, method): 1540 raise ValueError('%s is already bound to %s' % (method, repr(who))) 1541 setattr(who, method, logger) 1542 1543 for method in logger_method_names: 1544 if hasattr(who, method): 1545 raise ValueError('%s is already bound to %s' % (method, repr(who))) 1546 setattr(who, method, getattr(logger, method)) 1547 1548 return logger
1549