Hi,
this morning I took the time to analyze what's going on
with GeoServer logging.
On startup, installs the gt2 CommonHandler on
org.geotools, org.geoserver and org.vfny.geoserver
(the root packages of GeoTools and GeoServer, respectively).
Now, this alone is not sufficient, and by a long stretch,
because it does not alter the logging levels.
In GeoServer 1.6 we have the log configured by a log4j file,
which may look like the following:
https://svn.codehaus.org/geoserver/trunk/geoserver/web/src/main/java/GEOSERVER_DEVELOPER_LOGGING.properties
The above file is usually held in the data dir, where the users
can modify it, and configures the log4j handlers and the log4j
logging levels. Please note how different packages receive
different logging levels.
Now, the problem is how to transport the per package logging level settings from that property file to the java loggers and... here
that's where we are screwed.
Now, first of all, why do we need to change the java logging level?
Performance. Theoretically one could have java logging log everything
and have log4j deal with the loggability of each message at the
handler level... too bad this would literally make GeoServer 4 times
slower (see the FOSS4G presentation "MapServer vs GeoServer" to
see how much logging may impact rendering performance).
The current code in GeoServer.configureGeoServerLogging tries
the following:
* parse the log4j file
* find out every java logger by calling LogManager.getLogManager().getLoggerNames()
* for each of them, state a new logging level (using the
logging level found in the log4j file for that package
or for the nearest containing package)
This is very considerate, in that the code will touch only
the loggers for which we know about in the log4j package,
leaving every other logger untouched -> it will impact
only the other web applications that use geotools and
geoserver packages.
Unfortunately it does not work. The problem is that
LogManager.getLogManager().getLoggerNames() will return
only the loggers that have been registered so far.
So if, for example, at that moment no rendering occured,
the "org.geotools.renderer" logger is not there, and its
logging level won't be configured.
When that logger gets created (first time StreamingRenderer
is referenced), LogManager will configure it using the
directives contained in the java logging property file,
which in my case say INFO.
Double checking is easy. Set the logging level to
GEOTOOLS_DEVELOPER_LOGGING (everything in org.geotools
should log to FINE level, more on this later*),
start geoserver, have something rendered.... the log
shows nothing (because the rendering package is still
configured at INFO level, see above).
Now go to the geoserver configuration, press apply,
this will force GeoServer.configureGeoServerLogging
to run once more, and this time it'll find "org.geotools.renderer"
and configure it to FINE level.
Do a WMS request again, boom, lots of logs in the output.
In order to have java logging use the log level we want
we would have to make up a java logging configuration file
that corresponds to the log4j file, stating the appropriate
log levels. Now, this has its own fair share of issues:
* we would have to make up a full log file, so we would have
to configure the handlers too. If an admin has configured
java loggin in any way we would be overriding its work,
unless we can access the java logging property file and
parse it so that we keep whatever is external to
geotools/geoserver in place. We may not be able to do so
thought, because our code is not as trusted as the one
in the jvm itself (think running in a security manager)
* calling LogManager.readConfiguration(inputStream) will
reconfigure loggin for _every_ other web application
running in the same container. That's a big no no for
every web app (you should not mess with other application
state)
To avoid this mess we should roll into GeoServer the kind
of java.util.LogManager replacement the Tomcat developers
came up with, JULI (see: http://tomcat.apache.org/tomcat-5.5-doc/logging.html).
The problem is, I think they manage to do so because
they are in control of the classloading, and we are not.
I also don't see any way to force a replacement of LogManager
using the java logging API, LogManager is a class, not an
interface...
I also would like to point out that in GeoServer 1.5.x
we were no better. We were usign only java logging, but
in order to configure the logging level from the UI
we were taking all java logging handlers and configure them
with a single flat level. It worked because it played with
handlers instead of loggers (they are all instantiated at
startup), but nevertheless it was not ok because again
it was a VM wide modification of the logging levels.
I really don't know how to address this. The short term
solution I see:
* go back to a flat setting of the logging level, instead
of per package (too bad, trying to diagnose an error
setting the log level of a specific package is very useful)
* build up a java logging property file on the fly and have
java logging ingest it
Both solutions are very suboptimal in my opinion.
We would not be in such a mess if GeoTools used commons logging.
Martin, are you sure there is no way to have commons logging
report the thread information that you need?
Cheers
Andrea