[Bug 163] New: Copy & paste of LoggerFactory.getLogger

http://bugzilla.slf4j.org/show_bug.cgi?id=163 Summary: Copy & paste of LoggerFactory.getLogger Product: SLF4J Version: unspecified Platform: All OS/Version: All Status: NEW Severity: normal Priority: P2 Component: Core API AssignedTo: slf4j-dev@qos.ch ReportedBy: thomas.tom.mueller@gmail.com This is a feature request. Currently, when I create a new logger in a class, I copy & paste the following line from another class: private static Logger log = LoggerFactory.getLogger(AcmeImpl.class); Sometimes I forget to change the class name. SLF4J could help me here. I suggest to add a new method, so the class name is not required, so that I can copy & paste the line without having to remember that I have to change the class name. Also, it's a bit shorter: private static Logger log = LoggerFactory.getCallerClassLogger(); This is copy & paste friendly. The implementation is tricky, but trivial once you understand it: getCallerClassLogger() { return getLogger(new Error().getStackTrace()[1].getClassName()); } There is a project that tries to solve the copy & paste problem: http://code.google.com/p/morbok/ -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 Joern Huxhorn <joern@huxhorn.de> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |joern@huxhorn.de --- Comment #1 from Joern Huxhorn <joern@huxhorn.de> 2009-12-10 16:53:45 --- Good idea! -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #2 from Ceki Gulcu <listid@qos.ch> 2009-12-10 17:16:19 --- Thank you for sharing this idea which unfortunately suffers from a fatal flaw. Some virtual machines may, under some circumstances, omit one or more stack frames from the stack trace. In the extreme case, a virtual machine that has no stack trace information concerning this throwable is permitted to return a zero-length array from this method. Generally speaking, the array returned by this method will contain one element for every frame that would be printed by printStackTrace. source: http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Throwable.html#getStackTra... -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #3 from Thomas Mueller <thomas.tom.mueller@gmail.com> 2009-12-10 19:26:24 --- I didn't know about that... I agree this shouldn't be used (except maybe for testing). I wonder when in practice this would fail, do you know a case where the stack trace is wrong? Just for completeness, I guess this would always work, but only on a Sun JVM: sun.reflect.Reflection.getCallerClass(). I don't suggest to use it however. Another idea. What about: 1) LoggerFactory.getEnclosingLogger(new Object(){}); which would be a shortcut for: 2) LoggerFactory.getLogger(new Object(){}.getClass().getEnclosingClass()); This is copy & paste safe. It will generate an anonymous inner class, create one instance, and get the enclosing class. Instead of implementing 1) you could just document 2) somewhere. Disadvantages: - One additional class per logger (about 500 bytes; not sure how much if compressed). - Hard to understand and therefore possible to use incorrectly (users might forget the {} and then complain it doesn't work) - so better throw a nice error message if you implement getEnclosingLogger(Object obj). -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 Thorbjørn Ravn Andersen <thorbjoern@gmail.com> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |thorbjoern@gmail.com --- Comment #4 from Thorbjørn Ravn Andersen <thorbjoern@gmail.com> 2009-12-10 20:55:44 --- I usually use just the non-static form, where you can use this.getClass() which is easily cut-and-pasteable. I also had a Eclipse template for new classes containing a correct logger, but that didn't survive a reinstall. Since the convention is so strong about the class name being the logger name, I would find it easier to just supply an object instead of a claass and have that used instead. The LoggerFactory.getLogger(Foo.class) assignment often gets too long to fit on a single line so it is broken when formatting. I would like a less verbose incarnation form. -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #5 from Joern Huxhorn <joern@huxhorn.de> 2009-12-11 08:10:07 --- Well, that's really unfortunate. I didn't remember the optionality of the stacktrace myself... Good catch, Ceki. Concerning getClass() vs TheClass.class: This has an entirely different behavior in case of inheritance. class A { void foo() { logger.debug("foo"); } } class B extends A { void bar() { foo(); logger.debug("bar"); } } If class A and class B would obtain their logger using getLogger(A.class) and getLogger(B.class) then they would log like this when bar() is called: DEBUG - A - foo DEBUG - B - bar If they used getLogger(getClass()) instead, the log events would look like this: DEBUG - B - foo DEBUG - B - bar Further, configuring the level of the A logger would only have an effect on instances of A, not on instances of B. Calling a.foo() behave differently than calling b.foo(). I'd expect a message like DEBUG - B - foo to originate from class B and would be confused to find the actual call in class A. I've also created myself an IDEA LiveTemplate for the creation of Logger instances ;) -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #6 from Ceki Gulcu <listid@qos.ch> 2009-12-11 08:49:44 --- Hi Joern, I couldn't quite follow your reasoning but it's early in the morning and I am badly in need of coffee... --- file A.java import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class A { void foo() { Logger logger = LoggerFactory.getLogger(this.getClass()); logger.info("foo"); } } -- file B.java import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class B extends A { void bar() { Logger logger = LoggerFactory.getLogger(this.getClass()); logger.info("bar"); } } --- file Main.java public class Main { public static void main(String[] args) { A a = new A(); a.foo(); B b = new B(); b.foo(); b.bar(); } } Calling java Main, yields 2009-12-11 08:44:22,145 INFO [main] [] A - foo 2009-12-11 08:44:22,160 INFO [main] [] B - foo 2009-12-11 08:44:22,160 INFO [main] [] B - bar which looks pretty OK to me. Maybe you meant something else? -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #7 from Joern Huxhorn <joern@huxhorn.de> 2009-12-11 10:22:59 --- --- file A.java import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class A { void foo() { Logger logger = LoggerFactory.getLogger(A.class); logger.info("foo"); } } -- file B.java import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class B extends A { void bar() { Logger logger = LoggerFactory.getLogger(B.class); logger.info("bar"); } } would print 2009-12-11 08:44:22,145 INFO [main] [] A - foo 2009-12-11 08:44:22,160 INFO [main] [] A - foo 2009-12-11 08:44:22,160 INFO [main] [] B - bar instead. This is, in my opinion, better style as the code that prints "foo" is actually located in A, not B. Neither technique is wrong I just think that the result of using XYZ.class is more intuitive. Wow, you nearly had me there, I'm also in need of coffee! -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #8 from Thomas Mueller <thomas.tom.mueller@gmail.com> 2009-12-15 09:29:24 --- If you want to close this bug because there is no 'perfect' solution, then that's OK with me. Here a small summary of the 'anonymous inner class' workaround. Maybe integrating something like http://code.google.com/p/morbok/ would be better. A) LoggerFactory.getLogger(new Object(){}.getClass().getEnclosingClass()); Advantage: - You can copy & paste it to new classes Disadvantages: - Looks a bit weird - Adds 500 bytes for each logger B) LoggerFactory.getEnclosingLogger(new Object(){}); Advantage: - You can copy & paste it to new classes Disadvantages: - Still looks weird, but less so (shorter than A) - Adds 500 bytes for each logger - Requires a new method in LoggerFactory C) interface L{}; LoggerFactory.getLogger(new L(){}.getClass().getEnclosingClass()); Advantage: - You can copy & paste it to new classes Disadvantages: - Looks even more weird than A - Pollutes the namespace with 'L'. - Adds about 240 bytes for each logger -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #9 from Thorsten <thorsten.Moeller@unibas.ch> 2009-12-15 10:58:37 --- Created an attachment (id=67) --> (http://bugzilla.slf4j.org/attachment.cgi?id=67) SLF4J logger declaration Eclipse template -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #10 from Thorsten <thorsten.Moeller@unibas.ch> 2009-12-15 10:59:01 --- I'd like to comment on this request. I'm totally against such an extension for one simple reason. In my opinion there is a very simple and clean solution to the copy-and-paste based and error prone approach to declaring logger instances. Every modern IDE (and also editors such as emacs or vi) provide (dynamic) code templates. For instance, I use Eclipse and one of my oldest templates that I've added long time ago is this one (see also the attached screenshot): ${:import(org.slf4j.Logger, org.slf4j.LoggerFactory)}${argType} private static final Logger logger = LoggerFactory.getLogger(${enclosing_type}.class); Using this template one can declare a logger context sensitively in one fell swoop! Rather than introducing probably leaky extensions to the code one should make use of functions provided by modern tools. If you agree then I suggest to add a short documentation to the Web site so that (new) users can easily extend there Eclipse/IDE. -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #11 from Thomas Mueller <thomas.tom.mueller@gmail.com> 2009-12-15 11:20:34 ---
add a short documentation
Yes, that would be great! It's a good solution, but also not perfect. What I don't like about it is: I think libraries shouldn't rely on IDE features. Ideally, the library should provide a solution for the libraries problems, not the IDE. So, an Eclipse template also is a workaround (not the worst one however).
probably leaky extensions
Sorry, what do you mean with leaky extension? Leaky as in leaks memory? It doesn't leak memory. The 500/230 bytes are purely jar file size, not heap memory. -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #12 from Thorsten <thorsten.Moeller@unibas.ch> 2009-12-15 15:23:12 --- (In reply to comment #11)
add a short documentation
Yes, that would be great! It's a good solution, but also not perfect. What I don't like about it is: I think libraries shouldn't rely on IDE features. Well, my proposal does not make SLF4J rely nor depend on features of a particular IDE. It is just a proposal showing an example of how one can take advantage of functionality provided by modern tools. In other words, not every problem that developer might face when using some library can or should be tried to solve by making source code changes if there is tooling support that can nicely solve a particular problem. To say it in yet even other words, the problem that you face is not a matter of functionality missing in SLF4J but a matter of "how" one works (and maybe it is necessary to rethink the latter).
Ideally, the library should provide a solution for the libraries problems, not the IDE. So, an Eclipse template also is a workaround (not the worst one however). As I tried to convey, it is not a workaround but an example to make people aware and to guide them towards a direction on what they should do to prevent falling into error-prone programming patterns.
probably leaky extensions
Sorry, what do you mean with leaky extension? Leaky as in leaks memory? It doesn't leak memory. The 500/230 bytes are purely jar file size, not heap memory. You are right, was not the best wording as it can be easily misunderstood. I should have probably used "tricky".
-- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #13 from Thomas Mueller <thomas.tom.mueller@gmail.com> 2009-12-15 16:14:22 ---
my proposal does not make SLF4J rely nor depend on features of a particular IDE.
Well, it does. It requires to use a template to solve the usability problem.
if there is tooling support
As I already wrote, I prefer to not rely on this tooling support. I don't want to be forced to use a specific feature of the IDE (templates). I want to use the most logical solution to the problem (copy & paste).
rethink "how" one works
Do you tell me that developers should not use copy & paste? I'm sure most developers use copy & paste as well. They don't bother creating a template. And even if you document it nicely, they wont. That doesn't mean it shouldn't be documented.
error-prone programming patterns
Copy & paste is 'usually' not error prone. It is only error prone because copy & paste doesn't do what I expect - in this case - and I don't get an error message or warning. And that's the problem. -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #14 from Thorbjørn Ravn Andersen <thorbjoern@gmail.com> 2009-12-15 17:12:16 --- The advantage of explicitly stating the class to be used as the logger name is the robustness of the code. Anything that magically pulls out a piece of information from the JVM without explicitly being guaranteed by Sun to be available *everywhere* is prone to break whenever it is the most inconvenient. Hence, I advocate for the current explicit glue (which is templateable). I'd like a shorter way of writing it though, as it has problems staying on a single source line. -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #15 from Thomas Mueller <thomas.tom.mueller@gmail.com> 2009-12-15 17:42:17 --- This _is_ guaranteed to work: LoggerFactory.getEnclosingLogger(new Object(){}); -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #16 from Ceki Gulcu <listid@qos.ch> 2009-12-15 17:54:06 --- (In reply to comment #15)
This _is_ guaranteed to work: LoggerFactory.getEnclosingLogger(new Object(){});
It is not guaranteed which is the whole point here. See http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Throwable.html#getStackTra... -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #17 from Thomas Mueller <thomas.tom.mueller@gmail.com> 2009-12-15 17:55:47 --- getEnclosingLogger(new Object(){}) doesn't use getStackTrace. Please read the comments. -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #18 from Thomas Mueller <thomas.tom.mueller@gmail.com> 2009-12-15 17:57:13 --- Speciall comment #3: http://bugzilla.slf4j.org/show_bug.cgi?id=163#c3 -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #19 from Ceki Gulcu <listid@qos.ch> 2009-12-15 18:18:50 --- (In reply to comment #18)
Speciall comment #3: http://bugzilla.slf4j.org/show_bug.cgi?id=163#c3
OK, my bad. IMO, the LoggerFactory.getEnclosingLogger(new Object(){}) approach while clever, is too much of a hack to be publicly endorsed. -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #20 from Joern Huxhorn <joern@huxhorn.de> 2009-12-15 18:29:04 --- I agree with Ceki. The getEnclosingLogger(..) syntax is quite strange. You could always use LoggerFactory.getLogger(new Object(){}.getClass().getEnclosingClass()); as you said yourself. Changing the topic a bit: Does anyone know enough about annotations to be able to tell me if something like @Logger private final Logger logger; would be able to initialize logger appropriately? I've never written an annotation myself so I don't know. -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #21 from Thorbjørn Ravn Andersen <thorbjoern@gmail.com> 2009-12-15 18:54:49 --- (In reply to comment #20)
I agree with Ceki. The getEnclosingLogger(..) syntax is quite strange.
You could always use LoggerFactory.getLogger(new Object(){}.getClass().getEnclosingClass()); as you said yourself.
Which is the finest example of cut-and-paste template I've seen for long :) I do not fancy the anonymous class trick. I like the class name better, and some day I'll make Eclipse do it for me. Right now I'm happily typing it every time.
Changing the topic a bit: Does anyone know enough about annotations to be able to tell me if something like
@Logger private final Logger logger;
would be able to initialize logger appropriately? I've never written an annotation myself so I don't know.
I looked into this a while back. Basically annotations are metadata, so you need SOMETHING that will do something with it for this to happen. A normal classloader will NOT. I am at the moment trying to use Google Guice - which provides dependency injection - and it has built in support for JUL. http://code.google.com/p/google-guice/wiki/BuiltInBindings -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #22 from Thorsten <thorsten.Moeller@unibas.ch> 2009-12-15 19:04:08 --- (In reply to comment #13)
Do you tell me that developers should not use copy & paste? No. I'm saying: use the template approach for declaring a logger instance: convenient, fast (faster than c&p), safe.
-- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #23 from Thomas Mueller <thomas.tom.mueller@gmail.com> 2009-12-15 20:30:42 ---
@Logger private final Logger logger; would be able to initialize logger appropriately?
Yes. In Java 6 that's possible. You don't even need the second line: http://code.google.com/p/morbok/ (I already posted that link in the description). You might want to integrate something like Morbok in SLF4J. This is not a hack at all by the way. A similar technique is used in the JPA 2.0 API: http://blogs.sun.com/ldemichiel/entry/java_persistence_2_0_proposed (Metamodel API)
Basically annotations are metadata, so you need SOMETHING that will do something with it for this to happen. A normal classloader will NOT.
No, a classloader will not, but the annotation processor will. This is integrated in Java 6: http://www.javabeat.net/articles/14-java-60-features-part-2-pluggable-annota...
LoggerFactory.getEnclosingLogger(new Object(){}) too much of a hack to be publicly endorsed
I of agree. Specially with the 500 byte overhead. -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #24 from Joern Huxhorn <joern@huxhorn.de> 2009-12-15 20:56:52 --- Just for the record: I would *never* use LoggerFactory.getLogger(new Object(){}.getClass().getEnclosingClass()); myself. I just said it *can* be used as stated in comment #3. ;) Instead, I'd *always* use LoggerFactory.getLogger(TheClass.class); as I'm doing right now. IDEA is filling in everything for me. I have a LiveTemplate called "plog" that creates private final Logger logger = ... and another one called "flog" that creates final Logger logger = ... to be used in static methods. -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #25 from Joern Huxhorn <joern@huxhorn.de> 2009-12-15 21:07:14 --- In reply to comment #23: Now *this* looks promising! A feature like this would be a killer if it's simple enough for the end-user... even if it's Java6-only. One thing, though: The Logger created in the example is static which I think it shouldn't be, right? I can't remember why... I thought it was because of garbage-collection, but.... -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 Ralph Goers <rgoers@apache.org> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |rgoers@apache.org --- Comment #26 from Ralph Goers <rgoers@apache.org> 2009-12-16 04:25:32 --- Actually, the way I would like to do this would be to use something like what Seam already supports: @Logger private Logger log; http://www.tzavellas.com/techblog/2007/03/31/implementing-seam-style-logger-... shows one way of doing this. http://forums.sun.com/thread.jspa?threadID=5202309 describes this also and Google will lead to other samples. I would also like: @XLogger private XLogger log; public class MyClass { @Trace Object doSomething(String arg) { ... return object; } } In this case doSomething would become: Object doSomething(String arg) { log.entry(arg); ... log.exit(object); return object; } or even Object doSomething(String arg) { log.entry(arg); try { ... log.exit(object); return object; } catch (Exception ex) { log.throwing(LocationAwareLogger.TRACE_INT, ex); throw ex; } } -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #27 from Joern Huxhorn <joern@huxhorn.de> 2010-03-30 12:46:46 --- I've implemented something related to suggestions in this ticket: http://github.com/huxi/slf4j/blob/slf4j-redesign/slf4j-n-api/src/main/java/o... Please take a look at the documentation of the class. I'm wondering if this would be interesting despite the shortcomings. http://github.com/huxi/slf4j/blob/slf4j-redesign/slf4j-n-api/src/test/java/o... contains some example usage, i.e. static import of the logging method and simply calling debug("Message: {}","Foo"); It *would* be extremely nice from the usage point-of-view... but it contains a certain risk (as documented) as well as an unknown performance impact. What do you think? -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #28 from Ralph Goers <rgoers@apache.org> 2010-03-30 15:42:37 --- I suggest you run some performance tests. I am certain you are going to find that performance is very bad when logging is disabled as creating a Throwable on every call before doing any filtering is astronomically more expensive than just using a static string. Again, I would suggest using a solution based on annotations. I know someone has already done it. -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #29 from Joern Huxhorn <joern@huxhorn.de> 2010-03-30 17:06:57 --- (In reply to comment #28)
I suggest you run some performance tests. I am certain you are going to find that performance is very bad when logging is disabled as creating a Throwable on every call before doing any filtering is astronomically more expensive than just using a static string.
Again, I would suggest using a solution based on annotations. I know someone has already done it.
I also like the suggested annotation solution best since it does not have any downside I'm aware of. Unfortunately, I don't really know how to implement anything like it. It's pretty hard to benchmark code like the one in comment #27 without walking straight into some micro-benchmark trap. Any suggestions? I'm not sure if something like SimpleLogging above should really be included in SLF4J (or some extension jar) at all, I was merely playing around and wanted to share it. (most importantly, this shouldn't be considered a part of my SLF4J 2.0 proposal;)) However, I've just added another method, getCallingLogger(), that's returning either the logger of the calling class or a NOPLogger if such a logger could not be resolved (in case of Throwable.getStackTrace() returning an empty STE[]). This wouldn't have the likely big performance impact like the rest of the methods since it would only be called once while initializing the Logger. The code is again contained in http://github.com/huxi/slf4j/blob/slf4j-redesign/slf4j-n-api/src/main/java/o... and http://github.com/huxi/slf4j/blob/slf4j-redesign/slf4j-n-api/src/test/java/o... That method isn't really bad, I think. -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #30 from Ceki Gulcu <lists@slf4j.org> 2010-03-30 17:53:20 --- Hi Joern, I'd speculate that the SimpleLogging variant would be at least a 1'000'000 times slower than invoking any existing slf4j logger. I'd be very surprised by anything less than 100'000 but I like surprises. To get somewhat "reliable" micro-benching results, I'd use System.nanoTime before entering and after existing the loop, and run the loop 3 times (to let the HotSpot compiler warm up) using the 3rd result. The loop length should be inversely proportional to the speed of the code inside the loop (very quick code => long loop length, slow code => short loop length). HTH -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 Ceki Gulcu <lists@slf4j.org> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |lists@slf4j.org --- Comment #31 from Ceki Gulcu <lists@slf4j.org> 2010-03-31 10:12:16 --- There are at least two significant overhead costs in SimpleLogging. First, is the cost of looking up a logger and the second is computing the name of the logger via a stack trace examination. I mistakenly compounded the cost by multiplying them together instead of adding them. Hence the erroneous figure of 1'000'000. A more reasonable estimation is between 1'000 to 10'000 fold increase in the computational cost. 1 million fold increase was an exaggeration. Sorry about that. -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #32 from Joern Huxhorn <joern@huxhorn.de> 2010-03-31 10:30:09 --- Factor 1000-10.000 sounds like a very realistic guess. I was just playing around a bit, mourning the simplicity and beauty of the resulting method call. But that doesn't help at all as a solution like this, simply to get some syntactic sugar, isn't justifiable. The getCallingLogger-method might be interesting, though, with the known deficiency that it might return a NOPLogger if getStackTrace returns an empty STE[]. I'd be highly interested in the annotation solution. Is there any code available that's already implementing it? -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #33 from Ceki Gulcu <lists@slf4j.org> 2010-03-31 10:41:29 --- As far as I know, dependency injection frameworks such as guice, spring and weld already handle annotation based logger injection. I am sure there are others. It seems more reasonable to let them deal with logger injection via annotations. Any approach SLF4J comes up with using the java language will entail either byte code decoration or interfering with the lifecycle of objects. Both approaches are too heavy handed imo. In scala in contrast, this problem could be handled with a logging trait in a single line of code as is done in scalate. -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #34 from Thorbjørn Ravn Andersen <thorbjoern@gmail.com> 2010-03-31 10:44:09 --- (In reply to comment #32)
I'd be highly interested in the annotation solution. Is there any code available that's already implementing it?
I will do some annotation work in the time to come as we will use JSR-330 Dependency Injection to allow customer specific versions of a standard, unmodified code base. Also in JEE6 this is always available, and hence desirable. I'll report my findings back. -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #35 from Ceki Gulcu <lists@slf4j.org> 2010-03-31 11:22:31 --- Michael Glauche has come up with a solution using Guice[1]. Given that Guice has or will have support for JSR 330, we could perhaps propose a "standardized" solution based on JSR 330. Michael Glauche's approach looks like it is heavily based on Guice (TypeListener is a Guice-only class). I don't see how JSR 330 would be of any help but I am no expert. Let's wait for Thorbjørn to come up with a proposal. (An slf4j fork on github would be nice). [1] http://glauche.de/2009/08/24/ [2] http://code.google.com/p/google-guice/wiki/JSR330 -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #36 from Thorbjørn Ravn Andersen <thorbjoern@gmail.com> 2010-04-01 13:37:08 --- (In reply to comment #35)
Michael Glauche has come up with a solution using Guice[1]. Given that Guice has or will have support for JSR 330, we could perhaps propose a "standardized" solution based on JSR 330.
I've now had an initial look on the Weld RI (lots and lots of magic...sigh) You might find the Weld Logger extension interesting: import org.slf4j.Logger; import javax.inject.Inject; public class Checkout { private @Inject Logger log; public void invoiceItems() { ShoppingCart cart; ... log.debug("Items invoiced for {}", cart); } } http://docs.jboss.org/weld/reference/latest/en-US/html/extensions.html#d0e57... -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #37 from Thorbjørn Ravn Andersen <thorbjoern@gmail.com> 2010-04-02 13:46:07 --- (In reply to comment #36)
private @Inject Logger log;
I finally got this to work (the instructions are incorrect - it is the weld-extensions that provides this). The default is to use the name of the current class. This can be overridden with
private @Inject @Category("foo") Logger log;
The LoggerFactory class and the Category class are at http://anonsvn.jboss.org/repos/weld/extensions/trunk/src/main/java/org/jboss... and are about 10 lines of actual code each, and under the Apache license. -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #38 from Thorbjørn Ravn Andersen <thorbjoern@gmail.com> 2011-03-20 19:53:24 CET --- (In reply to comment #35)
Michael Glauche has come up with a solution using Guice[1]. Given that Guice has or will have support for JSR 330, we could perhaps propose a "standardized" solution based on JSR 330.
I have now spent quite some time making JSR-330 work as we want it to with the current main implementations Spring, Guice and Weld as possible backends. An interesting exercise. The conclusion was that a standard way for the user to use a SLF4J Logger with JSR-330 _is_ possible, but it must contain JSR-330 implementation specific code. The reason for this is that JSR-330 specifies a well-supported "Provider" (think Factory) concept but the API is very limited and does e.g. not provide any information about _where_ the provided object is to be used which is needed here. In order to get that information the actual backend needs to be asked, and this is not standardized. Also _configuration_ is outside the JSR-330 scope, so it is also backend specific, but not that difficult. Basically there are two approaches as I see it now, one for using @Inject, and the other for a custom @SLF4JLogger annotation without @Inject. The implementations are vastly different but the syntax is quite close: 1) @Inject Logger log; 2) @SLF4JLogger Logger log; so this is a matter of taste. Personally I think the @SLF4JLogger will be the most robust as it is separate from other @Injects. The Guice folks made a very important recommendation, namely that they recommend a public setter method for all injectable fields so that the _setter_ is invoked setting the field, instead of just putting the value in the field. The reason for this is that it allows you to unit test your code as plain classes without a JSR-330 environment active - remember it is just meta-data. I think this is both true and so important that this might be a good reason to reconsider the age old "private static Logger log = LoggerFactory.getLogger(....)" paradigm. If so, the new paradigm will be Logger log; @Inject public void setLogger(@SLF4JLogger Logger log) { this.log = log; } This might be a very good idea for a "SLF4JLogging" (SLF4JLoggable?) interface. -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #39 from Thorbjørn Ravn Andersen <thorbjoern@gmail.com> 2011-03-20 20:22:56 CET --- (In reply to comment #38)
I have now spent quite some time making JSR-330 work as we want it to with the current main implementations Spring, Guice and Weld as possible backends. An interesting exercise.
For clarification, the work I've done has been unrelated to slf4j, and I have not done any actual work on a SLF4JLogger provider. -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.

http://bugzilla.slf4j.org/show_bug.cgi?id=163 --- Comment #40 from Thomas Mueller <thomas.tom.mueller@gmail.com> 2011-07-19 09:22:48 CEST --- Inspired by http://beust.com/weblog/2011/07/15/accessing-the-class-object-in-a-static-co... - a new proposal: public class Test { private static Logger logger = LoggerFactory.getCallerLogger(); public static void main(String... args) { logger.log("Hello"); } } public class LoggerFactory { public static Logger getCallerLogger() { return new Logger() {{ name = new SecurityManager() { public Class<?> getClass(int level) { return getClassContext()[level]; } }.getClass(3).getName(); }}; } } public class Logger { String name; public void log(String s) { System.out.println(name + ": " + s); } } -- Configure bugmail: http://bugzilla.slf4j.org/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug.
participants (1)
-
bugzilla-daemon@pixie.qos.ch