DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
11 Monitoring and Observability Tools for 2023
Learn more
  1. DZone
  2. Coding
  3. Java
  4. Log4Play: Log4j Appender and UI with Play Framework, Knockout.js and WebSockets

Log4Play: Log4j Appender and UI with Play Framework, Knockout.js and WebSockets

Felipe Oliveira user avatar by
Felipe Oliveira
·
May. 16, 11 · Interview
Like (0)
Save
Tweet
Share
13.04K Views

Join the DZone community and get the full member experience.

Join For Free
[img_assist|nid=40327|title=|desc=|link=none|align=left|width=100|height=29]

Log4Play is a Play! Framework module that provides a log4j appender which publishes log entries to an EventStream. In theory, you can use it from any Java application that uses log4j, but I have only tested it with a Play! Framework application. Log4Play provides an user interface which uses a WebSocket to create a live stream of log messages. The user interface allows you to tail the logs of your application without needing to login to the actual box. I worked on it with Deepthi Rallabandi, who I am working with on an Accenture project; I just introduced her to Play!. It has been a pleasant surprise to see how quickly she's progressing, which again confirms how productive Play! is. In one day, she went from knowing nothing about WebSockets, and not that much Web experience, to having a full working WebSockets-based application with Play!. She used my previous WebSockets article to guide her through the process. So let me go over the implementation.

First we created a Log4J appender.
public class PlayWebSocketLogAppender extends WriterAppender implements Appender {

	/**
	 * Publish log event to WebSocket Stream
	 * 
	 * @see org.apache.log4j.WriterAppender#append(org.apache.log4j.spi.LoggingEvent)
	 */
	@Override
	public void append(LoggingEvent event) {
		LogStream.publish(new Log4PlayEvent(event));
	}
}


Then we created a plugin to add the appender to log4j automatically, so you don't have to modify your log4j configuration. A PlayPlugin allows you to customize the behavior of the framework, I highly recommend you going through the source code.
public class Log4PlayPlugin extends PlayPlugin {

	/**
	 * On application start.
	 */
	@Override
	public void onApplicationStart() {
		// Add appender that will stream log messages as Log4PlayEvent instances
		// through WebSocket (Log4Play.WebSocket.stream)
		PlayWebSocketLogAppender appender = new PlayWebSocketLogAppender();
		Logger.log4j.addAppender(appender);

		// Add routes for the UI
		Router.addRoute("GET", "/@logs", "Log4Play.index");
		Router.addRoute("WS", "/@logs/stream", "Log4Play.WebSocket.stream");
	}

}


Then we created an event stream which will be receiving the log messages from the appender.
public abstract class LogStream {

	/** The stream. */
	public static final ArchivedEventStream<log4playevent> stream = new ArchivedEventStream<log4playevent>(50);

	/**
	 * Gets the stream.
	 * 
	 * @return the stream
	 */
	public static EventStream<log4playevent> getStream() {
		return stream.eventStream();
	}

	/**
	 * Publish.
	 * 
	 * @param event
	 *            the event
	 */
	public static void publish(Log4PlayEvent event) {
		stream.publish(event);
	}

}

Notice how we are using an ArchivedEventStream which we can use to display x numbers of messages as soon as the user loads the user interface, instead of seeing a blank page which will then display log messages as they happen from that point on. That's the main difference between the ArchivedEventStream and the EventStream which I used on my first WebSockets article, WebSockets with Play Framework 1.2 in Action!.

Then we defined a WebSocketController which listens to messages dropped on the event stream and push them to the client.
public class Log4Play extends Controller {

	/**
	 * Index.
	 */
	public static void index() {
		render();
	}

	/**
	 * The Class WebSocket.
	 */
	public static class WebSocket extends WebSocketController {

		/**
		 * Index.
		 */
		public static void index() {
			EventStream<play.modules.log4play.log4playevent> loggingStream = play.modules.log4play.LogStream.getStream();
			while (inbound.isOpen()) {
				try {
					Promise<play.modules.log4play.log4playevent> promise = loggingStream.nextEvent();
					play.modules.log4play.Log4PlayEvent event = await(promise);
					outbound.sendJson(event);

				} catch (Throwable t) {
					Logger.error(play.modules.log4play.ExceptionUtil.getStackTrace(t));
				}
			}
		}

	}

}

The difference between this implementation and the one from my previous article is that on this case a JSON object is getting sent to the view, instead of a plain string.

Then on the view side we used Knockout.js.
    // Define Knockout.js Observable
    var viewModel = {};
    viewModel.details = ko.observable();
    ko.applyBindings(viewModel);
    viewModel.details("Log Events will start showing up here...");

    // Create a socket
    var socket = new WebSocket('@@{Log4Play.WebSocket.index()}');
    
    // Display a message
    var data = "";
    var display = function(json) {
		var event = JSON.parse(json);
    	if ( event != null && json != null ) {
	    	var checkLevel = document.getElementById("log4play_level_" + event.level);
	    	if ( checkLevel != null && checkLevel.checked == true ) {
	    		var item = event.level + ' - ' + event.category  + ' - ' + event.date + ' - ' + event.message;
	    		data = item + data;
	    		viewModel.details(data);
	    	}
    	}
    } 
    
    // Message received on the socket
    socket.onmessage = function(event) {
    	display(event.data);
    }


Installation
Under dependencies.yml:
require:
– play → log4play 0.0.1
Under routes:
WS      /logstream                    Log4Play.WebSocket.index
GET     /@logs                          Log4Play.index
GET     /public_log4play/          staticDir:public_log4play


Live Demo
A live demo is available at http://log4play.mashup.fm:9030/@logs. As soon as you enable the module on your application you should have the same UI available as well under /@logs.

Source Code
The source code is available on Github at https://github.com/feliperazeek/log4play.

Now Go Play!

Article originally posted at http://geeks.aretotally.in/log4play-log4j-ui-mashed-up-with-play-framework-knockout-js-and-websockets.
WebSocket Log4j Framework Play Framework application Interface (computing) Stream (computing) Implementation Event

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • What Is API-First?
  • Introduction to Automation Testing Strategies for Microservices
  • The 5 Books You Absolutely Must Read as an Engineering Manager
  • OWASP Kubernetes Top 10

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: