13

EDIT: I would love to read reactions to Steve Reed's AOP approach. Comments to his answer are encouraged!

I'm a novice, and at some point I realized it would be helpful to know the contents of a variable during program execution. So I started doing this:

EDIT: fixed this. Used to be: var + ": " + var, which was totally wrong. Dumb typo.

System.err.println ( "var: " + var );

Later I learned that this was common practice. At least, where a debugger was unavailable or unwanted.

I use a basic text editor, and typing the print statement every time I need to debug a variable is pretty tiresome, so I thought, why not something like this:

void dbug ( Object obj )
{
    String variableName = obj.somehowGetVariableName();
    String variableContents = obj.toString();
    System.out.println ( variableName +": " + variableContents );
}

But apparently getting the variable name is easier said than done.

java-reflection-how-to-get-the-name-of-a-variable

Am I stuck with:

System.err.println ( "var: " + var );

Or is there a popular shorthand version of this?

Community
  • 1
  • 1
Lasoldo Solsifa
  • 375
  • 3
  • 5
  • 14
  • Just for curiosity: "At least, where a debugger was unavailable or unwanted." In which situation would this be?? And what's the reason? – Juri Aug 06 '09 at 14:29
  • unavailable I understand, but unwanted ?!? that's just refusing to learn how powerful a real debugging tool is vs. print statements. – basszero Aug 06 '09 at 14:33
  • Unavailable would be when you haven't got yourself an IDE yet, and unwanted would be when you don't want to use an IDE. The consensus appears to be that an IDE is the only way to go. – Lasoldo Solsifa Aug 06 '09 at 14:34
  • @Juri - He uses a "basic text editor" for his code, so presumably he doesn't want an IDE or debugger. Perhaps he uses a really slow computer? – jsight Aug 06 '09 at 14:37
  • To clarify -- in my personal case, an IDE is neither unavailable nor unwanted. It's just that I try to use the least amount of technology, the simplest techniques. And my programming projects are small. So I've stuck with a basic text editor for as long as I felt I could. I've tried Eclipse, and it looks enormously useful, even for a small-time player like me. – Lasoldo Solsifa Aug 06 '09 at 14:55
  • I seem to remember the Pragmatic Programmers claim that println()s (or equiv in whatever language) are not to be sniffed at. There's a lot to be said for IDEs, but when you have multiple threads or processing lots of data, or just need a quick indication of what's going on, pertinent prints are hard to beat. – Brian Agnew Aug 06 '09 at 15:15
  • @Brian: I agree, sometimes there's no way around print statements. But again, we can see how much time he probably got lost, learning on how to use a logging framework (although that was the good side effect) and what would be the best way to write println debug statements when he could just download an IDE like Eclipse. It's even open source :) – Juri Aug 06 '09 at 20:02

12 Answers12

9

I wouldn't try and write any fancy methods around printing out debugging info. Just stick with either LOG.debug(...) if you are using a logger or System.err.println(...) otherwise.

You may wish to use String.format("var=%s val=%s", "VarName", val) instead of the String concatenation.

Make sure that you override the toString method in each of your classes to provide meaningful debug info.

At the end of the day it's often easier to fire up the debugger and take a look to see what's going on instead of having to trace through loads of logged debug lines.

The only time when i'd use your kind of debug method would be if my application maintained all of the state inside a map which I could easily print out the key value pairs (e.g. the session map in a web application).

pjp
  • 17,039
  • 6
  • 33
  • 58
  • Thanks for that! I definitely agree that your formatted string approach is better than my string concatenation. Using it henceforth. – Lasoldo Solsifa Aug 06 '09 at 14:39
7

I think that System.err.format is what you want:

System.err.format("var: %s\n", var);

is a shorthand for:

System.err.println(String.format("var: %s", var));
dfa
  • 114,442
  • 31
  • 189
  • 228
  • Hey, now we're getting somewhere. Thanks for this one. pjp suggested using String.format("var=%s val=%s", "VarName", val), and sure enough, yours is shorter. – Lasoldo Solsifa Aug 06 '09 at 15:01
5

Have a look at Simple Logging Framework, it allows you to type:

class Example {
    static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(Example.class);

