0

I'm sort of a beginner Java programmer with 2 years of experience. I am creating a small CLI tool with Java for human users (not called by server programs). My Java code got flagged by a code smell detecting tool of my company. The tool said that I shouldn't use System.out and I should consider using Log4j instead.

I googled a lot on the Internet, but nearly every post starts with something like "...System.out is usually used for quick logging, however, blah blah blah, so you should avoid using it".

How could they assume that System.out is always used for logging? Nobody even mentioned any other possible use cases of it.

Let's suppose you were developing a new grep for a Linux shell. You have to print the result of pattern matching to the user, right? I just can't imagine you would use Log4j to do that.

So, to simply put, shall I just totally forget about System.out? What would you use to output non-log information in Java?

Mark Rotteveel
  • 82,132
  • 136
  • 114
  • 158
Zhou
  • 389
  • 2
  • 11
  • 2
    if you are writing a command-line tool there is nothing wrong with using stdin, stdout, and stderr. But it's not evident what you are wanting to do, for anything but a command-line tool the standard streams aren't helpful. static code analyzers always assume you must be doing something stupid. – Nathan Hughes Dec 11 '20 at 21:42
  • 1
    You will not normally write UNIX commands in JAVA - that's what C was invented for in the first place. There are not a lot of console JAVA applications. And why would you ever use `System.out` for anything else? – PM 77-1 Dec 11 '20 at 21:42
  • @NathanHughes thanks for the quick response. I got it. – Zhou Dec 11 '20 at 21:45
  • this seems related? https://stackoverflow.com/q/31869391/217324 – Nathan Hughes Dec 11 '20 at 21:47
  • @PM77-1 I mention "grep" just as an example. Java can be used to create command-line tools, right? You can also imagine that you are using Java to create the Hadoop shell program, or MySQL shell, or something like that... – Zhou Dec 11 '20 at 21:48
  • @NathanHughes hm... that is sort of related. Actually, I understand if I'm creating a server-side program, I should definitely not expect anyone to be staring at the screen when the program is working. But I don't think Java programs are always server-side programs. – Zhou Dec 11 '20 at 21:54
  • 3
    Sure you can make command line tools. Just JVM startup time makes this unappealing – Nathan Hughes Dec 11 '20 at 21:55
  • 2
    @Zhou I think the "code smell" is correct but the suggested solution is not versatile, as you point out with the command line tool example. Writing explicitly to System.out makes it difficult to write unit tests for your code. Better to write to a PrintStream or Writer so that you can write unit tests that capture output in a ByteArrayOutputStream or StringWriter. – user4851 Dec 11 '20 at 21:56
  • Either your routines will have UI and then you will not use `System.out` or they will run in batch mode and you still won't use it. So anything other than a simple toy for your own use - and you need something more versatile. – PM 77-1 Dec 11 '20 at 21:59
  • @user4851 Aha, thanks! This is a more detailed answer. So if I define a PrintStream variable in an appropriate scope and let the rest of my program print to it. That would be a more "typical" practise in Java programming, right? – Zhou Dec 11 '20 at 22:00
  • 1
    @user4851 – Why would printing to System.out makes unit tests difficult to write? Refer to https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/System.html#setOut(java.io.PrintStream) next time you have a problem with that. – tquadrat Dec 11 '20 at 22:27
  • 1
    @tquadrat System.setOut affects global state; if you setOut to your custom stream and an exception is thrown and you don't reset System.out, your other tests are affected. Sure, you could reset it in a `finally` block in every test, but wouldn't a cleaner program design make your tests more readable? – user4851 Dec 11 '20 at 22:53
  • 1
    @Zhou - that is a more typical practice, in my experience. It also has benefits like allowing you to write to a FileOutputStream instead, based on command line options. – user4851 Dec 11 '20 at 22:54
  • 1
    @user4851 – The test framework I use will perform a check on the global state (most aspects of it) before each test and resets it if changed. It provides methods to tweak it as part of the test, and let a test fail when the global state is changed unexpectedly by the code under test. If your output is intended to go to ```System.out``` in the end, you should use it in your code; that you may have methods that provide output to an arbitrary Stream or Writer is a different kettle of fish. – tquadrat Dec 12 '20 at 00:08
  • @user4851 – And a ```finally``` block is not the right place for the cleanup after a (unit) test; that's where you have the before and after methods for (or however these are named in your framework). – tquadrat Dec 12 '20 at 00:12
  • @user4851 Thank you so much for sharing your valuable experience with me. Thank you! – Zhou Dec 14 '20 at 08:13
  • @tquadrat Hey, I think user4851's idea makes sense, and more importantly, **he (both of you) confirmed that using `System.out` in a non-server program is an acceptable practice**. This is the point I wanted to clarify in the first place. I do think your opinion makes sense too, and thank you for pointing out the `System.setOut` which I didn't know. New knowledge learned. Thank you! – Zhou Dec 14 '20 at 08:17

