[Python-Dev] PEP282 and the warnings framework

Vinay Sajip vinay_sajip@yahoo.co.uk
Fri, 17 May 2002 13:24:32 +0100

> They don't have to as long as they don't use custom LogRecord.

Yes, but by offering a class-based interface (user-created LogRecords) I am
more or less encouraging it, since people would not expect adverse effects
just by deriving a class.

> >>def log(event, eventtype=LogRecord):
> >>     if not isinstance(event, LogRecord):
> >>         event = eventtype(event)
> >>     ... the rest of the code
> >
> >
> > I think a better design is to pass in a callable which constructs the
> > LogRecord or a derived class thereof. This way, you can use a class, a
> > function, a bound method which has access to other context, or whatever.
> This will work in my version without a problem, because
> a function is not an instance of LogRecord.

True enough, though it's more by accident than design.

> > def log(..., record=LogRecord):
> >     ...check to see if logging will happen...
> >     record = record(...args)
> >     self.handle(record)
> OK, but when you pass in something that is already a LogRecord instance
> you have a problem with the "record = record(...args)" call. And

The semantics of my suggestion is - pass in a callable which returns an
instance. It wasn't intended to be used for passing in an instance. A class
is just a convenient callable.
> maybe the LogRecord instance would like to decide about whether
> logging should happen or not.

There's already three places where decisions about "whether to process an
event" take place - loggers, handlers and filters. I'm already concerned
about having three, and I'm not sure its a good idea to have a fourth! Some
argument can be made for having the three:

Logger level filtering to control verbosity based on application area -
offers simplicity for best performance
Handler level filtering to control verbosity as shown to a given audience
Filter level filtering to support more flexible filtering, at some cost to

> > The callable would be called with the same arguments as
> > currently is - it can do what it wants with them.

My idea is still incomplete - I still haven't considered what the callable
should be called with (my last suggestion - the same as Logger.makeRecord
is, I think, too clunky).

> But why do you want to move the construction of the LogRecord
> instance into the logger code. Instead of passing the constructor
> arguments of the LogRecord and the type/callable to the logger
> and let the logger create the LogRecord instance, why shouldn't
> it be  possible to pass in a LogRecord instance?

I'm not yet sure what's best. The main reason why I wanted to control
LogRecord creation was that some aspects of this creation need to be
thread-safe: particularly the calls which walk the stack to find the
filename/lineno of the caller. But I could make this a method of LogRecord
which is called when the Logger actually starts to process the LogRecord.
The reason I didn't do this initially was that I viewed a LogRecord as just
a home for the event attributes, little more than a dictionary.

> > logger.warn("Hello, %s", "world!", record=myRecordMaker)
> So go one step further and allow:
>    logger.warn(myRecordMaker("Hello, %s", "world!"))

It's more than just a step further. In my case, it's an optional argument.
In your case, everyone has to do it that way. I respect your needs, but also
the needs of those who just want to pass a format string and some optional
arguments. After all, most instances of use will probably follow the simpler

> (I assume that MySpecialRecord is derived from LogRecord).
> Either the LogRecord constructor should be responsible
> for creating the timestamp and finding file/line info. etc.
> or this could be done by the logger (via calling a method
> on the LogRecord instance).

Yes, I had the same idea, mentioned earlier in this post. Still thinking on
it :-)



Do You Yahoo!?
Get your free @yahoo.com address at http://mail.yahoo.com