In order to create your own appenders, you will have to create a cfc that extends logbox.system.logging.AbstractAppender
and implement the following methods:
Method | Description |
---|---|
init() |
Your constructor. Make sure to call super.init( argumentCollection=arguments ); |
logMessage() |
The method that is called when a message is received. |
onRegistration() |
An interceptor that fires when the appender gets created and initialized. It can be used for preparing the appender for operation. |
onUnRegistration() |
An interceptor that fires when the appender is removed from a logger. |
The signature of the init method is the following:
<--- Init --->
<cffunction name="init" access="public" returntype="AbstractAppender" hint="Constructor called by a Concrete Appender" output="false" >
<--- ************************************************************* --->
<cfargument name="name" type="string" required="true" hint="The unique name for this appender."/>
<cfargument name="properties" type="struct" required="false" default="#structnew()#" hint="A map of configuration properties for the appender"/>
<cfargument name="layout" type="string" required="false" default="" hint="The layout class to use in this appender for custom message rendering."/>
<cfargument name="levelMin" type="numeric" required="false" default="0" hint="The default log level for this appender, by default it is 0. Optional. ex: LogBox.logLevels.WARN"/>
<cfargument name="levelMax" type="numeric" required="false" default="4" hint="The default log level for this appender, by default it is 5. Optional. ex: LogBox.logLevels.WARN"/>
<--- ************************************************************* --->
</cffunction>
As you can see each appender receives a name
, a structure of properties
, an optional layout
class, and an optional levelMin
and levelMax
severity levels. The properties
and layout
are both optional, but you must call the super.init( argumentCollection = arguments )
method in order to have full ok operation on the appender. You can then do your own constructor as you see fit. Here is an example:
<--- Constructor --->
<cffunction name="init" access="public" returntype="FileAppender" hint="Constructor" output="false">
<---************************************************************** --->
<cfargument name="name" type="string" required="true" hint="The unique name for this appender."/>
<cfargument name="properties" type="struct" required="false" default="#structnew()#" hint="A map of configuration properties for the appender"/>
<cfargument name="layout" type="string" required="true" default="" hint="The layout class to use in this appender for custom message rendering."/>
<cfargument name="levelMin" type="numeric" required="false" default="0" hint="The default log level for this appender, by default it is 0. Optional. ex: LogBox.logLevels.WARN"/>
<cfargument name="levelMax" type="numeric" required="false" default="4" hint="The default log level for this appender, by default it is 5. Optional. ex: LogBox.logLevels.WARN"/>
<---************************************************************** --->
<cfscript>
super.init(argumentCollection=arguments);
// Setup Properties
if( NOT propertyExists("filepath") ){
$throw(message="Filepath property not defined",type="FileAppender.PropertyNotFound");
}
if( NOT propertyExists("autoExpand") ){
setProperty("autoExpand",true);
}
if( NOT propertyExists("filename") ){
setProperty("filename",getName());
}
if( NOT propertyExists("fileEncoding") ){
setProperty("fileEncoding","UTF-8");
}
// Setup the log file full path
instance.logFullpath = getProperty("filePath");
// Clean ending slash
if( right(instance.logFullpath,1) eq "/" OR right(instance.logFullPath,1) eq "\"){
instance.logFullPath = left(instance.logFullpath, len(instance.logFullPath)-1);
}
instance.logFullPath = instance.logFullpath & "/" & getProperty("filename") & ".log";
// Do we expand the path?
if( getProperty("autoExpand") ){
instance.logFullPath = expandPath(instance.logFullpath);
}
//lock information
instance.lockName = getname() & "logOperation";
instance.lockTimeout = 25;
return this;
</cfscript>
</cffunction>
The signature of the logMessage
method is the following:
<--- logMessage --->
<cffunction name="logMessage" access="public" output="false" returntype="void">
<cfargument name="logEvent" type="logbox.system.logging.LogEvent" required="true" hint="The logging event to log.">
</cffunction>
As you can see it is a very simple method that receives a LogBox logging event object. This object keeps track of the following properties with its appropriate getters and setters:
timestamp
category
message
severity
extraInfo
You can then use this logging event object to log to whatever destination you want. Here is a snippet from our scope appender:
<--- Log Message --->
<cffunction name="logMessage" access="public" output="true" returntype="void" hint="Write an entry into the appender.">
<---************************************************************** --->
<cfargument name="logEvent" type="logbox.system.logging.LogEvent" required="true" hint="The logging event"/>
<---************************************************************** --->
<cfscript>
var logStack = "";
var entry = structnew();
var limit = getProperty('limit');
var loge = arguments.logEvent;
// Verify storage
ensureStorage();
// Check Limits
logStack = getStorage();
if( limit GT 0 and arrayLen(logStack) GTE limit ){
// pop one out, the oldest
arrayDeleteAt(logStack,1);
}
// Log Away
entry.id = createUUID();
entry.logDate = loge.getTimeStamp();
entry.appenderName = getName();
entry.severity = severityToString(loge.getseverity());
entry.message = loge.getMessage();
entry.extraInfo = loge.getextraInfo();
entry.category = loge.getCategory();
// Save Storage
arrayAppend(logStack, entry);
saveStorage(logStack);
</cfscript>
</cffunction>
Finally, both the onRegistration
and onUnregistration
methods have to be void methods with no arguments.
<cffunction name="onRegistration" access="public" hint="Runs after the appender has been created and registered. Implemented by Concrete appender" output="false" returntype="void">
</cffunction>
<cffunction name="onUnRegistration" access="public" hint="Runs before the appender is unregistered from LogBox. Implemented by Concrete appender" output="false" returntype="void">
</cffunction>
These are great for starting or stopping your appenders if they so need to. Here is a sample from our socket appender:
<--- onRegistration --->
<cffunction name="onRegistration" output="false" access="public" returntype="void" hint="When registration occurs">
<cfif getProperty("persistConnection")>
<cfset openConnection()>
</cfif>
</cffunction>
<--- onRegistration --->
<cffunction name="onUnRegistration" output="false" access="public" returntype="void" hint="When Unregistration occurs">
<cfif getProperty("persistConnection")>
<cfset closeConnection()>
</cfif>
</cffunction>