Warning: Can't synchronize with repository "(default)" (Unsupported version control system "svn": No module named svn). Look in the Trac log for more information.

Changes between Version 11 and Version 12 of logging


Ignore:
Timestamp:
06/29/07 10:56:37 (12 years ago)
Author:
Chris Arndt
Comment:

migration notice

Legend:

Unmodified
Added
Removed
Modified
  • logging

    v11 v12  
    1 = Understanding the Logger = 
    2  
    3 As the [http://www.blueskyonmars.com/2006/04/29/turbogears-09a5-released/ original post] said TurboGears uses the Python standard library logger module, which you can read about in detail in the [http://docs.python.org/lib/module-logging.html official Python docs]. 
    4  
    5 The default Turbogears logging is defined in two places: 
    6  1. ''dev.cfg'' and ''prod.cfg'' - From your quickstart directory, contains configuration specific to your environment 
    7  1. ''<your package>/config/log.cfg'' - Contains configuration for your logging that won't change based on your environment 
    8  
    9 Logging specific configuration is marked by the tag [logging] in both of these files. 
    10  
    11 Please note the ''<your package>'' used throughout this doc. Remember to substitute your app's packagename when you try these things out for yourself. 
    12  
    13 = Using the Logger = 
    14 To get the most out of this section, check out the [http://docs.python.org/lib/module-logging.html first page] of the logging module docs. 
    15  
    16 Two lines in your controllers.py are all you need to get going. First, create a logger: 
    17  
    181{{{ 
    19 log = logging.getLogger("<your package>.controllers") 
     2#!rst 
     3.. note:: This page has been migrated to http://docs.turbogears.org/1.0/LoggingSystem. 
    204}}} 
    21  
    22 That is the preferred way to create a logger. The param string is the name of your new logger. Although this could be anything you want, it's good practice to follow a convention and keep your logs organized from the get go. Here we name the logger after the package/module since this is a unique name. 
    23  
    24 One way to easily set up logger names by package is 
    25 {{{ 
    26 log = logging.getLogger(__name__) 
    27 }}} 
    28  
    29 Next, send some info to our new logger object: 
    30  
    31 {{{ 
    32 log.debug("Happy TurboGears Controller Responding For Duty") 
    33 }}} 
    34  
    35 Which, as you read in the official docs, is a log with level 10, the debug level of severity. You can probably just ignore the level numbers, unless you want to create log levels of your own. 
    36  
    37 == Why not just use logging.debug(msg)? == 
    38  
    39 Because then you'd be shorting yourself on the power and flexibility of the python logging module.  We want to be able to send our logs to [http://docs.python.org/lib/multiple-destinations.html Multiple Destinations], as defined by you, so you can later find out if the error is on your side or on the framework just by looking at the logs. You can also send different logs to different places, all without sprinkling your app code with needless boilerplate code, thanks to the config files. 
    40  
    41 In general is a better practice to do it the first way and take full advantage of the python logging module. If not, it's only one extra line of code, anyways. 
    42  
    43 = TurboGears .cfg files = 
    44 If you are used to the "normal" way of creating loggers, either by [http://docs.python.org/lib/logging-config-api.html loggingConfig] or [http://docs.python.org/lib/logging-config-fileformat.html config files] you may get confused by this format. 
    45  
    46 So let's start with the format. This config file is read by ConfigObj and you can check out the documentation for the  [http://www.voidspace.org.uk/python/configobj.html#the-config-file-format ConfigObj File Format] for more detail. For our purposes here the only difference from standard library [http://docs.python.org/lib/module-ConfigParser.html ConfigParser] is the nested tags, which will generate a dictionary of the tag name and fill it with whatever's in that nested section. If you want to explore the goodness of ConfigObj, take a look at the  [http://www.voidspace.org.uk/python/configobj.html docs], especially the part about storing python types in a config file [http://www.voidspace.org.uk/python/configobj.html#unrepr-mode storing python types in a config file], which might clear up what's going on in the default [wiki:logging#inlog.cfg log.cfg]. 
    47  
    48 == What is this *()  == 
    49 Think of it as `%()` since we can't put it into a config file, as ConfigObj interprets it as something else. Just a [source:/tags/0.9a5/turbogears/config.py#L117 slight] [source:/tags/0.9a5/turbogears/config.py#L23 hack], nothing to see here, move along... 
    50  
    51 == Logging Config Format == 
    52 In your config files, always start the logging specific stuff with: 
    53 {{{ 
    54 [logging] 
    55 }}} 
    56  
    57 like in dev/prod.cfg.  They'll all load up into the same dict thanks to ConfigObj so in practice defining things in either file is ok. 
    58  
    59 When all is said and done, you have 3 sections defined ''somewhere'' in your config files like so: 
    60 {{{ 
    61 [[formatters]] 
    62 [[handlers]] 
    63 [[loggers]] 
    64 }}} 
    65  
    66 While all three are technically optional, it's recommended you have at least the "handlers" and "loggers" sections. 
    67  
    68 Let's go over these sections and their keys and what they do. Remember that these will be read into the dictionary as keyword arguments, so make sure you keep them unique: 
    69  
    70 === Formatters === 
    71 Each subsection within this section should have only one key as per the following: 
    72  
    73 `format` 
    74   The format string which initializes a [http://docs.python.org/lib/node357.html Formatter Object]. Just like in the standard docs but remember to replace `%(` with `*(` like we talked about [wiki:logging#Whatisthis above]. 
    75  
    76 == Handlers == 
    77 Each subsection within this section is required to have a class key. 
    78  
    79 `class` 
    80   Should be one of the [http://docs.python.org/lib/node345.html Handler Objects] or a subclass thereof. If you subclass you might get some errors from TG since your class is not in logging.__dict__ 
    81  
    82 `args` 
    83   Parameters pass to each subclass of Handler and will vary based on which on you pick. 
    84  
    85 `level` 
    86   Indicates the logging level of this handler and can be any of the default levels DEBUG, INFO, WARNING, ERROR and CRITICAL. With a patch it can also be a string with a level number (Is this fixed yet?). Defaults to NOTSET.  
    87  
    88 `formatter` 
    89   Can be the name of any subsection that you defined under the [wiki:logging#Formatters formatters] section. Defaults to `%(message)s` 
    90  
    91 == Loggers == 
    92 This is another way to create [http://docs.python.org/lib/node341.html Logger Objects].  You can either define them here or do the calls in your code, it depends on your situation/taste. Code accomplishing the same thing follow the descriptions of the parameters. 
    93  
    94 `qualname` 
    95   Name of the logger 
    96  
    97 {{{ 
    98 if qualname: 
    99 log=logging.getLogger(qualname) 
    100 else: 
    101 log=logging.getLogger() 
    102 }}} 
    103  
    104 `level` 
    105   The level threshold for this handler. Levels less severe than this will be ignored. 
    106  
    107 {{{ 
    108 log.setLevel(level) 
    109 }}} 
    110  
    111 `handlers` 
    112   Equivalent to calling the addhandler method of a [http://docs.python.org/lib/node341.html logger object]. Useful for sending logs to multiple places. 
    113  
    114 {{{ 
    115 for handler in handlers: 
    116  log.addHandlers(handler) 
    117 }}} 
    118  
    119 `propagate` 
    120   Same as the propagate class attribute of a [http://docs.python.org/lib/node341.html logger object]. Sets whether or not log messages are passed to parent loggers. Defaults to 1. 
    121  
    122 {{{ 
    123 log.propagate=propagate 
    124 }}} 
    125  
    126  
    127 == Putting it all together == 
    128 But these examples are not the way TurboGears does it, so let's take a look at how it does! We'll go through file by file. 
    129  
    130 === log.cfg === 
    131  
    132 {{{ 
    133 [logging] 
    134 [[formatters]] 
    135 [[[message_only]]] 
    136 format='*(message)s' 
    137  
    138 [[[full_content]]] 
    139 format='*(asctime)s *(name)s *(levelname)s *(message)s' 
    140  
    141 [[handlers]] 
    142 [[[debug_out]]] 
    143 class='StreamHandler' 
    144 level='DEBUG' 
    145 args='(sys.stdout,)' 
    146 formatter='full_content' 
    147  
    148 [[[access_out]]] 
    149 class='StreamHandler' 
    150 level='INFO' 
    151 args='(sys.stdout,)' 
    152 formatter='message_only' 
    153  
    154 [[[error_out]]] 
    155 class='StreamHandler' 
    156 level='ERROR' 
    157 args='(sys.stdout,)' 
    158 }}} 
    159  
    160 Can you follow it yet? We define 2 formatters named "message_only" and "full_content", then we pick 3 handlers which all stream to stdout but with different levels and using the two formatters we defined in the same file. 
    161  
    162 === dev.cfg === 
    163 {{{ 
    164 [logging] 
    165  
    166 [[loggers]] 
    167 [[[<your_package>]]] 
    168 level='DEBUG' 
    169 qualname='<your package>' 
    170 handlers=['debug_out'] 
    171  
    172 [[[allinfo]]] 
    173 level='INFO' 
    174 handlers=['debug_out'] 
    175  
    176 [[[access]]] 
    177 level='INFO' 
    178 qualname='turbogears.access' 
    179 handlers=['access_out'] 
    180 propagate=0 
    181 }}} 
    182  
    183 Here we create 3 loggers:  
    184  
    185  * '''your_project''' - The one we used at the start of this tutorial, prints DEBUG messages to stdout 
    186  * '''allinfo''' - The main logger (note the missing qualname) 
    187  * '''access''' - Prints INFO messages to stdout. 
    188  
    189 = Conclusion = 
    190  
    191 Now that you understand how it works it's time to turn it around and do lots of stuff with it. Check out LoggingConfigurationExamples for more. 
    192  
    193 TODO add patch to add custom levels