Creating Your Own Alert Rules For BugLogHQ
One very useful feature of BugLogHQ is the 'Rules' feature. These are basically rules or conditions that get evaluated every time a bug report is processed and can be used to provide custom behaviour. Rules may be used for things like alerting you when some special condition happens; For example you can create a rule that will send you an email as soon as you receive a bug report with the words "stack overflow" on it, or to possibly send you an SMS message once the amount of errors on the last X minutes is greater than 100 errors, or well, you get the idea.
In this article I want to go over how rules are implemented and give a brief example of how to create your own rules and add them to your BugLogHQ instance.
BugLogHQ Rules
Rules in BugLog are implemented as ColdFusion Components and are made to be configurable and stackable. That means that you can create multiple instances of the same type of rule, but each with different conditions; and all will be executed one after the other.
All rule CFCs inherit from a base rule (and implicitly implement a particular interface) that provides some basic functionality needed. All configuration options are defined as metadata using the cfproperty tag.
Building a Rule
Since the best way to learn is by example, lets get started creating a simple rule to use as an example. This sample rule will send you an email everytime a bug report that matches a given criteria is received. Our criteria will be Application Name, Host Name and Severity. So, for example, we could use this rule to alert us everytime a FATAL bug on host PRODSERVER1 is received from application ECOMMERCEWEB.
I mentioned before that rules are just CFCs. So we start by creating a cfc and we'll name it "incomingBugAlert.cfc". We will also put some basic metadata to describe our rule:
displayName="Incoming Bug Alert"
hint="This rule checks each incoming bug and fires an email message when a bug that matches a given criteria is received">
</cfcomponent>
For BugLogHQ to recognize this rule we will need to save it on the extensions directory, in /bugLog/extensions/rules/ BugLogHQ automatically scans this folder to check which rule types are available. However the presence of the component does not automatically imply that the rule will be processed and used. We will see later how rules are enabled for use.
Now that we have the basic skeleton, we need to add the configuration options. We do this using cfproperty tags. One for each item that we want out rule to expose as configurable. In our case we have 4 configurable settings:
- Recipient email address
- Application name
- Host name
- Severity type
So, we modify our incomingAlert.cfc as follows:
displayName="Incoming Bug Alert"
hint="This rule checks each incoming bug and fires an email message when a bug that matches a given criteria is received">
<cfproperty name="recipientEmail" type="string" displayName="Recipient Email" hint="The email address to which to send the notifications">
<cfproperty name="applicationCode" type="string" displayName="Application" hint="The application name that will trigger the rule. Leave empty to look for all applications">
<cfproperty name="severityCode" type="string" displayName="Severity Code" hint="The severity code (fatal,critical,error,etc) that will trigger the rule. Leave empty to look for all severity codes">
<cfproperty name="hostName" type="string" displayName="Host Name" hint="The severity code (fatal,critical,error,etc) that will trigger the rule. Leave empty to look for all severity codes">
</cfcomponent>
We need now to make our rule "do" something, since at this point is just a pretty empty shell with configurable options that don't do anything.
For our rule to have some functionality we need to implement two methods. The Init() constructor and the processRule() method.
Each rule that is configured for evaluation translates into an instance of the rule cfc initialized with different parameters. These instances are kept in memory as long as the BugLogListener service is instantiated and listening for incoming bug reports. That means that rules maintain their own state during the lifetime of the listener.
The Init() method is pretty simple, it will be called with the same arguments that we defined on the properties section. Most likely what you will want to do is to store the config settings into some local scope, and maybe define any default values that you wish to use later.
Here is our simple Init() method:
<cfargument name="recipientEmail" type="string" required="true">
<cfargument name="applicationCode" type="string" required="false" default="">
<cfargument name="hostName" type="string" required="false" default="">
<cfargument name="severityCode" type="string" required="false" default="">
<cfset variables.config.recipientEmail = trim(arguments.recipientEmail)>
<cfset variables.config.applicationCode = trim(arguments.applicationCode)>
<cfset variables.config.hostName = trim(arguments.hostName)>
<cfset variables.config.severityCode = trim(arguments.severityCode)>
<cfreturn this>
</cffunction>
We use variables.config as a structure to contain our config settings on a scope available to the entire component.
Finally we need to add the processRule() method. This is where all the magic is going to happen. processRule() is invoked for every bug report that is processed.
<cfargument name="rawEntry" type="bugLog.components.rawEntryBean" required="true">
<cfargument name="dataProvider" type="bugLog.components.lib.dao.dataProvider" required="true">
<cfargument name="configObj" type="bugLog.components.config" required="true">
<cfscript>
var stEntry = arguments.rawEntry.getMemento();
// evaluate conditions
var evalCond1 = (variables.config.application eq ""
or stEntry.applicationCode eq variables.config.applicationCode);
var evalCond2 = (variables.config.severityCode eq ""
or stEntry.severityCode eq variables.config.severityCode);
var evalCond3 = (variables.config.hostName eq ""
or stEntry.hostName eq variables.config.hostName);
// if all conditions are met, then send the alert
if(evalCond1 and evalCond2 and evalCond3) {
sendToEmail(rawEntryBean = arguments.rawEntry,
sender = arguments.configObj.getSetting("general.adminEmail"),
recipient = variables.config.recipientEmail,
subject = "BugLog: #arguments.rawEntry.getMessage()#",
comment = "This message has been sent because the following bug report matched the given criteria.");
}
return true;
</cfscript>
</cffunction>
processRule() receives 3 arguments always:
- rawEntry is an value object that contains the details of the bug report that is being processed.
- dataProvideris an instance of the object used by the DAO layer to talk to the database, just in case you wish to create your own DAOs for lookups or maybe execute direct SQL statements.
- configObj is an instance of the object used to store all the global configuration options. You use this object to access the settings defined in /buglog/buglog-config.xml.cfm
Also you will see that after evaluating the conditions, the code calls a function named sendToEmail(). This function is defined in baseRule and is used to send an email with the details of the bug report that is being processed.
Finally, the method must always return a boolean value. This value tells the rule processor whether to continue evaluating rules or just stop right there. The rule processor will keep evaluating rules in order as long as the return value is TRUE or it runs out of rules to process.
Here is the complete rule cfc code:
displayName="Incoming Bug Alert"
hint="This rule checks each incoming bug and fires an email message when a bug that matches a given criteria is received">
<cfproperty name="recipientEmail" type="string" displayName="Recipient Email" hint="The email address to which to send the notifications">
<cfproperty name="applicationCode" type="string" displayName="Application" hint="The application name that will trigger the rule. Leave empty to look for all applications">
<cfproperty name="severityCode" type="string" displayName="Severity Code" hint="The severity code (fatal,critical,error,etc) that will trigger the rule. Leave empty to look for all severity codes">
<cfproperty name="hostName" type="string" displayName="Host Name" hint="The severity code (fatal,critical,error,etc) that will trigger the rule. Leave empty to look for all severity codes">
<cffunction name="init" access="public" returntype="bugLog.components.baseRule">
<cfargument name="recipientEmail" type="string" required="true">
<cfargument name="applicationCode" type="string" required="false" default="">
<cfargument name="hostName" type="string" required="false" default="">
<cfargument name="severityCode" type="string" required="false" default="">
<cfset variables.config.recipientEmail = trim(arguments.recipientEmail)>
<cfset variables.config.applicationCode = trim(arguments.applicationCode)>
<cfset variables.config.hostName = trim(arguments.hostName)>
<cfset variables.config.severityCode = trim(arguments.severityCode)>
<cfreturn this>
</cffunction>
<cffunction name="processRule" access="public" returnType="boolean">
<cfargument name="rawEntry" type="bugLog.components.rawEntryBean" required="true">
<cfargument name="dataProvider" type="bugLog.components.lib.dao.dataProvider" required="true">
<cfargument name="configObj" type="bugLog.components.config" required="true">
<cfscript>
var stEntry = arguments.rawEntry.getMemento();
// evaluate conditions var evalCond1 = (variables.config.application eq ""
or stEntry.applicationCode eq variables.config.applicationCode);
var evalCond2 = (variables.config.severityCode eq ""
or stEntry.severityCode eq variables.config.severityCode);
var evalCond3 = (variables.config.hostName eq ""
or stEntry.hostName eq variables.config.hostName);
// if all conditions are met, then send the alert if(evalCond1 and evalCond2 and evalCond3) {
sendToEmail(rawEntryBean = arguments.rawEntry,
sender = arguments.configObj.getSetting("general.adminEmail"),
recipient = variables.config.recipientEmail,
subject = "BugLog: #arguments.rawEntry.getMessage()#",
comment = "This message has been sent because the following bug report matched the given criteria.");
}
return true;
</cfscript>
</cffunction>
</cfcomponent>
So that's it. We have now created our new rule. Keep reading to see how we will activate for processing.
Activating a Rule
Once a rule cfc is located in the /extensions/rules directory, we can now use the BugLogHQ UI to activate and configure it.
In the BugLogHQ interface, go into the "Rules" screen.
On the drop down next to "Create a new rule of type", select "incomingAlert". This is the new rule cfc that we added.
You will see now a form with the rule description and the rule properties that we defined before on the CFC. All this is dynamically obtained from the CFC metadata that we created. Fill in all the desired criteria and click "Save"
For the rule to be processed we need to recycle the listener service, so go back to the main Summary screen and click on the Stop link and then on Start. If everything was done properly, the rule will now be loaded in memory.
To test the new rule you will need to submit a bug report with the given properties.
Note also that we can add as many instances of the same rule type as we want, each with its own set of criteria to look out for.
Conclusion
On this example we created a very simple rule, but the same rule mechanism can be used to have any kind of behaviour that you wish to have. You can create rules for sending emails, sending messages via SMS, post to Twitter, call a webservice in your favority ticketing application to automatically create a ticket, whatever you can think of.
BugLogHQ comes with a few predefined rule types that you can explore to see more examples of how they are done, and of course, if you create a rule that you wish to share with other BugLogHQ users, just leave a message on the forum or contact me and we'll make sure to make it available on the bugloghq website.
Have fun!
There are no comments for this entry.
[Add Comment]