A Simple AS3 Logging Library using the Chain Of Responsibility and Singleton Pattern

Few years back, I wrote a simple logging library in ActionScript3 using the Chain of Responsibility pattern. Recently, I dug up the old codes, made some modifications, and put it in github for other people to use and contribute. In this post, I will mainly focus on how to use the library and do some project specific modifications, but  I will also briefly discuss my reasons for using the Chain Of Responsibility and the Singleton Pattern. I started writing this library with the following 4 goals in mind:

  1. It should be easily accessible to all of the classes of a project.
  2. It should be able to write logging information to different output sources, i.e., file, database, console, etc., and additional output sources can be added easily.
  3. It should be able to handle different levels of logging like FATAL_ERROR or INFO (as different actions may be needed for handling different levels), and additional levels can be added easily.
  4. It should require minimal setup.

Let’s start with Goal 4. To use this library, all you need to to is to add its source path to your project. All of your classes will now be able to log by calling SimpleLogger.getLogger().log(...) function.  Currently, this library can write to console and a mysql database. For each additional output source, you only need to add a function to the LogWriter class (Goal 2). Obviously, you need to create a table on your database, which you can do by running the following command:

CREATE TABLE `simple_as3_logger` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `logger_name` varchar(45) DEFAULT NULL,
  `class_name` varchar(100) DEFAULT NULL,
  `log_msg` varchar(500) DEFAULT NULL,
  `data` varchar(1000) DEFAULT NULL,
  `log_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;

To access the database, you need a small server side code. The following php code should suffice:

<?php
if($_POST['auth_string']!="f5a666ce1cf64709e2bdf6bd517cf71c19acbae4")
		exit("authentication failed.");
// Do not forget to fill up the following variables
$server_ = "";
$user_ = "";
$password_ = "";
$database_ = "";

$logger_name = $_POST['logger_name'];
$class_name = $_POST['class_name'];
$log_msg = $_POST['log_msg'];
$data = $_POST['data'];

$sql = new mysqli($server_,$user_,$password_,$database_);
$query = $sql->prepare("INSERT INTO `simple_as3_logger` (`logger_name`, `class_name`, `log_msg`, `data`) VALUES (?,?,?,?);");
$query->bind_param("ssss", $logger_name, $class_name, $log_msg, $data);

$query->execute();
?>

At last, save the url of the php file to the LogWriter class’s dbServerURL variable, and you are done!

Goal 3 is very standard for any logging library. A logger should be able to handle different levels of logging. The Chain Of Responsibility pattern is very handy for this purpose, because handling different levels is inherent in its design. In this pattern, different processing units are chained together (like a linked list), where each processing unit is responsible for processing a task. However, if one unit cannot process the task, it sends the task to the next unit, and so on until the task is processed. The rule of thumb here is that each unit can process tasks lower than or equal to its level. The following is an example of an abstract processing unit:

public class ProcessingLogger implements IProcessingLogger
{
	/**
	* next logger in the chain of responsibility.
	*/
	protected var next:IProcessingLogger;
	private var _loggingLevel:uint;
	public function ProcessingLogger(_loggingLevel:uint)
	{
		this._loggingLevel = _loggingLevel;
	}

	public function log(level:uint, className:String, logMsg:String = "", data:Object = null):void
	{
		if (level <= _loggingLevel) {
			process(className, logMsg, data);
		}
		else if (next != null) {
			next.log(level, className, logMsg, data);
		}
	}
	protected function process(className:String, logMsg:String = "", data:Object = null):void {
		throw new Error("Abstract method. Must be overridden");
	}
}

Each of the level of the chain extends this abstract class, which we call a concrete processing unit. One such example is as follows:

public class TraceProcessingLogger extends ProcessingLogger
{
	public function TraceProcessingLogger(next:IProcessingLogger=null)
	{
		super(LoggingLevel.TRACE);
		this.next = next;
	}
	protected override function process(className:String, logMsg:String = "", data:Object = null):void {
		//Write log to console
	}
}

Depending on what you want the level to do, you can either write to console, files, databases, etc., in the process(...) function of each of the concrete implementations. Currently, this library comes with implementations for 6 levels, namely TRACE, DEBUG, WARNING (writes to the console) and INFO, ERROR, FATAL (writes to the database). For additional levels, add the level to the LoggingLevel class, create a concrete implementation, and add the new class to the chain.

Now, I want to discuss how to achieve Goal 1. For this library to be easily accessible to all the classes to your project, I am making the top most class (or the entry point of this library) a singleton. Although I am not a big fan of the singleton design pattern, I think in this case it is appropriate. For one, logging is a one way information flow (a typical logger will only write, not read). But, most importantly, a logging class does not affect the core logic of your project in any way. To learn why singletons should be avoided in most cases, you can check out my other post here. This is also where we create the chain. To process a logging request we must start with the lowest level processing unit and move higher. The class looks like the following:

package org.rfaisal.logging
{
	import org.rfaisal.logging.levels.*;

    public class SimpleLogger
    {
		private static var instance:SimpleLogger;
		private var pLogger:IProcessingLogger;

		public static function getLogger():IProcessingLogger
		{
			if( instance == null )
					instance = new SimpleLogger( new SingletonEnforcer() );
			return instance.pLogger;
		}

		public function SimpleLogger( pvt:SingletonEnforcer )
		{
			if (pvt == null) {
				throw new Error("Error: Instantiation failed: Use SimpleLogger.getLogger() instead of new.");
			}
			else {
				var fatalLogger:IProcessingLogger = new FatalProcessingLogger();
				var errorLogger:IProcessingLogger = new ErrorProcessingLogger(fatalLogger);
				var infoLogger:IProcessingLogger = new InfoProcessingLogger(errorLogger);
				var warningLogger:IProcessingLogger = new WarningProcessingLogger(infoLogger);
				var debugLogger:IProcessingLogger = new DebugProcessingLogger(warningLogger);
				var traceLogger:IProcessingLogger = new TraceProcessingLogger(debugLogger);

				pLogger = traceLogger;
			}
		}
    }
}
internal class SingletonEnforcer{}

[Reference: Java Logging API]

Tagged: , , , , , , , , ,

2 thoughts on “A Simple AS3 Logging Library using the Chain Of Responsibility and Singleton Pattern

  1. limo bus toronto July 21, 2013 at 4:36 am Reply

    Hi fantastic blog! Does running a blog such as this require a great deal of work?
    I have no knowledge of programming but I had been hoping to start my own blog in the near future.
    Anyhow, if you have any recommendations or techniques
    for new blog owners please share. I know this is off topic however I simply needed to ask.
    Thank you!

    • Faisal R. July 21, 2013 at 1:26 pm Reply

      No, it doesn’t. Create an account at wordpress.com and you are good to go. You do not even need any programming skills.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: