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 4 (modified by jorge.vargas, 13 years ago) (diff)

--

NOTE: 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. the're 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  Multiple Destinations 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 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 .cfg files

if you are used to the "normal" way of creating loggers, either by logging.basicConfig or  config files you may get confused by this format.

So lets start with the format this is a ConfigObj? and its format is at  ConfigObj File Format for you the framework user the only diference from  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  docs

by the way if you ever want to store python type in a config file  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 change it?

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?

logging format

So the file log.cfg file always starts with

[logging]

and there is a section in dev/prod.cfg with the same name, they are all load up into the same dict so in practice defining things in either is ok.

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" and "loggers" after this all lvl 3 are just names which as all dict keys and therefore must be unique

Formatters

format key

each lvl 3 aggregate should have only one key call format which becomes the fmt param of a  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  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  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

if qualname:
log=logging.getLogger(qualname)
else:
log=logging.getLogger()

level

log.setLevel(level)

handlers

handlers is a list so

for handler in handlers:
 log.addHandlers(handler)

propagate

log.propagate=propagate

And no those are "examples" it's not the way TG does it.

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.

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.

NOTE: "your_project" is no longer there as of r1367 and 0.9a6, it was a "typo"

What now

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