All pages
Powered by GitBook
1 of 5

Loading...

Loading...

Loading...

Loading...

Loading...

Registering Appenders at Runtime

You can programmatically register appenders at runtime by using the registerAppender() function exposed in the LogBox object. Here is the function API:

registerAppender(name, class, [properties={},] [layout="",] [levelMin=0,] [levelMax=4]);

Info Please note that registering dynamic appenders at runtime is tricky as some objecs might already have references to some appenders. We recommend registering appenders at configuration load or when the application starts up.

Instance Members

Every Appender has access to the following public variables:

Property

Description

this.logLevels

A reference to the logbox.system.logging.LogLevels class.

Helper Methods

The abstract appender also has various cool methods that you can use when building appenders:

Properties Methods

Method

Description

struct getProperties()

Get the entire properties struct.

Utility Methods

Layout Methods

void setProperties(struct properties)

Override all properties with a new properties struct.

any getProperty(string property)

Get a property.

void setProperty(string property, any value)

Set a property.

boolean propertyExists(string property)

Checks if a property exists.

Method

Description

boolean isInitialized()

Returns true if the appender has been initialized.

string getName()

Get the name of the appender

string getHash()

Get the appender's unique hash id

string severityToString(numeric severity)

Transforms a severity integer to it's human readable form

Method

Description

any getCustomLayout()

Get the custom layout object if defined.

boolean hasCustomLayout()

Checks if the custom layout object is defined in this appender.

Dealing With Custom Layouts

In order for an appender to deal with custom layouts, you must use the layout methods when preparing to log your messages. Below is a simple example from the console appender of how to do this:

if( hasCustomLayout() ){
  entry = getCustomLayout().format(loge);
}
else{
  entry = "#severityToString(loge.getseverity())# #loge.getCategory()# #loge.getmessage()# ExtraInfo: #loge.getextraInfoAsString()#";
}

// Log message to system.out
instance.out.println(entry);

As you can see, all you need to do is have an if statement that checks whether the appender has a custom layout or not and then assign the return of the layout as your message to log.

Creating Custom Appenders

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.

init()

The signature of the init method is the following:

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:

logMessage()

The signature of the logMessage method is the following:

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

You can then use this logging event object to log to whatever destination you want. Here is a snippet from our scope appender:

onRegistration() & onUnregistration()

Finally, both the onRegistration and onUnregistration methods have to be void methods with no arguments.

These are great for starting or stopping your appenders if they so need to. Here is a sample from our socket appender:

severity
  • extraInfo

  • onUnRegistration()

    An interceptor that fires when the appender is removed from a logger.

    <---  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>
    <---  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>
    <---  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>
    <---  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>
    <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>
    <---  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>