Re: [logback-dev] Better introspection into logging setup

JCL loggers don't have configuration - JCL, like SLF4J, is a facade, not an implementation. The jcl-to-slf4j bridge is just about redirecting calls to jcl to end up calling slf4j - there's no configuration involved. The JCL and SLF4J apis are similar, since both are inspired by log4j. I'm struggling to understand the problem you are trying to solve. That may be my failing of comprehension, but is it possible you have fundamentally misunderstood the relationship between slf4j, the slf4j bridges and logback? Apologies if you do understand this, but think of the bridges as a funnel to get all the log statements from all those libraries out there that log to different logging systems (log4j, juli, jcl) redirected into SLF4J. Then, once SLF4J is on the receiving end of all log statements, you put an implementation underneath (e.g. logback) which you can configure. You don't need to know what subproject uses what logging machinery because they all end up using logback via slf4j. ----- Original Message ----- What I didn't see right throught he first scan is where the configuration for the jcl loggers come from. Is it logback.xml, or is it whatever configuration is present for jcl?

JCL loggers don't have configuration - JCL, like SLF4J, is a facade, not an implementation.
Ah, I wasn't aware of that. (This does not change the matter, but I might be able to get better at avoiding misleading terminology.) I'm using logback as the logging backend, that's why I'm using logback and SLF4J interchangeably when it comes to configuration. Of course that assumption doesn't transfer to all other projects, and certainly not to SLF4J in general, so that's another source of confusion. Sorry for that. With "SLF4J configuration", I mean whatever configuration the logging backend uses. It is even "the SLF4J configuration" in a broader sense: you can query SLF4J about its properties. It's just a subset of these properties, namely the logging level of each individual logger, and you need to know the logger's name in advance, but it's a queryable configuration all right. Now that terminology is hopefully cleared up a bit, here's what I want to achieve: Right now, the JUL bridge docs tell me to install a Level.ALL JUL root handler and let SLF4J sort out which messages are actually needed. The docs rightfully warn that that can have a performance impact. Very rightfully so actually. So for each logger that's configured for SLF4J, I want to create a JUL logger with the same logging level as its SLF4J counterpart. This should drastically cut down on the number of log messages created and thrown away. I hope this is getting a bit clearer now :) Regards (and thanks for your patience), Jo

