0

Is there any way I can mirror the console output to localhost in java and maybe even add some nice CSS to it. It would be cool if the console could also be reached by other devices in the same network. I have done much research regarding this topic but haven't found ANY websites/threads/questions about this. Help would be appreciated!

2 Answers2

1

In order to intercept the output normally going to the console (or the standard output) you need to use the following API somewhere in your code:

System.setOut(myStream); 
System.setErr(myStream); //If you want to grab the error stream also. Could go do a different location

Many log libraries can already do this for you. But this is basically how you need to capture the output. What 'myStream' actually does is up to you. The quickest route to get this out to a web server on http://localhost:8888 is to direct the output to a file and start the JDK's embedded web server. Here is an example you should be able to run:

package test.example;

import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpServer;

import java.io.OutputStream;
import java.io.PrintStream;
import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Instant;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class SystemOutToWeb
{
    public static void main(String... args ) throws Exception
    {
        final Path myOutputFile = Paths.get("./MyOutputFile.txt");
        final PrintStream myStream = new PrintStream(myOutputFile.toFile());

        System.out.println("Going to redirect to : " + myOutputFile.toAbsolutePath());

        System.setOut(myStream);
        System.setErr(myStream);

        System.out.println("Starting the Output");

        //Have something that logs every 5 seconds
        final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
        scheduler.scheduleAtFixedRate(() ->
        {
            System.out.println("Hello - the time is now " + Instant.now());
        }, 1, 5, TimeUnit.SECONDS);

        // Start the simple Java Built in Web Server.
        final HttpServer http = HttpServer.create(new InetSocketAddress(8888), 0);
        final HttpContext context = http.createContext("/");
        context.setHandler(exchange ->
        {
            byte[] data = Files.readAllBytes(myOutputFile);
            exchange.sendResponseHeaders(200, data.length);
            OutputStream os = exchange.getResponseBody();
            os.write(data);
            os.close();
        });
        http.start();

    }
}

If you give it a few seconds to run, then you should be able to see something at http://localhost:8888.

Of course, this is only the starting point. You could for instance use a different web server all together or augment this resource with some CSS further (perhaps even use a web socket to stream the file out as it updates).

JJS
  • 141
  • 3
  • Is it possible to "refresh" the content of the site while not pressing refresh in the browser? – CreativeDifference Dec 31 '21 at 11:20
  • Generally speaking there would be two ways of doing this. The easiest (low-tech) solution is to instead serve the resource as an HTML with a " " in the header. This will instruct the browser to refresh every 30 seconds (so it does a refresh without user intervention). The second approach is a bit more involved - you basically have to set up a Websocket (https://en.wikipedia.org/wiki/WebSocket) which will 'push' updates to the client. Bit more involved - perhaps beyond the scope of this question. – JJS Dec 31 '21 at 11:25
  • But how do I edit the source html of the page? – CreativeDifference Dec 31 '21 at 14:42
  • and what is the `byte[] data = Files.readAllBytes(myOutputFile);` for? Im assuming the `200` in `exchange.sendResponseHeaders(200, data.length);` is the response code the website returns right? – CreativeDifference Dec 31 '21 at 14:46
  • Ive scoured the entire internet, but still don't know how to do this without saving the console output to a file! pls help – CreativeDifference Dec 31 '21 at 22:52
  • 1
    You can direct your stream to a memory buffer instead, if that is what you're after. However, the usual caveats around running out of memory and this now being your responsibility to clean up after it applies. To do this - instead instantiate your PrintStream with: `final ByteArrayOutputStream bous = new ByteArrayOutputStream(); final PrintStream myStream = new PrintStream(bous);` – JJS Jan 02 '22 at 08:59
  • 1
    Subsequently, if you want to wrap your string in HTML say, you can do : `context.setHandler(exchange -> { StringBuilder html = new StringBuilder(""); html.append(new String(bous.toByteArray())); html.append(""); exchange.sendResponseHeaders(200, html.length()); OutputStream os = exchange.getResponseBody(); os.write(html.toString().getBytes()); os.close(); });` – JJS Jan 02 '22 at 09:03
0

Do you want something like centralized logs? There are tools for that like Grafana Loki, where you can use a log collector named promtail to collect logs from files and send them to your Loki instance, and with the Grafana frontend you have searchable, colored logs. This can include logs from multiple applications.

https://grafana.com/docs/loki/latest/clients/promtail/

You could send that to Grafana Cloud, which got a free tier for up to three users, or host a Grafana Stack with docker, here is an ready to use stack:

https://github.com/stefanprodan/dockprom

Adam Malik
  • 345
  • 1
  • 7