[Python-Dev] PEP282 and the warnings framework

Vinay Sajip vinay_sajip@red-dove.com
Thu, 16 May 2002 22:43:38 +0100


> > The last part (about __str__) is reasonable. But I disagree with the
> > statement that the LogRecord should do formatting.
>
> I meant only the formatting of the message itself, i.e. the
> "self.msg % self.args" part. Timestamps, file/line info etc. should
> be the responsiblity of the Logger/Formatter.

Oh, sorry I misunderstood. I thought you meant that the self.msg % self.args
was just the *default* implementation, and that subclasses might change
this.

> You're right, configuration should be changable at runtime. Doing this
> when configuration involves custom classes is hard, but this shouldn't
> be your job, but the job of those that implement these custom classes.

I agree it's not my job. I only point out that it involves extra work,
whoever ends up doing it. I don't want people who want to use logging in a
simple way (for less complex applications) to have to do the extra work.

> > You can subclass Logger and redefine makeRecord.
>
> Yes, that's the purpose of the factory method, but then I have to
> replace the factory method that generates the factory ...

Perhaps I used the wrong term? According to my reading of "Design Patterns"
(Gamma, Helms et al) the term "factory method" is intended to refer to a
method which encapsulates creation of an object. It's intended to be
redefined in subclasses, so that they can manage their own object creation.
But I get what you mean.

>
> ... which is done here. But this would change the LogRecord classes
> that are used on a global scale. I'd like to decide which class should
> be used when I'm creating the event. BTW, in setLoggerClass() you should
> probably use
>      if not issubclass(klass, Logger):
> instead of
>      if not (Logger in klass.__bases__):

Thanks for the tip. I've updated the source to use issubclass for 0.4.5!

> What I want is something like this (similar to what the warning
> framework does):
>
> 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.

def log(..., record=LogRecord):
    ...check to see if logging will happen...
    record = record(...args)
    self.handle(record)

The callable would be called with the same arguments as Logger.makeRecord
currently is - it can do what it wants with them. So you could do...

def myRecordMaker(**kwargs):
    record = MySpecialRecord(...)
    record.__dict__.update(kwargs)
    return record

and then:

logger.warn("Hello, %s", "world!", record=myRecordMaker)

I think this is neater and more Pythonic than the factory method :-) Let me
mull this one over a bit to see if there are any other repercussions.
Comments, please?

Regards


Vinay