With "SLF4J configuration", I mean whatever configuration the logging backend uses. It is even "the SLF4J configuration" in a broader sense: you can query SLF4J about its properties. It's just a subset of these properties, namely the logging level of each individual logger, and you need to know the logger's name in advance, but it's a queryable configuration all right.
No it's not; the SLF4J Logger interface has no inherent concept of a level. It has "isXEnabled" methods, but there's nothing to stop you writing an implementation that returns true (or false) for all of those. You could even write an implementation that returns a logger with those methods randomly returning true or false each time you called it if you wanted to - SLF4J doesn't care. The fact that there happen to be different loggers for different names with different persistent configured levels in Logback is an implementation detail specific to Logback. It's going to be a lot, lot easier if we refer to Logback loggers configured for Logback, so if you don't mind I'll quote you as if you had:
So for each logger that's configured for Logback, I want to create a JUL logger with the same logging level as its Logback counterpart. This should drastically cut down on the number of log messages created and thrown away.
To get hold of the root logback logger you can use the following: import org.slf4j.LoggerFactory; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; ... LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); Logger rootLogger = lc.getLogger(Logger.ROOT_LOGGER_NAME); However, oddly you can't then ask the root logger what children it has - it's stored as a private variable (List<Logger> childrenList) with no public accessor. I don't know what the reason for this is (I'm also curious why it's a List - it looks like a Set to me, order irrelevant but no duplicates). I see no reason why it shouldn't have a public accessor exposing an unchangeable view of this collection to allow the introspection you ask for - you could then recursively walk the Logger tree. It would be trivial to branch logback in git and implement this. Another approach might be to look at the Joran configurator for logback and add a listener to it as each Logback logger is configured that creates the corresponding JULI logger (if that's the right JULI terminology). Obviously both of these solutions would be Logback specific, but that's unavoidable - there is no sensible way to add Logger introspection to SLF4J, it just doesn't fit the interface nature of SLF4J.

On 1 Mar 2010, at 18:55, Robert Elliot wrote:
However, oddly you can't then ask the root logger what children it has - it's stored as a private variable (List<Logger> childrenList) with no public accessor. I don't know what the reason for this is (I'm also curious why it's a List - it looks like a Set to me, order irrelevant but no duplicates). I see no reason why it shouldn't have a public accessor exposing an unchangeable view of this collection to allow the introspection you ask for - you could then recursively walk the Logger tree. It would be trivial to branch logback in git and implement this.
I've got my own reasons for wanting this functionality, so I've raised a JIRA here: http://jira.qos.ch/browse/LBCLASSIC-190 and created the logback fork that does the work here: http://github.com/Mahoney/logback
Another approach might be to look at the Joran configurator for logback and add a listener to it as each Logback logger is configured that creates the corresponding JULI logger (if that's the right JULI terminology).
It looks as if you can just add a status listener class of your own writing to your logback.xml: <configuration> <statusListener class="com.whatever.YourStatusListener" /> ... the rest of the configuration file </configuration> Joran will pass it messages as it configures each Logger - see the source of ch.qos.logback.classic.joran.action.LoggerAction for details.

No it's not; the SLF4J Logger interface has no inherent concept of a level. It has "isXEnabled" methods, but there's nothing to stop you writing an implementation that returns true (or false) for all of those. You could even write an implementation that returns a logger with those methods randomly returning true or false each time you called it if you wanted to - SLF4J doesn't care.
Agreed, the official interface associates no explicit level semantics. (In fact a logger that retrieves just a random 1% sample of all log messages might do exactly that.) Not that it changes anything about the point I'm making - since SLF4J does not make assumptions about inter-level dependencies, bindings and bridges are free to do whatever pleases them, including establishing a level relationship.
The fact that there happen to be different loggers for different names with different persistent configured levels in Logback is an implementation detail specific to Logback.
Routines that call LoggerFactory and Logger see * that there's a namespace that maps names to Loggers (ILoggerFactory#getLogger), and * that each Logger has a level (in the form of isXEnabled). Nothing of that is specific to Logback. (In fact it cannot be specific to Logback. It's the interface that SLF4J exposes to client code that may emit messages, and the whole point of SLF4J is abstracting away the specifics of logback or any other backend.)
It's going to be a lot, lot easier if we refer to Logback loggers configured for Logback, so if you don't mind I'll quote you as if you had:
So for each logger that's configured for Logback, I want to create a JUL logger with the same logging level as its Logback counterpart. This should drastically cut down on the number of log messages created and thrown away.
SLF4J can use other backends than logback, and the JUL bridge should work in these cases, too. So I have to strongly disagree: using logback specifics for the JUL bridge would defeat the whole purpose of SLF4J. One thing where I was overly specific was when I was referring to logback.xml. With a different backend, the configuration file would have a different name. So, it's really the configuration as far as it's exposed at the SLF4J level that needs to be reflected down to JUL. Regards, Jo

Am 02.03.2010 11:23, schrieb Durchholz, Joachim:
SLF4J can use other backends than logback, and the JUL bridge should work in these cases, too. So I have to strongly disagree: using logback specifics for the JUL bridge would defeat the whole purpose of SLF4J.
I tend to agree with Joachim. There already is code in "org.slf4j.spi" to allow logging bridges to better interfere with the logging backend (eg. LocationAwareLogger). It think it might be a good idea to have some other API which logging backends *may* implement to allow participation in the life-cycle of the logging backend, the created/destroyed/modified loggers. Something like this: /** * A <code>LoggerEvent</code> listener. <code>LoggerEvent</code> is a * listener interface that may be implemented by a logging bridge developer. When a * <code>LoggerEvent</code> is fired, it is synchronously delivered to a * <code>LoggerListener</code>. The Framework may deliver * <code>LoggerEvent</code> objects to a <code>LoggerListener</code> out * of order and may concurrently call and/or reenter a * <code>LoggerListener</code>. * <p> * A <code>LoggerListener</code> object is registered with the Framework * using the <code>LoggerFactory.addLoggerListener</code> method. * <code>LoggerListener</code> objects are called with a * <code>LoggerEvent </code> object when a logger is created, modified, or * is in the process of being destroyed. * ... */ interface LoggerListener extends java.util.EventListener { /** * Receives notification that a logger has had a lifecycle change. * * @param event The <code>LoggerEvent </code> object. */ public void loggerChanged(LoggerEvent event); } The LoggerEvent indicates the type of change (eg., "MODIFIED") and may contain additional information (eg. "property 'log.level'"). The LoggerFactory would simpy pass the listeners to some SPI API that may be implemented (optionally) by logging backends. BTW, I do not agree with the argument that the "SLF4J Logger interface has no inherent concept of a level." Frankly, it has. It is true that SLF4J does not define what logging backend implementors may do with these level. However, it clearly establishes the notion of a LEVEL for SLF4J users through its JavaDoc and the various methods available. This creates an API contract which SLF4J users /may/ rely on. It also establishes the concept of enabling and disabling of a LEVEL through this API. Thus, it's perfectly fine to allow passing modifications of the enablement state of log levels through the LoggerEvent object. Implementations not dealing with levels can simply avoid send such additional information (which could mean NO LEVELS changed). Having this API as part of SLF4J would allow to have a JUL bridge which is agnostic of the actual logging backing. But a valid questions remains, if such API should be added to SLF4J just because of the JUL bridge. -Gunnar -- Gunnar Wagenknecht gunnar@wagenknecht.org http://wagenknecht.org/

On 02/03/2010 12:12 PM, Gunnar Wagenknecht wrote:
Am 02.03.2010 11:23, schrieb Durchholz, Joachim:
SLF4J can use other backends than logback, and the JUL bridge should work in these cases, too. So I have to strongly disagree: using logback specifics for the JUL bridge would defeat the whole purpose of SLF4J.
I tend to agree with Joachim. There already is code in "org.slf4j.spi" to allow logging bridges to better interfere with the logging backend (eg. LocationAwareLogger). It think it might be a good idea to have some other API which logging backends *may* implement to allow participation in the life-cycle of the logging backend, the created/destroyed/modified loggers.
Well, since neither j.u.l. nor log4j frameworks have any knowledge of SLF4J (which is quite unfortunate in the case of log4j), including such an interface would have no impact as jul nor log4j would implement it.
Something like this:
/** * A<code>LoggerEvent</code> listener.<code>LoggerEvent</code> is a * listener interface that may be implemented by a logging bridge developer. When a *<code>LoggerEvent</code> is fired, it is synchronously delivered to a *<code>LoggerListener</code>. The Framework may deliver *<code>LoggerEvent</code> objects to a<code>LoggerListener</code> out * of order and may concurrently call and/or reenter a *<code>LoggerListener</code>. *<p> * A<code>LoggerListener</code> object is registered with the Framework * using the<code>LoggerFactory.addLoggerListener</code> method. *<code>LoggerListener</code> objects are called with a *<code>LoggerEvent</code> object when a logger is created, modified, or * is in the process of being destroyed. * ... */ interface LoggerListener extends java.util.EventListener { /** * Receives notification that a logger has had a lifecycle change. * * @param event The<code>LoggerEvent</code> object. */ public void loggerChanged(LoggerEvent event); }
The LoggerEvent indicates the type of change (eg., "MODIFIED") and may contain additional information (eg. "property 'log.level'"). The LoggerFactory would simpy pass the listeners to some SPI API that may be implemented (optionally) by logging backends.
Given my past experience it's just not going to happen any time soon. It would be nice if log4j or jul were SLF4J-aware or took the SLF4J API into consideration but they don't.
BTW, I do not agree with the argument that the "SLF4J Logger interface has no inherent concept of a level." Frankly, it has. It is true that SLF4J does not define what logging backend implementors may do with these level. However, it clearly establishes the notion of a LEVEL for SLF4J users through its JavaDoc and the various methods available. This creates an API contract which SLF4J users /may/ rely on. It also establishes the concept of enabling and disabling of a LEVEL through this API. Thus, it's perfectly fine to allow passing modifications of the enablement state of log levels through the LoggerEvent object. Implementations not dealing with levels can simply avoid send such additional information (which could mean NO LEVELS changed).
Although you have a point about SLF4J loggers having a "notion" of levels, there is no SLF4J API to set or get levels of the logging backend. The SLF4J allows for checking whether a given logger is enabled for some level, but you can't actually get the level of a logger nor can you set it.
Having this API as part of SLF4J would allow to have a JUL bridge which is agnostic of the actual logging backing. But a valid questions remains, if such API should be added to SLF4J just because of the JUL bridge.
I think this problem can not be dealt with abstractly for all backends. However, if one assumed logback as the backend, some jul-bridge related code could listen to changes in logback and mirror them in jul. Such a listener could be enabled as part of logback configuration. Nevertheless, the home for such code would be in logback-classic and not SLF4J.
-Gunnar

Although you have a point about SLF4J loggers having a "notion" of levels, there is no SLF4J API to set or get levels of the logging backend. The SLF4J allows for checking whether a given logger is enabled for some level, but you can't actually get the level of a logger nor can you set it.
The ability to check the level is enough to get the logger level. Just try the isXxxEnabled functions. (It's not efficient and ugly that way, but it would be good enough if done just once per JUL logger.)
I think this problem can not be dealt with abstractly for all backends.
I'm not sure about that. JUL needs a list of logger names and associated levels. That's the same information that SLF4J uses to decide what to return for LoggerFactory#getLogger(String) and Logger#isXxxEnabled. Ah. It's actually not SLF4J that uses this information, it's the backend. So the real question is whether all backends provide a list of Loggers or not. Checking the list of available SLF4J backend: - logback-classic: ok - log4j: ? - java.util.logging: yes but irrelevant for the JUL bridge :) - simple: can fake a logger list - nop: can fake a logger list - jcl: ? So, do log4j and jcl return a list of all active Loggers, or is there some other API that allows inferring the list? (The next question would be whether all backends provide hooks for listening to changes in the list of Loggers.) Regards, Jo

Having this API as part of SLF4J would allow to have a JUL bridge which is agnostic of the actual logging backing. But a valid questions remains, if such API should be added to SLF4J just because of the JUL bridge.
*Something* should be done considering that JUL is going to be around for at least another decade, and that JUL kills performance if it's configured with the root logger at Level.ALL. I see two approaches: 1) Improve the JUL bridge so that it installs only loggers where the SLF4J configuration wants one. This requires minimal access to the backend's configuration. The question is what to do. Currently, I see two variants: 1) Improve the JUL bridge. 2) Improve the JUL bridge docs and tell people how to configure JUL so that the JUL bridge performs well. (Variant 2 seems doable to me. I think JUL bridge performance can be returned to acceptable levels by - configuring those third-party libs that use JUL via the JUL facilities and - configuring the SLF4J backend at Level.ALL for the logger namespace(s) of said libs. I wouldn't like to keep logging configuration in two separate files, and I haven't tried it yet, but it could be done... the question is: what approach would fit better with SLF4J? I can't answer that last question.)

I suggest that you file a jira issue against logback-classic so that whenever the level of a logback-classic logger changes this is mirrored in jul. Packaging such functionality into SLF4J would be out of scope. On 02/03/2010 2:00 PM, Durchholz, Joachim wrote:
Having this API as part of SLF4J would allow to have a JUL bridge which is agnostic of the actual logging backing. But a valid questions remains, if such API should be added to SLF4J just because of the JUL bridge.
*Something* should be done considering that JUL is going to be around for at least another decade, and that JUL kills performance if it's configured with the root logger at Level.ALL.
I see two approaches: 1) Improve the JUL bridge so that it installs only loggers where the SLF4J configuration wants one. This requires minimal access to the backend's configuration.
The question is what to do. Currently, I see two variants: 1) Improve the JUL bridge. 2) Improve the JUL bridge docs and tell people how to configure JUL so that the JUL bridge performs well.
(Variant 2 seems doable to me. I think JUL bridge performance can be returned to acceptable levels by - configuring those third-party libs that use JUL via the JUL facilities and - configuring the SLF4J backend at Level.ALL for the logger namespace(s) of said libs. I wouldn't like to keep logging configuration in two separate files, and I haven't tried it yet, but it could be done... the question is: what approach would fit better with SLF4J? I can't answer that last question.)
participants (4)
-
Ceki Gülcü
-
Durchholz, Joachim
-
Gunnar Wagenknecht
-
Robert Elliot