3 Answers3

1

First, System.out is not the target for logging output, except for very tiny tools or some one shot stuff; that's why logging frameworks have been invented. And there it does not really matter at first place, whether you go for Log4J, JDK Logging or something completely different. Although Log4J is established as the de facto standard for logging in the Java world.

As those "code smell detectors" always assume (until informed otherwise) that you write some kind of server software, they warn when you use output to System.out because for such a server, it usually makes no difference whether you print to System.out or /dev/nul

But if you write a command line tool of some kind (perhaps a batch client to a server, just to have another sample than grep …), it is perfectly okay to write the current status or other output to System.out (even when the "smell sniffer" is still moaning). "Status" means here mainly the positive feedback; any error messages should still be logged … to a logging subsystem, although it would be still a good idea to print them to the screen, too.

And usually you can annotate your source code to pacify the "smell detector" …

tquadrat
  • 1,653
  • 12
  • 16
  • Thank you. Your answer explained a lot of things. – Zhou Dec 11 '20 at 22:09
  • @tquadrat Very well said. As you said "means here mainly the positive feedback" this is one of the main reason of using loggs, so you see your application workflow goes as it should. – Co ti Dec 11 '20 at 22:11
  • 1
    @Coti – now you confuses me: usually you do *not* log the positive (or success) case (or only at ```info``` or less) because that does not provide any useful information (except you talk about an "audit log" – but that is not logging as we talk about here). So I would print the progress information not to the logs, but to ```System.out``` in most cases, while only failures will be logged. – tquadrat Dec 11 '20 at 22:16
  • @tquadrat Sorry for confuse. Logging typically means the recording of implementation level events that happen as the program is running (methods get called, objects are created, etc.). As such it focuses on things that interest programmers Auditing is about recording domain-level events: a transaction is created, a user is performing an action, etc. In certain types there is a legal obligation to record such events. Yes I meant an audit logging. – Co ti Dec 11 '20 at 22:23
1

It's a code smell issue, but logging may not be the preferred alternative. I'd prefer accepting a stream parameter to make writing unit tests easier. For example:

public class Greeter {

    public void greet(String name, PrintWriter out) {
        out.print("Hello, " + name);
    }

}

public class GreeterProgram {

    public static void main(String[] args) {
        new Greeter().greet(args[0], new PrintWriter(System.out));
    }

}

public class GreeterTest {

    @Test
    public void testGreet() {
        StringWriter sw = new StringWriter();
        PrintWriter out = new PrintWriter(sw);
        new Greeter().greet("Zhou", out);
        out.flush();
        assertEquals("greeting", "Hello, Zhou", sw.toString());
    }
}
user4851
  • 645
  • 1
  • 6
  • 17
  • Hey, thank you so much for the sample code, which makes things clearer, especially for a beginner Java programmer like me. You even correctly spelled my name :-D – Zhou Dec 14 '20 at 08:32
0

When you developing a bigger application with team of size > 5. You have to use one frameworks for logging definitely. It is important for others to get easier to the root of an issue.

System.out you can use in very small applications or small teams where you can communicate on basics, but it is not recommended, since you want to see logs reasoning why application crashed with coresponding data and not console print outs saying there is something wrong, but you have no idea in what class is it in happening becouse your team mate coded that particular class.

Co ti
  • 111
  • 10
  • 2
    To be honest, team size is the most irrelevant factor for the decision whether you should output to a logging system or to System.out. First, regular output is not meant to go to a logging system, and when your Java program lives on the console, System.out is where you write to. Logging out is not regular output, as the name says … – tquadrat Dec 11 '20 at 21:56
  • Thanks for the answer! You wrote that "You have to use one frameworks for logging". However, what I'd like to know is how you print "non-logging" information? – Zhou Dec 11 '20 at 21:56
  • @Zhou Are you asking question about logging on client side or server side? – Co ti Dec 11 '20 at 22:03
  • @Coti Sorry for the confusion! I am creating a CLI tool with Java that uploads/downloads artifacts to/from some repositories, so it is a client-side program. I'll update the question. – Zhou Dec 11 '20 at 22:07
  • @Zhou In that case have a look at this documentation. Let me know if it is what you was looking for. https://docs.oracle.com/javase/7/docs/api/java/util/logging/Logger.html – Co ti Dec 11 '20 at 22:15
  • @Coti hmm... this is a good document that I should've read much earlier. Thank you for sharing. However, I am not looking for good practice for logging. I wanted to know how a non-server CLI program prints "normal", "non-logging" output to the user. Thank you again for sharing me the knowledge. – Zhou Dec 14 '20 at 08:26