Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP - Version 2.0 #153

Open
wants to merge 48 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
dcf14ff
Updated version tag to 2.0.x
oarevalo Sep 25, 2014
165e0e4
Merge branch 'master' into 2.0
oarevalo Oct 10, 2014
3fc5071
Merge branch 'master' into 2.0
oarevalo Oct 13, 2014
911b851
added new listener endpoint; added UUID property
oarevalo Oct 13, 2014
973ab17
Allow view bug report by UUID
oarevalo Oct 13, 2014
9e007c0
use new listener format for test
oarevalo Oct 14, 2014
34d93d5
refactored queue operations to separate CFC
oarevalo Oct 14, 2014
6aacf03
consolidated buglloglistener and bugloglistenerasync into a single co…
oarevalo Oct 14, 2014
2ac828a
Merge branch 'master' into 2.0
oarevalo Oct 17, 2014
4beeba4
Merge branch '2.0' into 14-queue-improvements
oarevalo Oct 17, 2014
1640302
Created an interface to describe the Queue behavior
oarevalo Oct 17, 2014
79b109e
edited comments
oarevalo Oct 17, 2014
3f74c2e
Ported bugloglistener to cfscript
oarevalo Oct 17, 2014
af7c810
Updated comments
oarevalo Oct 17, 2014
73b1089
rewrote entry.cfc as cfscript
oarevalo Oct 20, 2014
adb0aad
Move app/host/msg grouping into main search SQL statement
oarevalo Oct 20, 2014
5750e0c
[FIX] Fixed incorrect exception type on catch
oarevalo Nov 10, 2014
4610671
Removed references to protocol var on client test script
oarevalo Nov 10, 2014
099e922
Merge branch '2.0' into 15-sql-grouping
oarevalo Nov 10, 2014
9216b3a
ensure that group by msg is set on dashboard
oarevalo Nov 10, 2014
4efdb7f
Dashboard uses raw data for its own summaries
oarevalo Nov 10, 2014
686623e
Merge branch 'master' into 2.0
oarevalo Nov 15, 2014
5a2f103
Merge branch '15-sql-grouping' into 2.0
oarevalo Nov 15, 2014
9726877
Merge branch 'master' into 2.0
oarevalo Nov 23, 2014
a5911e4
Merge branch 'master' into 2.0
oarevalo Nov 23, 2014
4c987e6
initial commit. separate rule processing
oarevalo Dec 29, 2014
6efeac2
set existing entries as processed on migration
oarevalo Dec 29, 2014
9728a3e
removed reference to rawentrybean
oarevalo Dec 29, 2014
c4205e2
Merge branch 'master' into 2.0
oarevalo Dec 29, 2014
f303015
Merge branch 'master' into 19-decouple-rule-processing
oarevalo Dec 29, 2014
e15ba57
updated argument expected by doAction internals
oarevalo Dec 29, 2014
4d74b45
fix incorrect variable reference
oarevalo Dec 30, 2014
cd3fb9a
added error handling to procee queue/rules harnesses
oarevalo Dec 30, 2014
d3d732d
bug fixes to existing rules
oarevalo Dec 31, 2014
320bd23
define scope config in base rule constructor
oarevalo Dec 31, 2014
ac6ee77
rewrite as cfscript
oarevalo Dec 31, 2014
81c50d8
revert processing of rules and queue into the same scheduled task, bu…
oarevalo Dec 31, 2014
df0acde
separate stats into its own request
oarevalo Dec 31, 2014
72125c0
more changes
oarevalo Dec 31, 2014
f3cee7c
Merge branch '2.0' into 19-decouple-rule-processing
oarevalo Dec 31, 2014
2dd06ea
moved rule processing logic into ruleProcessor.cfc
oarevalo Dec 31, 2014
f16c209
added missing semicolon
oarevalo Jan 7, 2015
39ea307
replaced wrong var reference
oarevalo Jan 11, 2015
1c27f2f
Fixed sql in migration script
oarevalo Apr 18, 2016
7da7c20
[FIX] fixed bug that caused HTMLReport to dissapear on rule processing
oarevalo Apr 18, 2016
12dbedb
added .DS_Store to gitignore
oarevalo Apr 18, 2016
02bda79
removed old listeners/listener.cfc
oarevalo Mar 28, 2019
d173a7b
allow overriding settings using environment variables
oarevalo Mar 28, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/*~
/deploy/
.DS_Store
51 changes: 51 additions & 0 deletions components/InMemoryQueue.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
component implements="Queue" {

variables.queue = 0;
variables.maxQueueSize = 0;
variables.instanceName = "";

// initialize queue
function init(
required config config,
required string instanceName
) {
variables.queue = CreateObject("java","java.util.ArrayList").init();
variables.maxQueueSize = arguments.config.getSetting("service.maxQueueSize");
variables.instanceName = arguments.instanceName;
return this;
}

// adds an item to the queue
void function add(
required rawEntryBean entryBean
) {
lock name="#lockName()#" type="exclusive" timeout="10" {
if(arrayLen(variables.queue) lte variables.maxQueueSize) {
arrayAppend(variables.queue, arguments.entryBean);
} else {
throw(message="Queue full", type="buglog.queueFull");
}
}
}

// retrieves all elements on the queue and leaves it empty
array function flush() {
var items = [];
lock name="#lockName()#" type="exclusive" timeout="10" {
items = duplicate(variables.queue);
variables.queue = CreateObject("java","java.util.ArrayList").Init()
}
return items;
}

// retrieves all elements on the queue without affecting them
array function getAll() {
return duplicate(variables.queue);
}

// generate a name for the exclusive lock used for reading and flushing a queue
private string function lockName() {
return "buglog_queue_#variables.instanceName#";
}

}
26 changes: 26 additions & 0 deletions components/Queue.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
interface {
// The Queue interface describes the behavior of a queue used to store all
// incoming messages. The queue is read and processed periocally.

// initialize queue
function init(
required config config,
required string instanceName
);

// Adds an item to the queue
// may throw "buglog.queueFull" exception if no item can be added
// to the queue.
void function add(
required rawEntryBean entryBean
);

// Retrieves all elements on the queue and leaves it empty
// Returns an array of rawEntryBean objects
array function flush();

// Retrieves all elements on the queue without affecting them
// Returns an array of rawEntryBean objects
array function getAll();

}
205 changes: 184 additions & 21 deletions components/baseRule.cfc
Original file line number Diff line number Diff line change
@@ -1,41 +1,155 @@
<cfcomponent displayname="baseRule" hint="This is the base component for any rule. A rule is a process that is evaluated each time a bug arrives and can determine if some actions need to be taken, such as sending an alert via email">

<!--- the internal config structure is used to store configuration values
for the current instance of this rule --->
<cfset variables.config = structNew()>
<cfscript>
// the internal config structure is used to store configuration values
// for the current instance of this rule
variables.config = {};

// the scope identifies the major properties for which a rule applies (application/host/severity)
variables.scope = {};

variables.ID_NOT_SET = -9999999;
variables.ID_NOT_FOUND = -9999990;
variables.SCOPE_KEYS = ["application","host","severity"];

</cfscript>

<cffunction name="init" access="public" returntype="baseRule">
<!--- the default behavior is to copy the arguments to the config
but this can be overriden if other type of initialization is needed
--->
<cfset variables.config = duplicate(arguments)>
<cfreturn this>
<cfargument name="severity" type="string" required="false" default="">
<cfargument name="application" type="string" required="false" default="">
<cfargument name="host" type="string" required="false" default="">
<cfscript>
// on buglog 1.8 rules were defined using severityCode instead of severity
arguments.severity = structKeyExists(arguments,"severityCode")
? arguments.severityCode
: arguments.severity;

// arguments to the constructor are used as the Rule configuration
config = duplicate(arguments);

// initialize scope context
for(var key in variables.SCOPE_KEYS) {
if(structKeyExists(config, key)) {
var cfg = trim(config[key]);
if(!len(cfg))
continue;
scope[key] = {
not_in = false,
items = {}
};
if( left(cfg,1) == "-" ) {
cfg = trim(removechars(cfg,1,1));
scope[key]["not_in"] = true;
}
for(var item in listToArray(cfg)) {
scope[key]["items"][trim(item)] = variables.ID_NOT_SET;
}
}
}

return this;
</cfscript>
</cffunction>

<cffunction name="processRule" access="public" returnType="boolean"
hint="This method performs the actual evaluation of the rule. Each rule is evaluated on a rawEntryBean.
hint="This method performs the actual evaluation of the rule. Each rule is evaluated on an EntryBean.
The method returns a boolean value that can be used by the caller to determine if additional rules
need to be evaluated.">
<cfargument name="rawEntry" type="bugLog.components.rawEntryBean" required="true">
<cfargument name="entry" type="bugLog.components.entry" required="true">
<cfscript>
var rtn = true;
updateScope();
if(matchScope(entry) && matchCondition(entry)) {
rtn = doAction(entry);
logTrigger(entry);
}
return rtn;
</cfscript>
</cffunction>



<!---- Hooks --->

<cffunction name="processQueueStart" access="public" returntype="boolean" hint="This method gets called BEFORE each processing of the queue">
<cfargument name="queue" type="array" required="true">
<!--- this method must be implemented by rules that extend the base rule --->
<cfreturn true>
</cffunction>

<cffunction name="processQueueStart" access="public" returntype="boolean" hint="This method gets called BEFORE each processing of the queue (only invoked when using the asynch listener)">
<cffunction name="processQueueEnd" access="public" returntype="boolean" hint="This method gets called AFTER each processing of the queue">
<cfargument name="queue" type="array" required="true">
<!--- this method must be implemented by rules that extend the base rule --->
<cfreturn true>
</cffunction>

<cffunction name="processQueueEnd" access="public" returntype="boolean" hint="This method gets called AFTER each processing of the queue (only invoked when using the asynch listener)">
<cfargument name="queue" type="array" required="true">
<cffunction name="matchCondition" access="public" returntype="boolean" hint="Returns true if the entry bean matches a custom condition">
<cfargument name="entry" type="bugLog.components.entry" required="true">
<!--- this method must be implemented by rules that extend the base rule --->
<cfreturn true />
</cffunction>

<cffunction name="doAction" access="public" returntype="boolean" hint="Performs an action when the entry matches the scope and conditions">
<cfargument name="entry" type="bugLog.components.entry" required="true">
<!--- this method must be implemented by rules that extend the base rule --->
<cfreturn true>
</cffunction>




<!---- Utility Methods --->

<cffunction name="updateScope" access="public" returntype="void">
<cfscript>
// get necessary IDs
for(var key in structKeyArray(scope)) {
var items = scope[key]["items"];
for(var item in items) {
if(items[item] == ID_NOT_SET || items[item] == ID_NOT_FOUND) {
switch(key) {
case "application":
items[item] = getApplicationID();
break;
case "severity":
items[item] = getHostID();
break;
case "host":
items[item] = getSeverityID();
break;
}
}
}
}
</cfscript>
</cffunction>

<cffunction name="matchScope" access="public" returntype="boolean" hint="Returns true if the entry bean matches the defined scope">
<cfargument name="entry" type="bugLog.components.entry" required="true">
<cfscript>
var matches = true;
var memento = entry.getMemento();
for(var key in structKeyArray(scope)) {
var matchesLine = scope[key]["not_in"];
for(var item in scope[key]["items"]) {
var id = scope[key]["items"][item];
if(scope[key]["not_in"]) {
matchesLine = matchesLine && memento[key & "ID"] != id;
} else {
matchesLine = matchesLine || memento[key & "ID"] == id;
}
}
matches = matches && matchesLine;
}
return matches;
</cfscript>
</cffunction>




<cffunction name="sendToEmail" access="public" returntype="void">
<cfargument name="rawEntryBean" type="bugLog.components.rawEntryBean" required="false">
<cfargument name="entry" type="bugLog.components.entry" required="false">
<cfargument name="recipient" type="string" required="true">
<cfargument name="subject" type="string" required="false" default="BugLog: bug received">
<cfargument name="comment" type="string" required="false" default="">
Expand All @@ -49,8 +163,8 @@
var bugReportURL = "";
var body = "";

if(structKeyExists(arguments,"rawEntryBean")) {
stEntry = arguments.rawEntryBean.getMemento();
if(structKeyExists(arguments,"entry")) {
stEntry = arguments.entry.getMemento();
}

if(arguments.recipient eq "") {writeToCFLog("Missing 'recipient' email address. Cannot send alert email!"); return;}
Expand All @@ -72,15 +186,16 @@
<hr />
</cfif>

<cfif structKeyExists(arguments,"rawEntryBean")>
<cfif structKeyExists(arguments,"entry")>
<cfset var createdOn = showDateTime(stEntry.createdOn)>
<table style="font-family:arial;font-size:12px;">
<tr>
<td><b>Message:</b></td>
<td><strong>#stEntry.message#</strong></td>
</tr>
<tr>
<td><b>Date/Time:</b></td>
<td>#showDateTime(stEntry.receivedOn)#</td>
<td>#createdOn#</td>
</tr>
<tr>
<td><b>Application:</b></td>
Expand Down Expand Up @@ -200,7 +315,6 @@
createdOn = now());
</cfscript>
</cffunction>


<cffunction name="getLastTrigger" access="public" returntype="query" hint="Returns a query object with information about the last time this rule was triggered">
<cfset var qry = 0>
Expand All @@ -215,7 +329,17 @@
</cffunction>

<cffunction name="explain" access="public" returntype="string" hint="returns a user friendly description of this rule">
<cfreturn "">
<cfset var rtn = "Matches bug reports ">
<cfif variables.config.application neq "">
<cfset rtn &= " from application <b>#variables.config.application#</b>">
</cfif>
<cfif variables.config.severity neq "">
<cfset rtn &= " with a severity of <b>#variables.config.severity#</b>">
</cfif>
<cfif variables.config.host neq "">
<cfset rtn &= " from host <b>#variables.config.host#</b>">
</cfif>
<cfreturn rtn>
</cffunction>

<cffunction name="showDateTime" returnType="string" access="private" hint="formats a date/time object according to user settings">
Expand All @@ -231,5 +355,44 @@
<cfset rtn = dateFormat(theDateTime, dateMask) & " " & lsTimeFormat(theDateTime)>
<cfreturn rtn>
</cffunction>

<cffunction name="getApplicationID" access="private" returntype="numeric">
<cfset var oDAO = getDAOFactory().getDAO("application")>
<cfset var oFinder = createObject("component","bugLog.components.appFinder").init(oDAO)>
<cfset var o = 0>
<cftry>
<cfset o = oFinder.findByCode(variables.config.application)>
<cfreturn o.getApplicationID()>
<cfcatch type="appFinderException.ApplicationCodeNotFound">
<cfreturn 0>
</cfcatch>
</cftry>
</cffunction>

<cffunction name="getHostID" access="private" returntype="numeric">
<cfset var oDAO = getDAOFactory().getDAO("host")>
<cfset var oFinder = createObject("component","bugLog.components.hostFinder").init(oDAO)>
<cfset var o = 0>
<cftry>
<cfset o = oFinder.findByName(variables.config.host)>
<cfreturn o.getHostID()>
<cfcatch type="hostFinderException.HostNameNotFound">
<cfreturn 0>
</cfcatch>
</cftry>
</cffunction>

<cffunction name="getSeverityID" access="private" returntype="numeric">
<cfset var oDAO = getDAOFactory().getDAO("severity")>
<cfset var oFinder = createObject("component","bugLog.components.severityFinder").init(oDAO)>
<cfset var o = 0>
<cftry>
<cfset o = oFinder.findByCode(variables.config.severity)>
<cfreturn o.getSeverityID()>
<cfcatch type="severityFinderException.codeNotFound">
<cfreturn 0>
</cfcatch>
</cftry>
</cffunction>

</cfcomponent>
Loading