Hello, Friends!
Have just re-configured my laptop to be a
contributor's box, ready to get to work :-)
What would you say to the following changes to
*LoggerManager:
1) The creator of LoggerManager passes an initial
logger to it.
This is the logger that LoggerManager outputs
messages to while it's
contextualize()
configure()
stages. This is all what normal components do:
get a logger and log to it.
In practice, with Fortress for instance, at
Fortress creation stage user will create
a ConsoleLogger or a ServletLogger or
something alike - bulletproof that will
log the early stages of the startup.
Then he the user would pass this
ConsoleLogger or ServletLogger to
DefaultContainerManager for instance.
This, the "initialization" logger will
be passed to LoggerManager to log its
contextualize() and configure()
2) After the logger manager has passed all
its initialization steps successfully
(currenlty our *LoggerManager have only
two such steps: contextualize() and
configure())
we shall have an option of the LoggerManager
to switch its own logging to a Logger
extracted from itself
To implement this I propose a new
3-argument constructor
XXXLoggerManager(
String prefix,
Logger initialLogger,
String switchToCategory )
The last argument will be the name
of the category to do
this.getLoggerForCategory( m_switchToCategory )
This will be the logger to log messages from
LoggerManager to after the switch.
Passing null as the last argument to this
constructor will disable this feature - the
logging will still happen to initialLogger.
If the initalLogger will be set to null
no logging will happen until end of configuration
when the switch is done to m_switchToCategory().
If both initialLogger and switchToCategory will
be null, no logging will happen at all.
I intend to create the AbstractLoggerManager
class that will implement this logger switching
functionality.
It is going to supply via getLogger()
a special Logger that will
wrap two underlying loggers:
* m_fallback
this will be the initialLogger passed to
XXXLoggerManager on creation or via enableLogging()
* m_preferred
the logger extracted via this.getLoggerForCategory(
m_switchToCategory )
So I'm going to implement getLogger() method in
AbstractLoggerManager that will return a special
o.a.a.e.logger.util.LoggerSwitch.SwitchingLogger
logger that will internally log to either
m_fallback or to m_preferred
3) One more thing I'm going to do [LogKitLoggerManager]
LogKit logging system has an ErrorHandler, a "logger"
that is used to "log" logging errors.
There is one such thing per the whole hierarchy.
The default implementation just writes to System.err
Here is what I'm going to do about this:
_if_ a LogKitLoggerManager is created with a constructor
that does not accept a Hierarchy as its argument and
we create the Hierarchy ourselves as
new Hierarchy()
then we shall set our own ErrorHandler for it.
This ErrorHandler will do the following:
it will try to log errors via getLogger()
This means that errors in logging with LogKit
will be attempted to be logged via LogKit itself!
But, recursive invocation will be tracked with
a couple of ThreadLocal variables and in case of
recursion the error will be logged via the
initialLogger
originally supplied to LogKitLoggerManager.
This is done for the following situation:
imagine we configure
<targets>
<... id='unreliable' .../>
<... id='reliable' .../>
</targets>
<categories>
<category name='1' ..>
<log-target id-ref='unreliable' />
</category>
<category name='system.logkit' ..>
<log-target id-ref='reliable' />
</category>
</categories>
imagine the LogKitLoggerManager has been created as
new LogKitLoggerManager( "fortress", consoleLogger, "system.logkit" );
and imagine target 'unreliable' fails and invokes
ErrorHandler. Then we shall first try to log
the error via the "fortress.system.logkit" category, that
is via the 'reliable' log target. If it successeds it's all.
If that fails, an error will be attempted to be recursively
logged via the same logger, but this time the recursion will
be detected by
LoggerSwitch.SwitchingLogger
and it will fallback to logging error together with the initial
error via the its m_fallback, that is the initial ConsoleLogger
passed to LogKitLoggerManager.
This scheme seems to be close to optimal to me.
We use the initialLogger as a fallback logger.
4)
I'm also going to do a
m_hierarchy.getRootLogger().unsetLogTargets()
in case we create the hierarchy ourselves. This will
remove the default System.out log target initially
configured by new Hierarchy() constructor.
And _if_ we detect that user has supplied no log targets
for the "" caterogy we will complain loudly via an exception.
5)
What a little worries me is what to do if we have been
given a Hierarchy from outside, like via the
LogKitLoggerManager( String prefix, Hierarchy hierarchy )
constructor. If we're not given it and we are creating the
Hierarchy ouselves we'll do
m_hierarchy.getRootLogger().unsetLogTargets()
m_hierarchy.setErrorHandler( m_errorHandler );
But if the hierarchy has been passed to us already created,
should we do _any_ of this? I'm currently inclined to
thing that we should do _none_, but I would like
an advice.
6)
There's an option to test if our logging is alive
in the end of configure() method by doing
getLoggerForCategory("").info("Logging started");
if this yields an error we can record it in our
custom error handler, in a boolean variable. Then we
can examine it right away and if there was an error
we can blow up with an exception.
However I have certain reservations on this:
a) this won't work if the log-level is above "info"
b) this won't work if the Hierarchy() has been passed
ready-made to use (we won't have our custom error
handler installed into it in this case)
c) this will produce an extra line in the logs in this
case (is this a plus or a minus?)
Please, give me an advice on this feature.
7)
The current implementation of LogKitLoggerManager
does the following: if it is given a Logger it uses
this logger as its defaultLogger. That is it has
(I have changed the names of arguments a bit, but still
I hope this all understandable)
LogKitLoggerManager(
String prefix,
Logger defaultLogger,
Logger interanlLogger )
{
m_defaultLogger = defaultLogger;
...
}
Logger getDefaultLogger()
{
return m_defaultLogger;
}
I must confess I sort of dislike this. I beleive that
we should do instead an equivalent of
Logger getDefaultLogger()
{
return getLoggerForCategory("");
}
to be true to the LoggerManager contract.
With regards to this issue I would consider the following
options:
a) remove this constructor completely
b) deprecate this constructor but retain functionality
c) leave the constructor undeprecated
I would like an advice on this, I just do not dare to
do a) and b) without a permit, although I feel very much
inclined to do one of this.
8)
There is also one more constructor of LogKitLoggerManager
that I would like to touch
LogKitLoggerManager(
String prefix,
Logger defaultLogger)
This currently has the following contract:
defaultLogger
becomes _both_ the internal logger (the logger to log
LogKitLoggerManager's own messages to and
m_defaultLogger
used in
getDefaultLogger(){ return m_defaultLogger; }
in line with changes discussed in section 7) I would
like to change the contract of this constructor and
make it be
LogKitLoggerManager(
String prefix,
Logger internalLogger )
makeing the Logger argument be just the logger used
for logging our own messages and instead have
getDefaultLogger(){ return getLoggerForCategory(""); }
As I have said I just do not see much point in
initializing m_defaultLogger with an externally supplied
logger since we can very well initialize if from the
config file. (Reference: 6.a) in this mail )
----
Conclusion:
I would certainly not like to make drastic changes untill
allowed to. On the other hand maybe it's better to just
go ahead and implement it and _then_ have a discussion
on why I did that and probably revert some of the changes?
Anyway, I have implemented all the mentioned changes in
my local copy and works fine for me, so I almost have
all the code ready for this.
WBR, Anton :-)
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]