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

Version 1 (modified by jorge.vargas, 13 years ago) (diff)

--

this is NOT finish

Undestanding the Logger

As the original post<link to post> said this is a normal python logger so we go to  http://docs.python.org/lib/module-logging.html to learn how to use it. there 2 places where logging is stored both mark by the tag [logging] your dev/prod.cfg and the config/log.cfg the idea is that log.cfg will have things that don't change based on the location.

Please note <your package> is use across this doc, on your local copy it should say the name of your project's package

Using the Logger

Basically what you need is a quick read of  http://docs.python.org/lib/module-logging.html the first page is sufficient for now. after that two lines in your controllers.py is all you need to get going

log = logging.getLogger("<your package>.controllers")

That is the prefered way to create a logger the param string is the name which could be anything, although it's good practice to follow a convention, here TG by default suggest to name the logger after the package/module since this is a unique name.

the other line you should care about is

log.debug("Happy TurboGears Controller Responding For Duty")
}{{

Which as you read in http://docs.python.org/lib/module-logging.html u did right? is a log with level 10 you also should know that :)

== Why not just use logging.debug(msg)? ==

because we want to be able to do this [http://docs.python.org/lib/multiple-destinations.html Multiple Destinations] so you can later find out if the error is on your side or on the framework just by looking at the log. You can also send diferent logs to diferent places, in general is a better practice to do it this way, and you will take full advantage of the python logging module.

= TurboGears log.cfg =
if you are used to the "normal" way of creating loggers, either by logging.basicConfig or [http://docs.python.org/lib/logging-config-fileformat.html files] you may get confused by this format.

So lets start with the format this is a ConfigObj and its format is at [http://www.voidspace.org.uk/python/configobj.html#the-config-file-format ConfigObj File Format] for you the framework user the only diference from [http://docs.python.org/lib/module-ConfigParser.html ConfigParser] is the extra nested tags (\[\]) and by now you should know that those generate a dict inside another. On the other hand if you want to explore the goodies of ConfigObj Here are the [http://www.voidspace.org.uk/python/configobj.html docs]

by the way if you ever want to store python type in a config file [http://www.voidspace.org.uk/python/configobj.html#unrepr-mode unrepr] is what you need which is exactly what TG is doing and yes those are format strings and python lists on the default log.cfg

== What is this *()  ==
think of it as %()
=== Why? ===
http://trac.turbogears.org/turbogears/browser/tags/0.9a5/turbogears/config.py#L117 and if you still dont believe me 
http://trac.turbogears.org/turbogears/browser/tags/0.9a5/turbogears/config.py#L23 now can we go on?

== log.cfg format==
So the file always starts with 
{{{
[logging]
}}}
This is the head if the logging cfg, if we ever more this to other file the core will still work thanks to configObj

then you have 3 sections 
{{{
[[formatters]]
[[handlers]]
[[loggers]]
}}}

where all are optional although you should at least have \[\[handlers\]\] 
after this all lvl 3 are just names which as all dict keys must be unique

== Formatters ==
=== format key ===
each lvl 3 aggregate should have only one key call format which becomes the fmt param of a [http://docs.python.org/lib/node357.html Formatter Object], there you can see all the possible values this can have, as well as what happens went ommited

== handlers ==
=== class key ===
each handler must have at least a class key which has to be one of [http://docs.python.org/lib/node345.html Handler Objects] and yes you can subclass it but I think you will have enough with the defaults, also TG will error out since your class is not part of logging.__dict__
=== args key ===
the "optional" args key are the parameters pass to each subclass of Handler
=== level key ===
the level key indicates the loggin level of this handler this can be any of the default levels DEBUG, INFO, WARNING, ERROR and CRITICAL or a string with the number, i got a patch to fix this TODO summit patch
=== formatter key ===
formatter any lvl 3 name form formatters 
if level is ommited level=NOTSET if formatter is omitted the default is used '%(message)s'

{{{
...
[[[full_content]]]
format='*(asctime)s *(name)s *(levelname)s *(message)s'
...
[[[debug_out]]]
class='StreamHandler'
level='DEBUG'
args='(sys.stdout,)'
formatter='full_content'
}}}
So now this makes sence We are creating a StreamHandler to sys.stdout with lvl debug using the formatted defined above 

== loggers ==
This is a 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 there for I'll make a reference to the method call and you will know how to do it both ways and I'll type it ones
=== qualname ===
name of this logger, 
log=logging.getLogger(qualname)
if omitted this is the root logger
log=logging.getLogger()
=== level === 
log.setLevel(level)
=== handlers === 
handlers is a list so 
for handler in handlers:
 log.addHandlers(handler)
=== propagate === 
 log.propagate=propagate

== Putting it all together==

=== in log.cfg ===

{{{
[logging]
[[formatters]]
[[[message_only]]]
format='*(message)s'

[[[full_content]]]
format='*(asctime)s *(name)s *(levelname)s *(message)s'

[[handlers]]
[[[debug_out]]]
class='StreamHandler'
level='DEBUG'
args='(sys.stdout,)'
formatter='full_content'

[[[access_out]]]
class='StreamHandler'
level='INFO'
args='(sys.stdout,)'
formatter='message_only'

[[[error_out]]]
class='StreamHandler'
level='ERROR'
args='(sys.stdout,)'
}}}

We define 2 formatters named message_only and full_content, then we define 3 handlers which all Stream to stdout with diferent levels and using the two formaters we define, and we have no loggers because we are handling that from controllers.py

=== In dev.cfg ===
{{{
[logging]

[[loggers]]
[[[your_project]]]
level='DEBUG'
qualname='<your package>'
handlers=['debug_out']

[[[allinfo]]]
level='INFO'
handlers=['debug_out']

[[[access]]]
level='INFO'
qualname='turbogears.access'
handlers=['access_out']
propagate=0
}}}

We define 3 loggers [[[your_project]]] which is the one we use at the start of this tutorial that prints DEBUG messages to stdout, [[[allinfo]]] which is the main logger (notice missing qualname) and [[[access]]] that prints INFO messages to stdout.

So now that you finally undestand how it works it's time to turn it around and do lots of stuff with it check out (LoggingConfiguration move this to LoggingConfigurationExamples?, and add an explanation) for some nice examples.

= Custom level Logging =
TODO add patch to add custom levels