[PEAK] Error handling (was Re: Unifying configuration)
Phillip J. Eby
pje at telecommunity.com
Sat Dec 20 11:26:03 EST 2003
At 12:32 PM 12/20/03 +0200, alexander smishlajev wrote:
>Phillip J. Eby wrote, at 20.12.2003 0:27:
>>
>>>pure ZConfig is a one-in-a-whole thing. if there is an error (either
>>>syntax error, or environment error, like missing directory, preventing
>>>an instantiation of at least one component) the application does not
>>>start at all, even if 90% of it are ok. if we need to change a setting
>>>for one component, we have to restart the whole application.
>>I would note that this is actually a good thing for end-user
>>configuration, because it should prevent somebody breaking something and
>>not noticing.
>
>i would agree with that, but the leader of our support staff is asking us
>to implement starting with config errors.
>
>to address the issue of breaking something and not noticing we should have
>a 'configtest' feature, touching each of the configured components to
>validate their configuration.
Once we have the 'zconfig:' URL schema, you could use them in a binding
with 'suggestParent=False', which would allow you load and instantiate the
objects (validating the configuration) but not actually attaching them to
the component hierarchy (and thus not activating their uponAssembly
bindings). Alternatively, using lookupComponent() with suggestParent=False
would do the same thing, but wouldn't hold on to the created
objects. (I.e. you could just call lookupComponent() and throw the result
away.)
>besides, errors in ZConfig file raise ecxeptions before anything of the
>application is activated, so we cannot use common logging procedures and
>have to produce a raw traceback that cannot be read and understood by
>support people.
Interesting. Well, if I add the zconfig: URL scheme as described in my
previous message, it should be possible to do something like:
class WrappedConfig(binding.Component):
filename = binding.Require("Filename to parse")
url = binding.Make(lambda self: "zconfig:the.schema:file:%s" %
self.filename)
component = binding.Obtain(naming.Indirect('url'))
def onStart(self):
try:
self.component
except ZConfig.something:
# whatever
onStart = binding.Make(onStart, uponAssembly=True)
You could then use this around each file to be loaded.
Or, maybe there should be an 'onError=' keyword for bindings to let you set
a handler for exceptions raised by a Make or Obtain?
Maybe certain kinds of components (such as commands) need to use an
"application error handling" service of some kind, so that startup errors
like ZConfig errors can not only be trapped, logged, and reported, but
application-specific messages can be obtained.
On a related I've also considered having bindings trap computation errors
and wrap them in a "BindingError" that would pinpoint the binding being
computed. But, this has the problem of *masking* the actual error causing
the problem! It may be that improved traceback formatting would be
helpful, as John Landahl has previously suggested off-list. Being able to
replace file and line info with "context" information would be helpful, and
that might be something an error handling service could do. For example,
if it is displaying a traceback that includes framework code, it could
replace the file and line info with information like "Computing attribute
foo on <bar object at 3459357>".
Hm. One interesting way to do that, would be to use the __traceback_info__
variable used to do such things in Zope. Ideally, one would '%' the
variable with the frame locals as you display the traceback. Then, by
simply adding key assignments to __traceback_info__ in various parts of the
framework, we could make it much easier to debug non-framework code, while
giving friendlier error messages. Further, since this would be done by a
pluggable service, one could even have an i18n-friendly traceback formatter
that translates the message prior to inserting the frame locals.
(It's interesting how the recent addition of the [Component Factories]
sections has now made service components more attractive to add and use.)
Anyway, I'm not sure how all the details of error handling services should
work. To some extent, there are some ideas living inside peak.web error
management already, and if we add a general error handling service it
ideally should be usable there as well, so that web errors can be logged, etc.
It will probably also be the case that there will be more than one kind of
error service. One will probably be for logging/reporting them and maybe
trapping/handling/displaying. There will also probably need to be
something specific for traceback formatting, used as a collaborator to
other components. That is, there might be a number of standard traceback
formatters, but you might plug one in to your logging system, and a
different one for end-user display or web viewing. (Note that traceback
formatting in the peak.running.logs subsystem should be replaced with use
of a pluggable traceback formatter.)
I guess I need to stop volunteering to create all these new components,
since my schedule at work is very busy right now. The logger: and zconfig:
changes are probably going to have to wait a while, let alone these new and
undesigned-as-yet error services. The "performance monitors" and "plugin
support" changes are the highest priority for PEAK right now, followed by
certain DB type conversion and stored procedure stuff, due to a project at
work that needs all of those items. But I may have some free time this or
next weekend when I'll be able to work on some of the other things.
In the meantime, if you need to create a top-level error handler, you can
always subclass ZConfigInterpreter or one of the related classes to trap
errors. You can then use an .ini to replace the current handler for either
the ZConfig command or the zconfig.schema: URL with your subclass or
subclasses, as needed. That's one of the really nice things about PEAK:
everything that really matters is subclassable, replaceable, and configurable.
More information about the PEAK
mailing list