4

The problem is either tricky or simple (not sure yet):

The project that I'm currently involved with has a bit too many logging libraries. We have log4j, sl4j, logback and who knows what else...

One of these is logging something to System.out and I've run out of ideas on how to find out which one and when...

I'm thinking about something like "putting a breakpoint" on sysout but I don't know if that's even something possible.

Any ideas?

Pawel Gorczynski
  • 1,227
  • 1
  • 15
  • 17

3 Answers3

5

You can redirect System.out to a custom PrintStream that throws an error or logs it in a file. You'll have to manually go through the stacktrace and look for the offending method.

To create it:

PrintWriter pw = ...;
PrintStream ps = new PrintStream(System.out) {
  public void write(int b) {
    super.write(b);
    /*try { throw new Error(); } 
    catch (Error e) { e.printStackTrace(pw); }*/

    //Edit: Jeff Wang's answer is a better approach since it doesn't
    // throw an exception and just prints the stacktrace
    new Exception().printStackTrace(pw);

    //You could also write to stderr
    Thread.dumpStack();
  }
};

To change System.out:

System.setOut(ps);

If you set this at the start of your program, this'll throw an error whenever a library prints to System.out, catch it, and write to a file (or somewhere else). You can always format it better if you want.

This isn't a great solution beyond testing, because it involves throwing an exception every time you write a single byte (you could override the other write method if you wish, though) and because you need to manually go through the stacktraces. In production, you'd want something else.

Edit: Throwing an exception is unnecessary. You can do Thread.dumpStack or new Exception().printStackTrace, as the other answer by Jeff Wang suggested.

user
  • 7,435
  • 3
  • 14
  • 44
  • Why wouldn't you recommend this? It seems like a perfectly reasonable way to instrument `System.out` calls. – dimo414 Jun 25 '20 at 21:06
  • @dimo414 Because it throws an error instead of letting the program continue as is. It's only okay for testing purposes, not when you want to log which libraries are logging via stdout – user Jun 25 '20 at 21:07
  • You could instrument it to do anything else you wanted, like write to a file or to stderr instead. – dimo414 Jun 25 '20 at 21:08
  • 1
    I suggest editing that first sentence to remove your reluctance to recommend. Instead, put a note at the end cautioning that they remove the instrumentation before deploying to production. – Basil Bourque Jun 25 '20 at 21:14
  • @user I forgot to add that I'm running an org.junit unit test so it perfectly fine to throw an exception :) Your approach seems quite promising although I've put the code in the unit test constructor (together with `System.setErr(ps)` just in case) and nothing happens :/ (i.e. the output is still on the console but the exception is not being thrown). Anyways, it's quite late, I'll take another look tomorrow. – Pawel Gorczynski Jun 25 '20 at 21:18
  • No need to throw-and-catch, just use `new Exception().printStackTrace()`. – dimo414 Jun 25 '20 at 21:21
  • I personally prefer the other solution (from Jeff Wang). No need to throw and catch an exception. – Thomas Kläger Jun 25 '20 at 21:31
  • 1
    Nice answer using `System.setOut`. – Unmitigated Jun 25 '20 at 21:52
  • @PawelGorczynski Does it still not work for you? Try overriding a different method, like `write(byte[] b)` or `println` – user Jun 26 '20 at 18:11
  • @user Actually I managed to figure out the problem by classic analysis but for the sake of science I can try when I have a bit of time – Pawel Gorczynski Jun 27 '20 at 19:58
5

I've used this before on err, to see the actual root cause (instead of the ...and more)

    PrintStream o = new PrintStream(new File("A.txt")) { 
        public void write(byte[] b) throws IOException {
              super.write(b);
              IOException ie = new IOException();
              ie.printStackTrace(); //don't throw this!
        }
    };

and set it like user mentioned above. It's a different method then the one he used, I'm not too sure what the difference is, I just remembered that it worked.

Jeff Wang
  • 1,837
  • 1
  • 15
  • 29
3

There are a few options:

  1. Step through your code with a debugger, as @hev1 suggested. You should be able to set a break-point right on System.out.println() (though remember there are other methods you may need to add breakpoints to as well). Especially for a test this is likely the easiest and least-invasive approach.

  2. Instrument System.out by pointing it to a custom PrintStream implementation via System.setOut() that's implemented however you see fit, as @user and @Jeff Wang suggested. You can throw an exception or log a stack trace to manually inspect the call stack, or do something more clever like inspecting the call stack at runtime.

    Note that, unlike the other answers, you'll want to capture a reference to the original value of System.out before replacing it. I think the "real" out/err instances will be lost forever if you don't :)

  3. You may be able to configure a SecurityManager to control stdout/stderr. I'm not yet sure whether this is possible out of the box, but presumably you could use System.setOut() as above to register a PrintStream that queries a SecurityManager. Basically this is just a more enterprise-scale version of #2. This is probably not necessary unless you intend to enforce who can write to stdout/stderr, but it's worth mentioning an option :)

dimo414
  • 47,227
  • 18
  • 148
  • 244