    void doSomething(Object obj1, Object obj2) {
        LOG.debug("This is object 1: {}, and this is object 2: {}", obj1, obj2);
    }
}
Tim
  • 19,793
  • 8
  • 70
  • 95
  • @Tim - does that reflectively print the contents of the object or does it call toString() on the object ? – Brian Agnew Aug 06 '09 at 14:22
  • 3
    I think it just does a toString() so this doesn't offer much other than avoiding the String concatenation. – pjp Aug 06 '09 at 14:24
  • @pjp- if that's the case I don't think it's hugely useful in this scenario, is it ? – Brian Agnew Aug 06 '09 at 14:30
  • Thanks for that link, Tim. I'm checking it out right now. It may take me a while to find out if this, or other logging techniques, are less hassle than my trusty print statement. – Lasoldo Solsifa Aug 06 '09 at 14:37
  • @pjp & brian: You're right in that it invokes the toString() method, but for complex objects with a properly overridden toString() method it could still very well be useful. The top answer in the question linked in this question already shows it's hard to do anything using reflection, and way past what's needed here. – Tim Aug 06 '09 at 14:44
2

Some thoughts:

  1. I would implement toString() on objects of interest, and in that print out the members in a friendly fashion (e.g. convert timestamps to a readable format etc.). I usually choose a format like:

    Object[member1=,member2=...]
    

    Otherwise printing the object alone will give you the classname plus the identity hash code, and (as you've discovered) that's not hugely useful!

    Commons has a facility to do this automatically. But here's a simple toString() tutorial which I think is more appropriate.

  2. There are logging frameworks that you may be interested in in the future. e.g. check out Log4j. At the moment, however, I wouldn't worry about that.

Brian Agnew
  • 268,207
  • 37
  • 334
  • 440
  • Just watchout implementing a toString method that lists all of the members on class containing lazily loaded members as this will force them all to be created. (e.g. Hibernate collections). – pjp Aug 06 '09 at 14:28
  • I admit that I always assumed the existence of a sensible toString(). I also assumed primitives could be actual parameters where Object was expected. Thank you for the links! They look like they will be helpful. – Lasoldo Solsifa Aug 06 '09 at 14:47
2

Personally I don't suggest to use sysout statements anywhere. You should always use a debugger (using some IDE). I cannot imagine where this wouldn't be wanted.

Otherwise I'm suggesting logging frameworks like log4j, but again, this already gets more complicated where I would again then switch to a real IDE with debugger.

Juri
  • 32,424
  • 20
  • 102
  • 136
  • Logging is always useful for production systems when you cannot always fire up a debugger. They're not too complicated to setup once you find a sample Log4J config file :) – pjp Aug 06 '09 at 14:32
  • Indeed... switching to a good IDE seems like a good step for this person. – jsight Aug 06 '09 at 14:36
  • Thanks, Juri, and I'm sure you're right. I always try to use the simplest tool possible, and my projects are small, so I have not been compelled to use an IDE yet. I have tried Eclipse, though, and it is very easy to see that the advantages are many. – Lasoldo Solsifa Aug 06 '09 at 14:43
  • I'd say that your need to inspect program state compels you strongly to use a debugger and thus an IDE. – Michael Borgwardt Aug 06 '09 at 14:54
  • @Lasoldo: You should definitely switch to Eclipse. I always use it, even for a single-class project. Why to bother with notepad, searching the right packages to import and compile over command line, when Eclipse does it for you in the background. Moreover the debugger comes for free, so.... Using println statements is considered bad practice – Juri Aug 06 '09 at 16:20
  • Yes, you are right. My original motivation was the urge to learn programming from the ground up, the "hard way", if you will. I guessed that this meant using only a text editor and the command line. Now it's probably time to move on to an IDE. – Lasoldo Solsifa Aug 06 '09 at 16:37
1

If you use IntelliJ IDEA, you can use the "Live template" shortcut for printing to System.out such as soutp (and then a TAB) to debug the method parameters, soutv to trace the name of a variable along with it's value, etc.

To read the list of shortcuts\modify it, go to File->Settings->Live Templates->Output

arviman
  • 5,087
  • 41
  • 48
1

My first advice is: stick to java.util.logging package. There is really no need for 3rd party logging libraries.

  1. Get instance of java.util.Logger

    Logger logger = Logger.getLogger("some.package.XyzClassName");

  2. Log objects (using placeholder {N})

    logger.log(Level.INFO, "logging stuff {0} and {1}", new Object[]{new String("Test"), 1222});

In case of user defined classes you need to have some sensible toString() override implementation, because this method is called when replacing placeholder in message {N}.

Mitja Gustin
  • 1,723
  • 13
  • 17
0

You can get access to the variable names with AOP and compile-time weaving. The nice thing about this is that if you don't do the weaving, you don't add the debug logging code and your runtime code is leaner and quicker as a result.

Here's an example of using AspectJ to throw an exception when a field is set to null. Note the usage of "joinPoint.getSignature()" to get access to the code metadata.

@Aspect
public class NotNullValidator {

   @Pointcut(value = "set(@com.acme.NotNull * *.*) && args(valueBeingSet)")
   private void setOfNonNullField(final Object valueBeingSet) { }

   @Before(value = "setOfNonNullField(valueBeingSet)")
   public void validate(final JoinPoint joinPoint, final Object valueBeingSet) {
      if (valueBeingSet == null) {
         throw new NullPointerException("Cannot set " + joinPoint.getSignature().getName() + " to null.");
      }
   }
}

See JoinPoint Javadoc to see what else you can get (line numbers, source and target objects, etc).

Brad Parks
  • 66,836
  • 64
  • 257
  • 336
Steve Reed
  • 2,481
  • 2
  • 20
  • 20
  • This looks pretty interesting, but it is over my hobbyist-level head, that's for sure! It will take me a while to figure out how to test it. Maybe someone could comment on this approach... any takers? – Lasoldo Solsifa Aug 06 '09 at 15:19
  • It definitely takes a while to wrap ones mind around AOP in general, but I've been able to leverage the above approach pretty well. The aspect above allows me to simple annotate fields with a @NotNull and ANY time they are set the null-checking code is "woven" in during the compile. It's really nice and quite a time-saver. – Steve Reed Aug 07 '09 at 18:52
0

It's a stretch, but...

You're getting advice to use an IDE instead of a simple text editor. I'd go along with that, 100%.

You're getting advice to use a logging framework or a debugger instead of println() calls. Well, sure, but...

Better yet is unit tests. Don't ask what it is - tell it what you expect. Then, the integrated unit-testing framework (junit, typically) will verify that you're getting what you expect. The more I use unit tests, the less I need debugging and the less I need println's. And when something changes, the testing framework tells me - I don't need to manually re-evaluate every test's output, just watch the bar.

Unit tests are better than debugging, better than logging. They're not a 100% replacement for debugging and logging, but start using them and you'll find much less need for those tedious activities.

Carl Manaster
  • 39,912
  • 17
  • 102
  • 155
  • I think you're spot on here. When I did an Eclipse tutorial, it was all about writing the tests first, then basically letting the IDE create the methods from them. I have always tried to use the lightest-weight tools possible, and have stuck with the text editor, in spite of the tedium it induces. I may be at the point where this ideology is counter-productive. Thanks for the insight. – Lasoldo Solsifa Aug 06 '09 at 15:07
0

Have you tried DoodleDebug? It's an Eclipse plugin and meant to be as easy as System.out.println() but much more powerful.

It can be found here: http://scg.unibe.ch/wiki/projects/DoodleDebug

Cedric Reichenbach
  • 8,970
  • 6
  • 54
  • 89
0

Regardless of what route you take, I'd recommend doing the following:

  1. Make a custom Debug.out(Object) method in a "Util" or "Debug" class, so no matter how you decide to log the errors, you can change it for the whole program easily;
  2. If you use Eclipse, VSCode, or any other IDE that has "Templates", make a "debug" template that will just write Debug.out("<method name> " + string). At least with Eclipse, you can make the template write the method name for you.
ABadHaiku
  • 65
  • 1
  • 6
0

I would recommend you to properly configure and use Apache Log4J. System.out or System.err lines cause a lot of delay in execution of program. (You can confirm this by adding some time info that how much time your program takes without System.out etc and how much without these lines.)

Logging APIs use separate threads to log your logging info in log files, thats why they are more useful in realistic applications. Also logging APIs like Log4J give a lot of control over how to configure and format your logs. For example look here a few logs generated from Log4J:

2012-01-05 15:16:41,730 [main] 16   DEBUG dmfssecbuild.IOUtil  - 
2012-01-05 15:16:41,730 [main] 16   DEBUG dmfssecbuild.IOUtil  - Application Configs Loaded
2012-01-05 15:16:41,730 [main] 16   DEBUG dmfssecbuild.Constants  - Running Application between dates.
2012-01-05 15:16:41,730 [main] 16   DEBUG dmfssecbuild.Constants  - Sat Jan 01 00:00:00 GMT+05:00 2011 From Date 
2012-01-05 15:16:41,730 [main] 16   DEBUG dmfssecbuild.Constants  - Mon Dec 31 00:00:00 GMT+05:00 2012 To Date 
Naveed Kamran
  • 169
  • 2
  • 8