From dcf14ff3800cfdefa7cfe8588c1892a1f2100dcd Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Wed, 24 Sep 2014 21:23:10 -0700 Subject: [PATCH 01/36] Updated version tag to 2.0.x --- hq/config/config.xml.cfm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hq/config/config.xml.cfm b/hq/config/config.xml.cfm index 53dd7b3..e8a6669 100644 --- a/hq/config/config.xml.cfm +++ b/hq/config/config.xml.cfm @@ -3,7 +3,7 @@ - + From 911b8515002296516735e9144137ed6a4849e6b8 Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Sun, 12 Oct 2014 20:51:44 -0700 Subject: [PATCH 02/36] added new listener endpoint; added UUID property --- components/bugLogListener.cfc | 1 + components/db/entryDAO.cfc | 1 + components/entry.cfc | 7 ++- components/entryFinder.cfc | 7 ++- components/listener.cfc | 60 ++++++++++++++++++++ components/rawEntryBean.cfc | 4 +- install/migration/1_8_to_2_0/mysql.sql | 3 + listener/index.cfm | 78 ++++++++++++++++++++++++++ 8 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 components/listener.cfc create mode 100644 install/migration/1_8_to_2_0/mysql.sql create mode 100644 listener/index.cfm diff --git a/components/bugLogListener.cfc b/components/bugLogListener.cfc index 67fae26..3ef545e 100644 --- a/components/bugLogListener.cfc +++ b/components/bugLogListener.cfc @@ -117,6 +117,7 @@ oEntry.setTemplatePath(bean.gettemplate_Path()); oEntry.setHTMLReport(bean.getHTMLReport()); oEntry.setCreatedOn(bean.getReceivedOn()); + oEntry.setUUID(bean.getUUID()); // save entry oEntry.save(); diff --git a/components/db/entryDAO.cfc b/components/db/entryDAO.cfc index 474ef21..b41b104 100644 --- a/components/db/entryDAO.cfc +++ b/components/db/entryDAO.cfc @@ -17,6 +17,7 @@ + diff --git a/components/entry.cfc b/components/entry.cfc index 4aff947..1d26ece 100644 --- a/components/entry.cfc +++ b/components/entry.cfc @@ -17,6 +17,7 @@ variables.instance.templatePath = ""; variables.instance.HTMLReport = ""; variables.instance.createdOn = now(); + variables.instance.UUID = ""; function setEntryID(data) {variables.instance.ID = arguments.data;} function setDateTime(data) {variables.instance.mydateTime = arguments.data;} @@ -33,7 +34,8 @@ function setTemplatePath(data) {variables.instance.templatePath = left(arguments.data,500);} function setHTMLReport(data) {variables.instance.HTMLReport = arguments.data;} function setCreatedOn(data) {variables.instance.createdOn = arguments.data;} - + function setUUID(data) {variables.instance.UUID = arguments.data;} + function getEntryID() {return variables.instance.ID;} function getDateTime() {return variables.instance.mydateTime;} function getMessage() {return variables.instance.message;} @@ -49,6 +51,7 @@ function getTemplate_Path() {return variables.instance.templatePath;} function getHTMLReport() {return variables.instance.HTMLReport;} function getCreatedOn() {return variables.instance.createdOn;} + function getUUID() {return variables.instance.UUID;} function getID() {return variables.instance.ID;} @@ -89,4 +92,4 @@ - \ No newline at end of file + diff --git a/components/entryFinder.cfc b/components/entryFinder.cfc index 1296856..cb188a4 100644 --- a/components/entryFinder.cfc +++ b/components/entryFinder.cfc @@ -23,6 +23,7 @@ o.setTemplatePath(qry.templatePath); o.setHTMLReport(qry.HTMLReport); o.setCreatedOn(qry.createdOn); + o.setUUID(qry.UUID); return o; } else { throw("ID not found","entryFinderException.IDNotFound"); @@ -46,6 +47,7 @@ + @@ -63,7 +65,7 @@ SELECT e.entryID, e.message, e.cfid, e.cftoken, e.mydateTime, e.exceptionMessage, e.exceptionDetails, e.templatePath, e.userAgent, a.code as ApplicationCode, h.hostName, s.code AS SeverityCode, - src.name AS SourceName, e.applicationID, e.hostID, e.severityID, e.sourceID, e.createdOn, + src.name AS SourceName, e.applicationID, e.hostID, e.severityID, e.sourceID, e.createdOn, e.UUID, datePart(year, e.createdOn) as entry_year, @@ -168,6 +170,9 @@ WHERE userID = ) + + AND e.UUID = + ORDER BY e.createdOn DESC, entryID DESC diff --git a/components/listener.cfc b/components/listener.cfc new file mode 100644 index 0000000..1ffa5a5 --- /dev/null +++ b/components/listener.cfc @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + // get listener service wrapper + var serviceLoader = createObject("component", "service").init( instanceName = variables.instanceName ); + + // get handle to bugLogListener service + var bugLogListener = serviceLoader.getService(); + + // create entry bean + var rawEntry = createObject("component","rawEntryBean") + .init() + .setDateTime(arguments.dateTime) + .setMessage(arguments.message) + .setApplicationCode(arguments.applicationCode) + .setSourceName(arguments.source) + .setSeverityCode(arguments.severityCode) + .setHostName(arguments.hostName) + .setExceptionMessage(arguments.exceptionMessage) + .setExceptionDetails(arguments.exceptionDetails) + .setCFID(arguments.cfid) + .setCFTOKEN(arguments.cftoken) + .setUserAgent(arguments.userAgent) + .setTemplatePath(arguments.templatePath) + .setHTMLReport(arguments.HTMLReport) + .setReceivedOn(now()); + + // validate Entry + bugLogListener.validate(rawEntry, arguments.APIKey); + + // log entry + bugLogListener.logEntry(rawEntry); + + return rawEntry; + + + + diff --git a/components/rawEntryBean.cfc b/components/rawEntryBean.cfc index ffe2456..8bac570 100644 --- a/components/rawEntryBean.cfc +++ b/components/rawEntryBean.cfc @@ -17,6 +17,7 @@ variables.instance.HTMLReport = ""; variables.instance.sourceName = ""; variables.instance.receivedOn = now(); + variables.instance.UUID = createUUID(); function setDateTime(data) {variables.instance.dateTime = arguments.data; return this;} function setMessage(data) {variables.instance.message = arguments.data; return this;} @@ -49,6 +50,7 @@ function getHTMLReport() {return variables.instance.HTMLReport;} function getSourceName() {return variables.instance.sourceName;} function getReceivedOn() {return variables.instance.receivedOn;} + function getUUID() {return variables.instance.UUID;} @@ -65,4 +67,4 @@ - \ No newline at end of file + diff --git a/install/migration/1_8_to_2_0/mysql.sql b/install/migration/1_8_to_2_0/mysql.sql new file mode 100644 index 0000000..6ab5c17 --- /dev/null +++ b/install/migration/1_8_to_2_0/mysql.sql @@ -0,0 +1,3 @@ +ALTER TABLE `bl_Entry` +ADD COLUMN `UUID` VARCHAR(50) NULL AFTER `createdOn`, +ADD INDEX `IX_Entry_UUID` (`UUID` ASC); diff --git a/listener/index.cfm b/listener/index.cfm new file mode 100644 index 0000000..b276296 --- /dev/null +++ b/listener/index.cfm @@ -0,0 +1,78 @@ + + + + try { + + instance = ""; + status = { + code = 200, + text = "OK" + }; + response = { + "status" = "OK", + "id" = "" + }; + + // This endpoint must respond to both JSON and regular HTTP requests + requestBody = toString( getHttpRequestData().content ); + if( isJSON(requestBody) ) { + // json is what we got + args = deserializeJSON(requestBody); + } else { + // This is a regular http request, so let's build a + // collection of all incoming URL & Form parameters + args = duplicate(form); + structAppend(args, url); + } + + // at minimum we need a message + if(!structKeyExists(args, "message")) { + throw(type="bugLog.invalidFormat", message="Message field is required"); + } + + // set default values + param name="args.applicationCode" default="Unknown"; + param name="args.dateTime" type="date" default=now(); + param name="args.severityCode" default="ERROR"; + param name="args.hostName" default=cgi.REMOTE_ADDR; + + // log how we got this report + args.source = ucase(cgi.request_method); + + // See if we this is a named instance of buglog + if( structKeyExists(request,"bugLogInstance") and len(request.bugLogInstance) ) { + instance = request.bugLogInstance; + } + + // log entry + listener = createObject("component","bugLog.components.listener").init( instance ); + rawEntryBean = listener.logEntry( argumentCollection = args ); + response.id = rawEntryBean.getUUID(); + + + // Handle exceptions + } catch("bugLog.invalidFormat" e) { + status = {code = 400, text = "Bad Request"}; + response["status"] = "Error"; + response["error"] = e.message; + + } catch("bugLog.invalidAPIKey" e) { + status = {code = 401, text = "Unauthorized"}; + response["status"] = "Error"; + response["error"] = e.message; + + } catch("applicationNotAllowed" e) { + status = {code = 403, text = "Forbidden"}; + response["status"] = "Error"; + response["error"] = e.message; + + } catch(any e) { + status = {code = 500, text = "Server Error"}; + response["status"] = "Error"; + response["error"] = e.message; + } + + + + +#serializeJson(response)# From 973ab1743bb03a6ec4c509fdc0dc388a75169b26 Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Sun, 12 Oct 2014 22:31:43 -0700 Subject: [PATCH 03/36] Allow view bug report by UUID --- components/hq/appService.cfc | 11 ++++++++++- hq/handlers/general.cfc | 12 ++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/components/hq/appService.cfc b/components/hq/appService.cfc index f177433..69b9ab0 100644 --- a/components/hq/appService.cfc +++ b/components/hq/appService.cfc @@ -189,7 +189,16 @@ return entry; - + + + + + + var qryEntry = variables.oEntryDAO.search( uuid = arguments.uuid ); + return getEntry(qryEntry.entryID, arguments.user); + + + diff --git a/hq/handlers/general.cfc b/hq/handlers/general.cfc index 97a0206..91100de 100644 --- a/hq/handlers/general.cfc +++ b/hq/handlers/general.cfc @@ -253,18 +253,22 @@ try { var appService = getService("app"); var entryID = getValue("entryID"); + var entryUUID = getValue("uuid"); var argsSearch = {}; var qryEntriesUA = queryNew(""); var currentUser = getValue("currentUser"); + var oEntry = 0; - if(val(entryID) eq 0) { + // get requested entry object + if(entryID gt 0) { + oEntry = appService.getEntry(entryID, currentUser); + } else if(len(entryUUID)) { + oEntry = appService.getEntryByUUID(entryUUID, currentUser); + } else { setMessage("warning","Please select an entry to view"); setNextEvent("main"); } - // get requested entry object - oEntry = appService.getEntry(entryID, currentUser); - // search for recent ocurrences (last 24 hours) args.message = "__EMPTY__"; args.startDate = dateAdd("d", -1, now()); From 9e007c0667306bdf822f8aebca176478f78b8faf Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Tue, 14 Oct 2014 00:34:20 -0700 Subject: [PATCH 04/36] use new listener format for test --- test/client.cfm | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/test/client.cfm b/test/client.cfm index 57733ca..037a9c4 100644 --- a/test/client.cfm +++ b/test/client.cfm @@ -8,7 +8,6 @@ severity: type of error to send. Values are ERROR, FATAL, CRITICAL and INFO ---> - @@ -22,15 +21,12 @@ - - - + - - + @@ -57,12 +53,11 @@ Creating client instance...
- ... Listener type is: #protocol#
- ... Listener is: #bugLogListener[protocol]#
+ ... Listener URL: #bugLogListener#
... Listener API Key is: #apikey#
- ... Adding a checkpoint @@ -96,11 +91,8 @@

- Send test bug report via: - - #key# -  |  - + Test Again +  | 

From 34d93d5533b15116124eaa3ad82664119b332bcc Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Tue, 14 Oct 2014 00:34:59 -0700 Subject: [PATCH 05/36] refactored queue operations to separate CFC --- components/InMemoryQueue.cfc | 51 ++++++++++++++++++++++++ components/bugLogListenerAsync.cfc | 63 +++++++++++++----------------- config/buglog-config.xml.cfm | 1 + 3 files changed, 79 insertions(+), 36 deletions(-) create mode 100644 components/InMemoryQueue.cfc diff --git a/components/InMemoryQueue.cfc b/components/InMemoryQueue.cfc new file mode 100644 index 0000000..5b618fb --- /dev/null +++ b/components/InMemoryQueue.cfc @@ -0,0 +1,51 @@ +component { + + 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#"; + } + +} diff --git a/components/bugLogListenerAsync.cfc b/components/bugLogListenerAsync.cfc index 41a41fd..afa228d 100644 --- a/components/bugLogListenerAsync.cfc +++ b/components/bugLogListenerAsync.cfc @@ -1,8 +1,6 @@ - - - + @@ -11,10 +9,9 @@ // initialize variables and read settings - //variables.queue = arrayNew(1); - variables.queue = CreateObject("java","java.util.ArrayList").Init(); + var queueClass = arguments.config.getSetting("service.queueClass"); + variables.queue = createObject("component",queueClass).init(arguments.config, arguments.instanceName); variables.msgLog = arrayNew(1); - variables.maxQueueSize = arguments.config.getSetting("service.maxQueueSize"); variables.schedulerIntervalSecs = arguments.config.getSetting("service.schedulerIntervalSecs"); // do the normal initialization @@ -29,18 +26,16 @@ - - - - + + + - - + + - @@ -51,27 +46,23 @@ - - - ---> - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + @@ -108,11 +99,11 @@ - + - \ No newline at end of file + diff --git a/config/buglog-config.xml.cfm b/config/buglog-config.xml.cfm index e7593ae..2cc44f7 100644 --- a/config/buglog-config.xml.cfm +++ b/config/buglog-config.xml.cfm @@ -23,6 +23,7 @@ bugLog.components.bugLogListenerAsync + bugLog.components.InMemoryQueue true false 2CF20630-DD24-491F-BA44314842183AFC From 6aacf03573e7ae85030a204050fd658b67bf69ce Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Tue, 14 Oct 2014 01:31:05 -0700 Subject: [PATCH 06/36] consolidated buglloglistener and bugloglistenerasync into a single component --- components/bugLogListener.cfc | 91 +++++++++++++++++++++++- components/bugLogListenerAsync.cfc | 109 ----------------------------- config/buglog-config.xml.cfm | 2 +- 3 files changed, 90 insertions(+), 112 deletions(-) delete mode 100644 components/bugLogListenerAsync.cfc diff --git a/components/bugLogListener.cfc b/components/bugLogListener.cfc index 3ef545e..dbd6d7c 100644 --- a/components/bugLogListener.cfc +++ b/components/bugLogListener.cfc @@ -18,6 +18,9 @@ + + + @@ -62,6 +65,9 @@ // load the mailer service variables.mailerService = createObject("component","bugLog.components.MailerService").init( variables.oConfig ); + // configure the incoming queue + configureQueue(); + // configure history purging configureHistoryPurging(); @@ -77,7 +83,17 @@ - + + + + + + + + + + + @@ -128,12 +144,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + @@ -213,6 +275,15 @@ + + + + + + + + + @@ -455,4 +526,20 @@ + + + // setup queueing service + var queueClass = oConfig.getSetting("service.queueClass"); + variables.queue = createObject("component",queueClass).init(oConfig, instanceName); + + // create a task to process the queue periodically + var schedulerIntervalSecs = oConfig.getSetting("service.schedulerIntervalSecs"); + scheduler.setupTask("bugLogProcessQueue", + "util/processQueue.cfm", + "00:00", + schedulerIntervalSecs, + [{name="key",value=variables.KEY}]); + + + diff --git a/components/bugLogListenerAsync.cfc b/components/bugLogListenerAsync.cfc deleted file mode 100644 index afa228d..0000000 --- a/components/bugLogListenerAsync.cfc +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - - - - - - // initialize variables and read settings - var queueClass = arguments.config.getSetting("service.queueClass"); - variables.queue = createObject("component",queueClass).init(arguments.config, arguments.instanceName); - variables.msgLog = arrayNew(1); - variables.schedulerIntervalSecs = arguments.config.getSetting("service.schedulerIntervalSecs"); - - // do the normal initialization - super.init( arguments.config, arguments.instanceName ); - - // start scheduler - startScheduler(); - - return this; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/config/buglog-config.xml.cfm b/config/buglog-config.xml.cfm index 2cc44f7..b52998f 100644 --- a/config/buglog-config.xml.cfm +++ b/config/buglog-config.xml.cfm @@ -22,7 +22,7 @@ - bugLog.components.bugLogListenerAsync + bugLog.components.bugLogListener bugLog.components.InMemoryQueue true false From 16403022f142965bd23ac72197ba1b6869dc64de Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Thu, 16 Oct 2014 18:08:55 -0700 Subject: [PATCH 07/36] Created an interface to describe the Queue behavior --- components/InMemoryQueue.cfc | 2 +- components/Queue.cfc | 22 +++++++ components/bugLogListenerRemote.cfc | 91 ----------------------------- 3 files changed, 23 insertions(+), 92 deletions(-) create mode 100644 components/Queue.cfc delete mode 100644 components/bugLogListenerRemote.cfc diff --git a/components/InMemoryQueue.cfc b/components/InMemoryQueue.cfc index 5b618fb..ee202d4 100644 --- a/components/InMemoryQueue.cfc +++ b/components/InMemoryQueue.cfc @@ -1,4 +1,4 @@ -component { +component implements="Queue" { variables.queue = 0; variables.maxQueueSize = 0; diff --git a/components/Queue.cfc b/components/Queue.cfc new file mode 100644 index 0000000..49e5039 --- /dev/null +++ b/components/Queue.cfc @@ -0,0 +1,22 @@ +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 + void function add( + required rawEntryBean entryBean + ); + + // retrieves all elements on the queue and leaves it empty + array function flush(); + + // retrieves all elements on the queue without affecting them + array function getAll(); + +} diff --git a/components/bugLogListenerRemote.cfc b/components/bugLogListenerRemote.cfc deleted file mode 100644 index e39a994..0000000 --- a/components/bugLogListenerRemote.cfc +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From 79b109e4d8c3fb4e74e16275a8a729120fdff7ac Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Thu, 16 Oct 2014 20:30:02 -0700 Subject: [PATCH 08/36] edited comments --- components/Queue.cfc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/components/Queue.cfc b/components/Queue.cfc index 49e5039..aa6cb47 100644 --- a/components/Queue.cfc +++ b/components/Queue.cfc @@ -8,15 +8,17 @@ interface { required string instanceName ); - // adds an item to the queue + // 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 + // Retrieves all elements on the queue and leaves it empty array function flush(); - // retrieves all elements on the queue without affecting them + // Retrieves all elements on the queue without affecting them array function getAll(); } From 3f74c2ee069bc38b132e88c808ba7273772024d1 Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Thu, 16 Oct 2014 20:31:32 -0700 Subject: [PATCH 09/36] Ported bugloglistener to cfscript --- components/bugLogListener.cfc | 974 +++++++++++++++++----------------- 1 file changed, 482 insertions(+), 492 deletions(-) diff --git a/components/bugLogListener.cfc b/components/bugLogListener.cfc index 71aaaff..9c6f8e0 100644 --- a/components/bugLogListener.cfc +++ b/components/bugLogListener.cfc @@ -1,545 +1,535 @@ - +component output="false" { - - - - - - - - - - - - - - - - - - - var cacheTTL = 300; // timeout in minutes for cache entries - - variables.oConfig = arguments.config; // global configuration - variables.instanceName = arguments.instanceName; - - logMessage("Starting BugLogListener (#instanceName#) service..."); - - // load settings - variables.maxLogSize = arguments.config.getSetting("service.maxLogSize"); - - // load DAO Factory - variables.oDAOFactory = createObject("component","bugLog.components.DAOFactory").init( variables.oConfig ); - - // load the finder objects - variables.oAppFinder = createObject("component","bugLog.components.appFinder").init( variables.oDAOFactory.getDAO("application") ); - variables.oHostFinder = createObject("component","bugLog.components.hostFinder").init( variables.oDAOFactory.getDAO("host") ); - variables.oSeverityFinder = createObject("component","bugLog.components.severityFinder").init( variables.oDAOFactory.getDAO("severity") ); - variables.oSourceFinder = createObject("component","bugLog.components.sourceFinder").init( variables.oDAOFactory.getDAO("source") ); - variables.oUserFinder = createObject("component","bugLog.components.userFinder").init( variables.oDAOFactory.getDAO("user") ); - - // load the rule processor - variables.oRuleProcessor = createObject("component","bugLog.components.ruleProcessor").init(); - - // create cache instances - variables.oAppCache = createObject("component","bugLog.components.lib.cache.cacheService").init(50, cacheTTL, false); - variables.oHostCache = createObject("component","bugLog.components.lib.cache.cacheService").init(50, cacheTTL, false); - variables.oSeverityCache = createObject("component","bugLog.components.lib.cache.cacheService").init(10, cacheTTL, false); - variables.oSourceCache = createObject("component","bugLog.components.lib.cache.cacheService").init(5, cacheTTL, false); - variables.oUserCache = createObject("component","bugLog.components.lib.cache.cacheService").init(50, cacheTTL, false); - - // load scheduler - variables.scheduler = createObject("component","bugLog.components.schedulerService").init( variables.oConfig, variables.instanceName ); - - // load the mailer service - variables.mailerService = createObject("component","bugLog.components.MailerService").init( variables.oConfig ); - - // configure the incoming queue - configureQueue(); - - // load rules - loadRules(); - - // configure history purging - configureHistoryPurging(); - - // configure the digest sender - configureDigest(); - - // record the date at which the service started - variables.startedOn = Now(); - - logMessage("BugLogListener Service (#instanceName#) Started"); - - - - - - - - - - - - - - - - - - - - var bean = arguments.entryBean; - var oEntry = 0; - var oApp = 0; - var oHost = 0; - var oSeverity = 0; - var oSource = 0; - var oDF = variables.oDAOFactory; - - // get autocreate settings - var autoCreateApp = allowAutoCreate("application"); - var autoCreateHost = allowAutoCreate("host"); - var autoCreateSeverity = allowAutoCreate("severity"); - var autoCreateSource = allowAutoCreate("source"); - - // extract related objects from bean - oApp = getApplicationFromBean( bean, autoCreateApp ); - oHost = getHostFromBean( bean, autoCreateHost ); - oSeverity = getSeverityFromBean( bean, autoCreateSeverity ); - oSource = getSourceFromBean( bean, autoCreateSource ); - - // create entry - oEntry = createObject("component","bugLog.components.entry").init( oDF.getDAO("entry") ); - oEntry.setDateTime(bean.getdateTime()); - oEntry.setMessage(bean.getmessage()); - oEntry.setApplicationID(oApp.getApplicationID()); - oEntry.setSourceID(oSource.getSourceID()); - oEntry.setSeverityID(oSeverity.getSeverityID()); - oEntry.setHostID(oHost.getHostID()); - oEntry.setExceptionMessage(bean.getexceptionMessage()); - oEntry.setExceptionDetails(bean.getexceptionDetails()); - oEntry.setCFID(bean.getcfid()); - oEntry.setCFTOKEN(bean.getcftoken()); - oEntry.setUserAgent(bean.getuserAgent()); - oEntry.setTemplatePath(bean.gettemplate_Path()); - oEntry.setHTMLReport(bean.getHTMLReport()); - oEntry.setCreatedOn(bean.getReceivedOn()); - oEntry.setUUID(bean.getUUID()); - - // save entry - oEntry.save(); - - // process rules - variables.oRuleProcessor.processRules(bean, oEntry); - - - - - - - - - - + */ + + variables.startedOn = 0; + variables.oDAOFactory = 0; + variables.oRuleProcessor = 0; + variables.oConfig = 0; + variables.msgLog = arrayNew(1); + variables.maxLogSize = 10; + variables.instanceName = ""; + variables.autoCreateDefault = true; + variables.queue = 0; + variables.schedulerIntervalSecs = 0; + + // this is a simple protection to avoid calling processqueue() too easily. This is NOT the apiKey setting + variables.key = "123456knowthybugs654321"; + + // timeout in minutes for cache entries + variables.CACHE_TTL = 300; + + + // Constructor + bugLogListener function init( + required config config, + required string instanceName + ) { + + variables.oConfig = arguments.config; // global configuration + variables.instanceName = arguments.instanceName; + + logMessage("Starting BugLogListener (#instanceName#) service..."); + + // load settings + variables.maxLogSize = arguments.config.getSetting("service.maxLogSize"); + + // load DAO Factory + variables.oDAOFactory = createObject("component","bugLog.components.DAOFactory").init( variables.oConfig ); + + // load the finder objects + variables.oAppFinder = createObject("component","bugLog.components.appFinder").init( variables.oDAOFactory.getDAO("application") ); + variables.oHostFinder = createObject("component","bugLog.components.hostFinder").init( variables.oDAOFactory.getDAO("host") ); + variables.oSeverityFinder = createObject("component","bugLog.components.severityFinder").init( variables.oDAOFactory.getDAO("severity") ); + variables.oSourceFinder = createObject("component","bugLog.components.sourceFinder").init( variables.oDAOFactory.getDAO("source") ); + variables.oUserFinder = createObject("component","bugLog.components.userFinder").init( variables.oDAOFactory.getDAO("user") ); + + // load the rule processor + variables.oRuleProcessor = createObject("component","bugLog.components.ruleProcessor").init(); + + // create cache instances + variables.oAppCache = createObject("component","bugLog.components.lib.cache.cacheService").init(50, variables.CACHE_TTL, false); + variables.oHostCache = createObject("component","bugLog.components.lib.cache.cacheService").init(50, variables.CACHE_TTL, false); + variables.oSeverityCache = createObject("component","bugLog.components.lib.cache.cacheService").init(10, variables.CACHE_TTL, false); + variables.oSourceCache = createObject("component","bugLog.components.lib.cache.cacheService").init(5, variables.CACHE_TTL, false); + variables.oUserCache = createObject("component","bugLog.components.lib.cache.cacheService").init(50, variables.CACHE_TTL, false); + + // load scheduler + variables.scheduler = createObject("component","bugLog.components.schedulerService").init( variables.oConfig, variables.instanceName ); + + // load the mailer service + variables.mailerService = createObject("component","bugLog.components.MailerService").init( variables.oConfig ); + + // configure the incoming queue + configureQueue(); + + // load rules + loadRules(); + + // configure history purging + configureHistoryPurging(); + + // configure the digest sender + configureDigest(); + + // record the date at which the service started + variables.startedOn = Now(); + + logMessage("BugLogListener Service (#instanceName#) Started"); + + return this; + } + + // This method adds a bug report entry to the incoming queue. Bug reports must be passed as RawEntryBeans + void function logEntry( + required rawEntryBean entryBean + ) { + try { + variables.queue.add( arguments.entryBean ); + } catch(bugLog.queueFull) { + logMessage("Queue full! Discarding entry."); + } + } + + // This method adds a bug report entry into BugLog. Bug reports must be passed as RawEntryBeans + void function addEntry( + required rawEntryBean entryBean + ) { + var bean = arguments.entryBean; + var oDF = variables.oDAOFactory; + + // get autocreate settings + var autoCreateApp = allowAutoCreate("application"); + var autoCreateHost = allowAutoCreate("host"); + var autoCreateSeverity = allowAutoCreate("severity"); + var autoCreateSource = allowAutoCreate("source"); + + // extract related objects from bean + var oApp = getApplicationFromBean( bean, autoCreateApp ); + var oHost = getHostFromBean( bean, autoCreateHost ); + var oSeverity = getSeverityFromBean( bean, autoCreateSeverity ); + var oSource = getSourceFromBean( bean, autoCreateSource ); + + // create entry + var oEntry = createObject("component","bugLog.components.entry").init( oDF.getDAO("entry") ); + oEntry.setDateTime(bean.getdateTime()); + oEntry.setMessage(bean.getmessage()); + oEntry.setApplicationID(oApp.getApplicationID()); + oEntry.setSourceID(oSource.getSourceID()); + oEntry.setSeverityID(oSeverity.getSeverityID()); + oEntry.setHostID(oHost.getHostID()); + oEntry.setExceptionMessage(bean.getexceptionMessage()); + oEntry.setExceptionDetails(bean.getexceptionDetails()); + oEntry.setCFID(bean.getcfid()); + oEntry.setCFTOKEN(bean.getcftoken()); + oEntry.setUserAgent(bean.getuserAgent()); + oEntry.setTemplatePath(bean.gettemplate_Path()); + oEntry.setHTMLReport(bean.getHTMLReport()); + oEntry.setCreatedOn(bean.getReceivedOn()); + oEntry.setUUID(bean.getUUID()); + + // save entry + oEntry.save(); + + // process rules + variables.oRuleProcessor.processRules(bean, oEntry); + } + + // Processes all entries on the queue + numeric function processQueue( + required string key + ) { + var i = 0; + var errorQueue = arrayNew(1); + var count = 0; + var dp = variables.oDAOFactory.getDataProvider(); - - - - + if(arguments.key neq variables.key) { + logMessage("Invalid key received. Exiting."); + return 0; + } - - - - - - - - - - - - - - - - - + // get a snapshot of the queue as of right now + var myQueue = variables.queue.flush(); + variables.oRuleProcessor.processQueueStart(myQueue, dp, variables.oConfig ); + if(arrayLen(myQueue) gt 0) { + logMessage("Processing queue. Queue size: #arrayLen(myQueue)#"); + for(var i=1;i lte arrayLen(myQueue);i++) { + try { + addEntry(myQueue[i]); + count++; + } catch(any e) { + // log error and save entry in another queue + arrayAppend(errorQueue,myQueue[i]); + logMessage("ERROR: #cfcatch.message# #cfcatch.detail#. Original message in entry: '#myQueue[i].getMessage()#'"); + } + } + } + variables.oRuleProcessor.processQueueEnd(myQueue, dp, variables.oConfig ); - - - - - - - - - - + // add back all entries on the error queue to the main queue + if(arrayLen(errorQueue)) { + for(var i=1;i lte arrayLen(errorQueue);i++) { + logEntry(errorQueue[i]); + } + logMessage("Failed to add #arrayLen(errorQueue)# entries. Returned failed entries to main queue. To clear up queue and discard this entries reset the listener."); + } + + return count; + } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // validate API - if(getConfig().getSetting("service.requireAPIKey",false)) { - if(arguments.apiKey eq "") { - throw(message="Invalid API Key",type="bugLog.invalidAPIKey"); - } - var masterKey = getConfig().getSetting("service.APIKey"); - if(arguments.apiKey neq masterKey) { - var user = getUserByAPIKey(arguments.apiKey); - if(!user.getIsAdmin() and arrayLen(user.getAllowedApplications())) { - // key is good, but since the user is a non-admin - // we need to validate the user can post bugs to the requested - // application. - var app = getApplicationFromBean( entryBean, false ); - if(!user.isApplicationAllowed(app)) { - throw(message="Application not allowed",type="applicationNotAllowed"); - } + // Performs any clean up action required + void function shutDown() { + logMessage("Stopping BugLogListener (#instanceName#) service..."); + logMessage("Stopping ProcessQueue scheduled task..."); + scheduler.removeTask("bugLogProcessQueue"); + logMessage("Processing remaining elements in queue..."); + processQueue(variables.key); + logMessage("BugLogListener service (#instanceName#) stopped."); + } + + // this method appends an entry to the messages log as well as displays the message on the console + void function logMessage( + required string msg + ) { + var System = CreateObject('java','java.lang.System'); + var txt = timeFormat(now(), 'HH:mm:ss') & ": " & msg; + System.out.println("BugLogListener: " & txt); + lock name="bugLogListener_logMessage" type="exclusive" timeout="10" { + if(arrayLen(variables.msgLog) gt variables.maxLogSize) { + arrayDeleteAt(variables.msgLog, ArrayLen(variables.msgLog)); + } + arrayPrepend(variables.msgLog,txt); + } + } + + // Validates that a bug report is valid, if not throws an error. + // This validates API key (if needed) and auto-create settings + boolean function validate( + required rawEntryBean entryBean, + required string apiKey + ) { + // validate API + if(getConfig().getSetting("service.requireAPIKey",false)) { + if(arguments.apiKey eq "") { + throw(message="Invalid API Key", type="bugLog.invalidAPIKey"); + } + var masterKey = getConfig().getSetting("service.APIKey"); + if(arguments.apiKey neq masterKey) { + var user = getUserByAPIKey(arguments.apiKey); + if(!user.getIsAdmin() and arrayLen(user.getAllowedApplications())) { + // key is good, but since the user is a non-admin + // we need to validate the user can post bugs to the requested + // application. + var app = getApplicationFromBean( entryBean, false ); + if(!user.isApplicationAllowed(app)) { + throw(message="Application not allowed",type="applicationNotAllowed"); } } } - - // validate application - if(!allowAutoCreate("application")) { - getApplicationFromBean( entryBean, false ); + } + + // validate application + if(!allowAutoCreate("application")) { + getApplicationFromBean( entryBean, false ); + } + + // validate host + if(!allowAutoCreate("host")) { + getHostFromBean( entryBean, false ); + } + + // validte severity + if(!allowAutoCreate("severity")) { + getSeverityFromBean( entryBean, false ); + } + + // validate source + if(!allowAutoCreate("source")) { + getSourceFromBean( entryBean, false ); + } + + return true; + } + + // Reloads all rules + void function reloadRules() { + loadRules(); + } + + // Return the contents of the queue for inspection + array function getEntryQueue() { + return variables.queue.getAll(); + } + + // Getter functions + config function getConfig() {return variables.oConfig;} + string function getInstanceName() {return variables.instanceName;} + array function getMessageLog() {return variables.msgLog;} + string function getKey() {return variables.key;} + date function getStartedOn() {return variables.startedOn;} + + + + /***** Private Methods ******/ + + // Uses the information on the rawEntryBean to retrieve the corresponding Application object + private app function getApplicationFromBean( + required rawEntryBean entryBean, + boolean createIfNeeded = false + ) { + var bean = arguments.entryBean; + var oApp = 0; + var oDF = variables.oDAOFactory; + + var key = bean.getApplicationCode(); + try { + // first we try to get it from the cache + oApp = variables.oAppCache.retrieve( key ); + + } catch(cacheService.itemNotFound e) { + // entry not in cache, so we get it from DB + try { + oApp = variables.oAppFinder.findByCode( key ); + + } catch(appFinderException.ApplicationCodeNotFound e) { + // code does not exist, so we need to create it (if autocreate enabled) + if(!arguments.createIfNeeded) throw(message="Invalid Application",type="invalidApplication"); + oApp = createObject("component","bugLog.components.app").init( oDF.getDAO("application") ); + oApp.setCode( key ); + oApp.setName( key ); + oApp.save(); } - // validate host - if(!allowAutoCreate("host")) { - getHostFromBean( entryBean, false ); - } + // store entry in cache + variables.oAppCache.store( key, oApp ); + } - // validte severity - if(!allowAutoCreate("severity")) { - getSeverityFromBean( entryBean, false ); - } + return oApp; + } - // validate source - if(!allowAutoCreate("source")) { - getSourceFromBean( entryBean, false ); - } + // Uses the information on the rawEntryBean to retrieve the corresponding Host object + private host function getHostFromBean( + required rawEntryBean entryBean, + boolean createIfNeeded = false + ) { + var bean = arguments.entryBean; + var oHost = 0; + var oDF = variables.oDAOFactory; - return true; - - + var key = bean.getHostName(); - - - + try { + // first we try to get it from the cache + oHost = variables.oHostCache.retrieve( key ); - - - + } catch(cacheService.itemNotFound e) { + // entry not in cache, so we get it from DB + try { + oHost = variables.oHostFinder.findByName( key ); + + } catch(hostFinderException.HostNameNotFound e) { + // code does not exist, so we need to create it (if autocreate enabled) + if(!arguments.createIfNeeded) throw(message="Invalid Host",type="invalidHost"); + oHost = createObject("component","bugLog.components.host").init( oDF.getDAO("host") ); + oHost.setHostName(key); + oHost.save(); + } - - - + // store entry in cache + variables.oHostCache.store( key, oHost ); + } - - - + return oHost; + } + // Uses the information on the rawEntryBean to retrieve the corresponding Severity object + private severity function getSeverityFromBean( + required rawEntryBean entryBean, + boolean createIfNeeded = false + ) { + var bean = arguments.entryBean; + var oSeverity = 0; + var oDF = variables.oDAOFactory; - + var key = bean.getSeverityCode(); - - - - - var key = ""; - var bean = arguments.entryBean; - var oApp = 0; - var oDF = variables.oDAOFactory; + try { + // first we try to get it from the cache + oSeverity = variables.oSeverityCache.retrieve( key ); - key = bean.getApplicationCode(); + } catch(cacheService.itemNotFound e) { + // entry not in cache, so we get it from DB try { - // first we try to get it from the cache - oApp = variables.oAppCache.retrieve( key ); + oSeverity = variables.oSeverityFinder.findByCode( key ); + + } catch(severityFinderException.codeNotFound e) { + // code does not exist, so we need to create it (if autocreate enabled) + if(!arguments.createIfNeeded) throw(message="Invalid Severity",type="invalidSeverity"); + oSeverity = createObject("component","bugLog.components.severity").init( oDF.getDAO("severity") ); + oSeverity.setCode( key ); + oSeverity.setName( key ); + oSeverity.save(); + } - } catch(cacheService.itemNotFound e) { - // entry not in cache, so we get it from DB - try { - oApp = variables.oAppFinder.findByCode( key ); - - } catch(appFinderException.ApplicationCodeNotFound e) { - // code does not exist, so we need to create it (if autocreate enabled) - if(!arguments.createIfNeeded) throw(message="Invalid Application",type="invalidApplication"); - oApp = createObject("component","bugLog.components.app").init( oDF.getDAO("application") ); - oApp.setCode( key ); - oApp.setName( key ); - oApp.save(); - } + // store entry in cache + variables.oSeverityCache.store( key, oSeverity ); + } - // store entry in cache - variables.oAppCache.store( key, oApp ); - } - - - + return oSeverity; + } - - - - - var key = ""; - var bean = arguments.entryBean; - var oHost = 0; - var oDF = variables.oDAOFactory; + // Finds a user object using by its API Key + private user function getUserByAPIKey( + required string apiKey + ) { + var oUser = 0; - key = bean.getHostName(); + try { + // first we try to get it from the cache + oUser = variables.oUserCache.retrieve( apiKey ); + } catch(cacheService.itemNotFound e) { + // entry not in cache, so we get it from DB try { - // first we try to get it from the cache - oHost = variables.oHostCache.retrieve( key ); + oUser = variables.oUserFinder.findByAPIKey( apiKey ); - } catch(cacheService.itemNotFound e) { - // entry not in cache, so we get it from DB - try { - oHost = variables.oHostFinder.findByName( key ); - - } catch(hostFinderException.HostNameNotFound e) { - // code does not exist, so we need to create it (if autocreate enabled) - if(!arguments.createIfNeeded) throw(message="Invalid Host",type="invalidHost"); - oHost = createObject("component","bugLog.components.host").init( oDF.getDAO("host") ); - oHost.setHostName(key); - oHost.save(); - } + var qryUserApps = oDAOFactory.getDAO("userApplication").search(userID = oUser.getUserID()); + var apps = oAppFinder.findByIDList(valueList(qryUserApps.applicationID)); + oUser.setAllowedApplications(apps); - // store entry in cache - variables.oHostCache.store( key, oHost ); + } catch(sourceFinderException.usernameNotFound e) { + // code does not exist, so we need to create it (if autocreate enabled) + throw(message="Invalid API Key",type="bugLog.invalidAPIKey"); } - - - - - - - - var key = ""; - var bean = arguments.entryBean; - var oSeverity = 0; - var oDF = variables.oDAOFactory; + // store entry in cache + variables.oUserCache.store( key, oUser ); + } - key = bean.getSeverityCode(); + return oUser; + } - try { - // first we try to get it from the cache - oSeverity = variables.oSeverityCache.retrieve( key ); - - } catch(cacheService.itemNotFound e) { - // entry not in cache, so we get it from DB - try { - oSeverity = variables.oSeverityFinder.findByCode( key ); - - } catch(severityFinderException.codeNotFound e) { - // code does not exist, so we need to create it (if autocreate enabled) - if(!arguments.createIfNeeded) throw(message="Invalid Severity",type="invalidSeverity"); - oSeverity = createObject("component","bugLog.components.severity").init( oDF.getDAO("severity") ); - oSeverity.setCode( key ); - oSeverity.setName( key ); - oSeverity.save(); - } + // Uses the information on the rawEntryBean to retrieve the corresponding Source object + private source function getSourceFromBean( + required rawEntryBean entryBean, + boolean createIfNeeded = false + ) { + var bean = arguments.entryBean; + var oSource = 0; + var oDF = variables.oDAOFactory; - // store entry in cache - variables.oSeverityCache.store( key, oSeverity ); - } - - - + var key = bean.getSourceName(); - - - - var oUser = 0; + try { + // first we try to get it from the cache + oSource = variables.oSourceCache.retrieve( key ); + } catch(cacheService.itemNotFound e) { + // entry not in cache, so we get it from DB try { - // first we try to get it from the cache - oUser = variables.oUserCache.retrieve( apiKey ); - - } catch(cacheService.itemNotFound e) { - // entry not in cache, so we get it from DB - try { - oUser = variables.oUserFinder.findByAPIKey( apiKey ); - - var qryUserApps = oDAOFactory.getDAO("userApplication").search(userID = oUser.getUserID()); - var apps = oAppFinder.findByIDList(valueList(qryUserApps.applicationID)); - oUser.setAllowedApplications(apps); + oSource = variables.oSourceFinder.findByName( key ); + + } catch(sourceFinderException.codeNotFound e) { + // code does not exist, so we need to create it (if autocreate enabled) + if(!arguments.createIfNeeded) throw(message="Invalid Source",type="invalidSource"); + oSource = createObject("component","bugLog.components.source").init( oDF.getDAO("source") ); + oSource.setName( key ); + oSource.save(); + } - } catch(sourceFinderException.usernameNotFound e) { - // code does not exist, so we need to create it (if autocreate enabled) - throw(message="Invalid API Key",type="bugLog.invalidAPIKey"); - } + // store entry in cache + variables.oSourceCache.store( key, oSource ); + } - // store entry in cache - variables.oUserCache.store( key, oUser ); - } + return oSource; + } - return oUser; - - + // This method loads the rules into the rule processor + private void function loadRules() { + var oRule = 0; + var thisRule = 0; - - - - - var key = ""; - var bean = arguments.entryBean; - var oSource = 0; - var oDF = variables.oDAOFactory; + // clear all existing rules + variables.oRuleProcessor.flushRules(); - key = bean.getSourceName(); + // get the rule definitions from the extensions service + var dao = variables.oDAOFactory.getDAO("extension"); + var oExtensionsService = createObject("component","bugLog.components.extensionsService").init( dao ); + var aRules = oExtensionsService.getRules(); - try { - // first we try to get it from the cache - oSource = variables.oSourceCache.retrieve( key ); + // create rule objects and load them into the rule processor + for(var i=1; i lte arrayLen(aRules);i=i+1) { + thisRule = aRules[i]; - } catch(cacheService.itemNotFound e) { - // entry not in cache, so we get it from DB - try { - oSource = variables.oSourceFinder.findByName( key ); - - } catch(sourceFinderException.codeNotFound e) { - // code does not exist, so we need to create it (if autocreate enabled) - if(!arguments.createIfNeeded) throw(message="Invalid Source",type="invalidSource"); - oSource = createObject("component","bugLog.components.source").init( oDF.getDAO("source") ); - oSource.setName( key ); - oSource.save(); - } + if(thisRule.enabled) { + oRule = + thisRule.instance + .setListener(this) + .setDAOFactory( variables.oDAOFactory ) + .setMailerService( variables.mailerService ); - // store entry in cache - variables.oSourceCache.store( key, oSource ); - } - - - - - - - var oRule = 0; - var oExtensionsService = 0; - var aRules = arrayNew(1); - var i = 0; - var dao = 0; - var thisRule = 0; - - // clear all existing rules - variables.oRuleProcessor.flushRules(); - - // get the rule definitions from the extensions service - dao = variables.oDAOFactory.getDAO("extension"); - oExtensionsService = createObject("component","bugLog.components.extensionsService").init( dao ); - aRules = oExtensionsService.getRules(); - - // create rule objects and load them into the rule processor - for(i=1; i lte arrayLen(aRules);i=i+1) { - thisRule = aRules[i]; - - if(thisRule.enabled) { - oRule = - thisRule.instance - .setListener(this) - .setDAOFactory( variables.oDAOFactory ) - .setMailerService( variables.mailerService ); - - // add rule to processor - variables.oRuleProcessor.addRule(oRule); - } + // add rule to processor + variables.oRuleProcessor.addRule(oRule); } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // setup queueing service - var queueClass = oConfig.getSetting("service.queueClass"); - variables.queue = createObject("component",queueClass).init(oConfig, instanceName); - - // create a task to process the queue periodically - var schedulerIntervalSecs = oConfig.getSetting("service.schedulerIntervalSecs"); - scheduler.setupTask("bugLogProcessQueue", - "util/processQueue.cfm", - "00:00", - schedulerIntervalSecs, - [{name="key",value=variables.KEY}]); - - - - + } + } + + private boolean function allowAutoCreate( + required string entityType + ) { + var setting = "autocreate." & arguments.entityType; + return getConfig().getSetting(setting, variables.autoCreateDefault); + } + + private void function configureHistoryPurging() { + var enabled = getConfig().getSetting("purging.enabled"); + if( enabled ) { + scheduler.setupTask("bugLogPurgeHistory", + "util/purgeHistory.cfm", + "03:00", + "daily"); + } else { + scheduler.removeTask("bugLogPurgeHistory"); + } + } + + private void function configureDigest() { + var enabled = oConfig.getSetting("digest.enabled"); + var interval = oConfig.getSetting("digest.schedulerIntervalHours"); + var startTime = oConfig.getSetting("digest.schedulerStartTime"); + + if( enabled ) { + scheduler.setupTask("bugLogSendDigest", + "util/sendDigest.cfm", + startTime, + interval*3600); + } else { + scheduler.removeTask("bugLogSendDigest"); + } + } + + private void function configureQueue() { + // setup queueing service + var queueClass = oConfig.getSetting("service.queueClass"); + variables.queue = createObject("component",queueClass).init(oConfig, instanceName); + + // create a task to process the queue periodically + var schedulerIntervalSecs = oConfig.getSetting("service.schedulerIntervalSecs"); + scheduler.setupTask("bugLogProcessQueue", + "util/processQueue.cfm", + "00:00", + schedulerIntervalSecs, + [{name="key",value=variables.KEY}]); + } + +} + From af7c810e9a62155fc2bdaad025bc36e0f77e421a Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Fri, 17 Oct 2014 10:20:17 -0700 Subject: [PATCH 10/36] Updated comments --- components/Queue.cfc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/Queue.cfc b/components/Queue.cfc index aa6cb47..588ca18 100644 --- a/components/Queue.cfc +++ b/components/Queue.cfc @@ -16,9 +16,11 @@ interface { ); // 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(); } From 73b1089b250f9fe8c676f3bb0f0e3fe6e9175de2 Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Sun, 19 Oct 2014 19:13:51 -0700 Subject: [PATCH 11/36] rewrote entry.cfc as cfscript --- components/entry.cfc | 184 +++++++++++++++++++++++-------------------- 1 file changed, 98 insertions(+), 86 deletions(-) diff --git a/components/entry.cfc b/components/entry.cfc index 1d26ece..655eb49 100644 --- a/components/entry.cfc +++ b/components/entry.cfc @@ -1,95 +1,107 @@ - - - - variables.oDAO = 0; - variables.instance.ID = 0; - variables.instance.mydateTime = now(); - variables.instance.message = ""; - variables.instance.applicationID = 0; - variables.instance.sourceID = 0; - variables.instance.severityID = 0; - variables.instance.hostID = 0; - variables.instance.exceptionMessage = ""; - variables.instance.exceptionDetails = ""; - variables.instance.CFID = ""; - variables.instance.CFTOKEN = ""; - variables.instance.userAgent = ""; - variables.instance.templatePath = ""; - variables.instance.HTMLReport = ""; - variables.instance.createdOn = now(); - variables.instance.UUID = ""; +component output="false" { + + variables.oDAO = 0; + + variables.instance = { + ID = 0, + mydateTime = now(), + message = "", + applicationID = 0, + sourceID = 0, + severityID = 0, + hostID = 0, + exceptionMessage = "", + exceptionDetails = "", + CFID = "", + CFTOKEN = "", + userAgent = "", + templatePath = "", + HTMLReport = "", + createdOn = now(), + UUID = "" + }; - function setEntryID(data) {variables.instance.ID = arguments.data;} - function setDateTime(data) {variables.instance.mydateTime = arguments.data;} - function setMessage(data) {variables.instance.message = left(arguments.data,500);} - function setApplicationID(data) {variables.instance.applicationID = arguments.data;} - function setSourceID(data) {variables.instance.sourceID = arguments.data;} - function setSeverityID(data) {variables.instance.severityID = arguments.data;} - function setHostID(data) {variables.instance.hostID = arguments.data;} - function setExceptionMessage(data) {variables.instance.exceptionMessage = left(arguments.data,500);} - function setExceptionDetails(data) {variables.instance.exceptionDetails = left(arguments.data,5000);} - function setCFID(data) {variables.instance.CFID = left(arguments.data,255);} - function setCFTOKEN(data) {variables.instance.CFTOKEN = left(arguments.data,255);} - function setUserAgent(data) {variables.instance.userAgent = left(arguments.data,500);} - function setTemplatePath(data) {variables.instance.templatePath = left(arguments.data,500);} - function setHTMLReport(data) {variables.instance.HTMLReport = arguments.data;} - function setCreatedOn(data) {variables.instance.createdOn = arguments.data;} - function setUUID(data) {variables.instance.UUID = arguments.data;} + function setEntryID(data) {variables.instance.ID = arguments.data;} + function setDateTime(data) {variables.instance.mydateTime = arguments.data;} + function setMessage(data) {variables.instance.message = left(arguments.data,500);} + function setApplicationID(data) {variables.instance.applicationID = arguments.data;} + function setSourceID(data) {variables.instance.sourceID = arguments.data;} + function setSeverityID(data) {variables.instance.severityID = arguments.data;} + function setHostID(data) {variables.instance.hostID = arguments.data;} + function setExceptionMessage(data) {variables.instance.exceptionMessage = left(arguments.data,500);} + function setExceptionDetails(data) {variables.instance.exceptionDetails = left(arguments.data,5000);} + function setCFID(data) {variables.instance.CFID = left(arguments.data,255);} + function setCFTOKEN(data) {variables.instance.CFTOKEN = left(arguments.data,255);} + function setUserAgent(data) {variables.instance.userAgent = left(arguments.data,500);} + function setTemplatePath(data) {variables.instance.templatePath = left(arguments.data,500);} + function setHTMLReport(data) {variables.instance.HTMLReport = arguments.data;} + function setCreatedOn(data) {variables.instance.createdOn = arguments.data;} + function setUUID(data) {variables.instance.UUID = arguments.data;} - function getEntryID() {return variables.instance.ID;} - function getDateTime() {return variables.instance.mydateTime;} - function getMessage() {return variables.instance.message;} - function getApplicationID() {return variables.instance.applicationID;} - function getSourceID() {return variables.instance.sourceID;} - function getSeverityID() {return variables.instance.severityID;} - function getHostID() {return variables.instance.hostID;} - function getExceptionMessage() {return variables.instance.exceptionMessage;} - function getExceptionDetails() {return variables.instance.exceptionDetails;} - function getCFID() {return variables.instance.CFID;} - function getCFTOKEN() {return variables.instance.CFTOKEN;} - function getUserAgent() {return variables.instance.userAgent;} - function getTemplate_Path() {return variables.instance.templatePath;} - function getHTMLReport() {return variables.instance.HTMLReport;} - function getCreatedOn() {return variables.instance.createdOn;} - function getUUID() {return variables.instance.UUID;} + function getEntryID() {return variables.instance.ID;} + function getDateTime() {return variables.instance.mydateTime;} + function getMessage() {return variables.instance.message;} + function getApplicationID() {return variables.instance.applicationID;} + function getSourceID() {return variables.instance.sourceID;} + function getSeverityID() {return variables.instance.severityID;} + function getHostID() {return variables.instance.hostID;} + function getExceptionMessage() {return variables.instance.exceptionMessage;} + function getExceptionDetails() {return variables.instance.exceptionDetails;} + function getCFID() {return variables.instance.CFID;} + function getCFTOKEN() {return variables.instance.CFTOKEN;} + function getUserAgent() {return variables.instance.userAgent;} + function getTemplate_Path() {return variables.instance.templatePath;} + function getHTMLReport() {return variables.instance.HTMLReport;} + function getCreatedOn() {return variables.instance.createdOn;} + function getUUID() {return variables.instance.UUID;} - function getID() {return variables.instance.ID;} - + function getID() {return variables.instance.ID;} + + entry function init( + required bugLog.components.db.entryDAO dao + ) { + variables.oDAO = arguments.dao; + return this; + } - - - - - + void function save(){ + variables.instance.ID = variables.oDAO.save(argumentCollection = variables.instance); + } - - - - - + // Returns the application object + app function getApplication() { + var dp = variables.oDAO.getDataProvider(); + var dao = createObject("component","bugLog.components.db.applicationDAO").init( dp ); + return createObject("component","bugLog.components.appFinder") + .init( dao ) + .findByID(variables.instance.applicationID); + } - - - - - + // Returns the host object + host function getHost() { + var dp = variables.oDAO.getDataProvider(); + var dao = createObject("component","bugLog.components.db.hostDAO").init( dp ); + return createObject("component","bugLog.components.hostFinder") + .init( dao ) + .findByID(variables.instance.hostID); + } - - - - - + // Returns the source object + source function getSource() { + var dp = variables.oDAO.getDataProvider(); + var dao = createObject("component","bugLog.components.db.sourceDAO").init( dp ); + return createObject("component","bugLog.components.sourceFinder") + .init( dao ) + .findByID(variables.instance.sourceID); + } - - - - - + // Returns the severity object + severity function getSeverity() { + var dp = variables.oDAO.getDataProvider(); + var dao = createObject("component","bugLog.components.db.severityDAO").init( dp ); + return createObject("component","bugLog.components.severityFinder") + .init( dao ) + .findByID(variables.instance.severityID); + } - - - - - - - +} From adb0aaddb48c2943d464570c0422f36731f8393f Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Mon, 20 Oct 2014 07:11:08 -0700 Subject: [PATCH 12/36] Move app/host/msg grouping into main search SQL statement --- components/entryFinder.cfc | 25 ++++++++++++++++++++++++- components/hq/appService.cfc | 8 ++++---- hq/handlers/general.cfc | 15 +++++++++++---- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/components/entryFinder.cfc b/components/entryFinder.cfc index cb188a4..9566840 100644 --- a/components/entryFinder.cfc +++ b/components/entryFinder.cfc @@ -48,11 +48,16 @@ + + + + + @@ -63,6 +68,17 @@ + + SELECT + ApplicationCode, ApplicationID, + HostName, HostID, + Message, + COUNT(entryID) AS bugCount, + MAX(createdOn) as createdOn, + MAX(entryID) AS EntryID, + MAX(severityCode) AS SeverityCode + FROM ( + SELECT e.entryID, e.message, e.cfid, e.cftoken, e.mydateTime, e.exceptionMessage, e.exceptionDetails, e.templatePath, e.userAgent, a.code as ApplicationCode, h.hostName, s.code AS SeverityCode, src.name AS SourceName, e.applicationID, e.hostID, e.severityID, e.sourceID, e.createdOn, e.UUID, @@ -173,7 +189,14 @@ AND e.UUID = - ORDER BY e.createdOn DESC, entryID DESC + + ) a + GROUP BY + ApplicationCode, ApplicationID, + HostName, HostID, + Message + + ORDER BY createdOn DESC, entryID DESC diff --git a/components/hq/appService.cfc b/components/hq/appService.cfc index 69b9ab0..180fe98 100644 --- a/components/hq/appService.cfc +++ b/components/hq/appService.cfc @@ -124,6 +124,9 @@ + + + var oEntryFinder = 0; var qry = 0; @@ -478,7 +481,6 @@ - var maxEntries = 20; @@ -486,8 +488,6 @@ // search bug reports var qryEntries = searchEntries(argumentCollection = criteria); - if(summary) - qryEntries = applyGroupings(qryEntries, criteria.groupByApp, criteria.groupByHost); // build rss feed var meta = { @@ -498,7 +498,7 @@ for(var i=1;i lte min(maxEntries, qryEntries.recordCount);i=i+1) { queryAddRow(data,1); - if(summary) { + if(criteria.groupByMsg) { querySetCell(data,"title", qryEntries.message[i] & " (" & qryEntries.bugCount[i] & ")"); querySetCell(data,"body", composeMessage(qryEntries.createdOn[i], qryEntries.applicationCode[i], diff --git a/hq/handlers/general.cfc b/hq/handlers/general.cfc index 91100de..6786836 100644 --- a/hq/handlers/general.cfc +++ b/hq/handlers/general.cfc @@ -163,6 +163,9 @@ // get current filters selected var criteria = getValue("criteria"); + // ensure that we are grouping at least by message + criteria.groupByMsg = true; + // perform search var qryEntries = appService.searchEntries(argumentCollection = criteria); @@ -179,9 +182,6 @@ else setValue("pageTitle", "Summary"); - // perform grouping for summary display - qryEntries = appService.applyGroupings(qryEntries, criteria.groupByApp, criteria.groupByHost); - setValue("qryEntries", qryEntries); setValue("refreshSeconds",refreshSeconds); setValue("rowsPerPage",rowsPerPage); @@ -220,6 +220,11 @@ setValue("msgFromEntryID", ""); } + // ensure that we are not grouping anything + criteria.groupByMsg = false; + criteria.groupByApp = false; + criteria.groupByHost = false; + // perform search var qryEntries = appService.searchEntries(argumentCollection = criteria); @@ -346,7 +351,9 @@ } var criteria = normalizeCriteria(); - var rssXML = appService.buildRSSFeed(criteria, summary, rssService); + criteria.groupByMsg = summary; + + var rssXML = appService.buildRSSFeed(criteria, rssService); setValue("rssXML", rssXML); setView("feed"); From 5750e0cbb504d9f93c38dccebdeda6e7a5130635 Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Sun, 9 Nov 2014 23:35:14 -0800 Subject: [PATCH 13/36] [FIX] Fixed incorrect exception type on catch --- components/bugLogListener.cfc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/bugLogListener.cfc b/components/bugLogListener.cfc index 9c6f8e0..a5c3052 100644 --- a/components/bugLogListener.cfc +++ b/components/bugLogListener.cfc @@ -406,7 +406,7 @@ component output="false" { var apps = oAppFinder.findByIDList(valueList(qryUserApps.applicationID)); oUser.setAllowedApplications(apps); - } catch(sourceFinderException.usernameNotFound e) { + } catch(userFinderException.usernameNotFound e) { // code does not exist, so we need to create it (if autocreate enabled) throw(message="Invalid API Key",type="bugLog.invalidAPIKey"); } From 461067138c7c9d1b1229200359f20c1be6696c1d Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Sun, 9 Nov 2014 23:48:38 -0800 Subject: [PATCH 14/36] Removed references to protocol var on client test script --- test/client.cfm | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/client.cfm b/test/client.cfm index 037a9c4..110ca87 100644 --- a/test/client.cfm +++ b/test/client.cfm @@ -4,7 +4,6 @@ of how to use the buglog client. URL Parameters: - protocol: determines which buglog listener to use. values are cfc, soap and rest severity: type of error to send. Values are ERROR, FATAL, CRITICAL and INFO ---> @@ -68,11 +67,11 @@ Throwing sample error message...
- + - Notify service via [#protocol#] using severity [#severity#]....
+ Notify service using severity [#severity#]....
Date: Mon, 10 Nov 2014 00:35:00 -0800 Subject: [PATCH 15/36] ensure that group by msg is set on dashboard --- hq/handlers/general.cfc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hq/handlers/general.cfc b/hq/handlers/general.cfc index 6786836..9bc8998 100644 --- a/hq/handlers/general.cfc +++ b/hq/handlers/general.cfc @@ -109,6 +109,9 @@ // get current filters selected criteria = getValue("criteria"); + // ensure that we are grouping at least by message + criteria.groupByMsg = true; + qryEntries = appService.searchEntries(argumentCollection = criteria); setValue("qryEntries",qryEntries); @@ -135,6 +138,9 @@ // get current filters selected var criteria = getValue("criteria"); + // ensure that we are grouping at least by message + criteria.groupByMsg = true; + var qryEntries = appService.searchEntries(argumentCollection = criteria); var qryTriggers = appService.getExtensionsLog(criteria.startDate, getValue("currentUser")); From 4efdb7fe63bfdae7c5d94c05b17af0281b1b7396 Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Mon, 10 Nov 2014 00:53:52 -0800 Subject: [PATCH 16/36] Dashboard uses raw data for its own summaries --- hq/handlers/general.cfc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hq/handlers/general.cfc b/hq/handlers/general.cfc index 9bc8998..0d68cf0 100644 --- a/hq/handlers/general.cfc +++ b/hq/handlers/general.cfc @@ -138,8 +138,10 @@ // get current filters selected var criteria = getValue("criteria"); - // ensure that we are grouping at least by message - criteria.groupByMsg = true; + // for the dashboard, we need the raw data to do adhoc queries + criteria.groupByMsg = false; + criteria.groupByApp = false; + criteria.groupByHost = false; var qryEntries = appService.searchEntries(argumentCollection = criteria); var qryTriggers = appService.getExtensionsLog(criteria.startDate, getValue("currentUser")); From 4c987e6139610ce84f9ac17957a8f5f996a45cef Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Sun, 28 Dec 2014 20:53:08 -0800 Subject: [PATCH 17/36] initial commit. separate rule processing --- components/baseRule.cfc | 198 ++++++++++++++++++++++--- components/bugLogListener.cfc | 101 +++++++++++-- components/db/entryDAO.cfc | 2 + components/entry.cfc | 61 +++++--- components/entryFinder.cfc | 69 ++++++--- components/ruleProcessor.cfc | 9 +- config/buglog-config.xml.cfm | 5 + extensions/rules/discard.cfc | 38 +++-- extensions/rules/firstMessageAlert.cfc | 106 +++++++------ extensions/rules/frequencyAlert.cfc | 123 +++++++-------- extensions/rules/heartbeatMonitor.cfc | 109 +++++++------- extensions/rules/mailAlert.cfc | 50 +++++-- extensions/rules/mailRelay.cfc | 36 ++++- hq/handlers/general.cfc | 2 +- install/migration/1_8_to_2_0/mysql.sql | 7 +- util/processRules.cfm | 13 ++ 16 files changed, 660 insertions(+), 269 deletions(-) create mode 100644 util/processRules.cfm diff --git a/components/baseRule.cfc b/components/baseRule.cfc index 708bee2..754f7d6 100644 --- a/components/baseRule.cfc +++ b/components/baseRule.cfc @@ -1,41 +1,148 @@ - - + + // 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"]; + + - - - + + // 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; + - + - - + + var rtn = true; + updateScope(); + if(matchScope(entry) && matchCondition(entry)) { + rtn = doAction(entry); + logTrigger(entry); + } + return rtn; + - + + + + + - + + + + + + + + + + + + + + + + + + + + + // 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; + } + } + } + } + + + + + + + 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; + + + + + + - + @@ -49,8 +156,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;} @@ -72,7 +179,8 @@
- + + @@ -80,7 +188,7 @@ - + @@ -200,7 +308,6 @@ createdOn = now()); - @@ -215,7 +322,17 @@ - + + + + + + + + + + + @@ -231,5 +348,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/bugLogListener.cfc b/components/bugLogListener.cfc index a5c3052..5157eb4 100644 --- a/components/bugLogListener.cfc +++ b/components/bugLogListener.cfc @@ -51,6 +51,7 @@ component output="false" { variables.oSeverityFinder = createObject("component","bugLog.components.severityFinder").init( variables.oDAOFactory.getDAO("severity") ); variables.oSourceFinder = createObject("component","bugLog.components.sourceFinder").init( variables.oDAOFactory.getDAO("source") ); variables.oUserFinder = createObject("component","bugLog.components.userFinder").init( variables.oDAOFactory.getDAO("user") ); + variables.oEntryFinder = createObject("component","bugLog.components.entryFinder").init( variables.oDAOFactory.getDAO("entry") ); // load the rule processor variables.oRuleProcessor = createObject("component","bugLog.components.ruleProcessor").init(); @@ -71,15 +72,15 @@ component output="false" { // configure the incoming queue configureQueue(); - // load rules - loadRules(); - // configure history purging configureHistoryPurging(); // configure the digest sender configureDigest(); + // configure the rule processor + configureRuleProcessor(); + // record the date at which the service started variables.startedOn = Now(); @@ -138,9 +139,6 @@ component output="false" { // save entry oEntry.save(); - - // process rules - variables.oRuleProcessor.processRules(bean, oEntry); } // Processes all entries on the queue @@ -159,7 +157,6 @@ component output="false" { // get a snapshot of the queue as of right now var myQueue = variables.queue.flush(); - variables.oRuleProcessor.processQueueStart(myQueue, dp, variables.oConfig ); if(arrayLen(myQueue) gt 0) { logMessage("Processing queue. Queue size: #arrayLen(myQueue)#"); for(var i=1;i lte arrayLen(myQueue);i++) { @@ -173,7 +170,6 @@ component output="false" { } } } - variables.oRuleProcessor.processQueueEnd(myQueue, dp, variables.oConfig ); // add back all entries on the error queue to the main queue if(arrayLen(errorQueue)) { @@ -189,8 +185,9 @@ component output="false" { // Performs any clean up action required void function shutDown() { logMessage("Stopping BugLogListener (#instanceName#) service..."); - logMessage("Stopping ProcessQueue scheduled task..."); + logMessage("Stopping scheduled tasks..."); scheduler.removeTask("bugLogProcessQueue"); + scheduler.removeTask("bugLogProcessRules"); logMessage("Processing remaining elements in queue..."); processQueue(variables.key); logMessage("BugLogListener service (#instanceName#) stopped."); @@ -270,6 +267,55 @@ component output="false" { return variables.queue.getAll(); } + // Processes rules for new added entries + numeric function processRules( + required string key + ) { + // validate key + if(arguments.key neq variables.key) { + logMessage("Invalid key received. Exiting."); + return 0; + } + + // get rows that have not been processed yet + var qryEntries = variables.oEntryFinder.search( + isProcessed=False + ); + + if(qryEntries.recordCount) + logMessage("Processing rules. Queue size: #qryEntries.recordCount#"); + + // convert to an array of entry beans + var entries = oEntryFinder.createBeansFromQuery(qryEntries); + + // process 'begin' event + try { + variables.oRuleProcessor.processQueueStart( entries ); + } catch(any e) { + logEntry( parseInternalException(e) ); + } + + // process rules for each entry + for(var oEntry in entries) { + try { + variables.oRuleProcessor.processRules( oEntry ); + oEntry.setIsProcessed(true); + oEntry.save(); + } catch(any e) { + logMessage("An error ocurred while processing rules for entry ###oEntry.getID()#: #e.message#"); + logEntry( parseInternalException(e) ); + } + } + + // process 'end' event + try { + variables.oRuleProcessor.processQueueEnd( entries ); + } catch(any e) { + logEntry( parseInternalException(e) ); + } + } + + // Getter functions config function getConfig() {return variables.oConfig;} string function getInstanceName() {return variables.instanceName;} @@ -531,5 +577,42 @@ component output="false" { [{name="key",value=variables.KEY}]); } + private void function configureRuleProcessor() { + // load rule instances + loadRules(); + + // create a task to process the run the configure rules + var schedulerIntervalSecs = oConfig.getSetting("service.schedulerIntervalSecs"); + scheduler.setupTask("bugLogProcessRules", + "util/processRules.cfm", + "00:01", + schedulerIntervalSecs, + [{name="key",value=variables.KEY}]); + } + + private any function parseInternalException( + required any e + ) { + // parses an internal error into a raw entry bean for logging + var error = createObject("component", "bugLog.components.rawEntryBean"); + error.setMessage(e.message); + error.setApplicationCode("BugLogListener"); + error.setSeverityCode("ERROR"); + error.setExceptionMessage(e.message); + error.setExceptionDetails(e.detail); + error.setHostName(CGI.SERVER_NAME); + + var htmlReport = "Type: " & e.type & "

"; + if(structKeyExists(e,"tagContext")) { + htmlReport &= "Stack Trace:
"; + for(var line in e.tagContext) { + htmlReport &= "#line.template# (#line.line#)
"; + } + } + error.setHTMLReport(htmlReport); + + return error; + } + } diff --git a/components/db/entryDAO.cfc b/components/db/entryDAO.cfc index b41b104..fdf40b0 100644 --- a/components/db/entryDAO.cfc +++ b/components/db/entryDAO.cfc @@ -17,6 +17,8 @@ + + diff --git a/components/entry.cfc b/components/entry.cfc index 655eb49..621466c 100644 --- a/components/entry.cfc +++ b/components/entry.cfc @@ -7,9 +7,12 @@ component output="false" { mydateTime = now(), message = "", applicationID = 0, + applicationCode = "", sourceID = 0, severityID = 0, + severityCode = "", hostID = 0, + hostName = "", exceptionMessage = "", exceptionDetails = "", CFID = "", @@ -18,25 +21,32 @@ component output="false" { templatePath = "", HTMLReport = "", createdOn = now(), - UUID = "" + updatedOn = now(), + UUID = "", + isProcessed = 0 }; - function setEntryID(data) {variables.instance.ID = arguments.data;} - function setDateTime(data) {variables.instance.mydateTime = arguments.data;} - function setMessage(data) {variables.instance.message = left(arguments.data,500);} - function setApplicationID(data) {variables.instance.applicationID = arguments.data;} - function setSourceID(data) {variables.instance.sourceID = arguments.data;} - function setSeverityID(data) {variables.instance.severityID = arguments.data;} - function setHostID(data) {variables.instance.hostID = arguments.data;} - function setExceptionMessage(data) {variables.instance.exceptionMessage = left(arguments.data,500);} - function setExceptionDetails(data) {variables.instance.exceptionDetails = left(arguments.data,5000);} - function setCFID(data) {variables.instance.CFID = left(arguments.data,255);} - function setCFTOKEN(data) {variables.instance.CFTOKEN = left(arguments.data,255);} - function setUserAgent(data) {variables.instance.userAgent = left(arguments.data,500);} - function setTemplatePath(data) {variables.instance.templatePath = left(arguments.data,500);} - function setHTMLReport(data) {variables.instance.HTMLReport = arguments.data;} - function setCreatedOn(data) {variables.instance.createdOn = arguments.data;} - function setUUID(data) {variables.instance.UUID = arguments.data;} + function setEntryID(data) {variables.instance.ID = arguments.data; return this;} + function setDateTime(data) {variables.instance.mydateTime = arguments.data; return this;} + function setMessage(data) {variables.instance.message = left(arguments.data,500); return this;} + function setApplicationID(data) {variables.instance.applicationID = arguments.data; return this;} + function setSourceID(data) {variables.instance.sourceID = arguments.data; return this;} + function setSeverityID(data) {variables.instance.severityID = arguments.data; return this;} + function setHostID(data) {variables.instance.hostID = arguments.data; return this;} + function setExceptionMessage(data) {variables.instance.exceptionMessage = left(arguments.data,500); return this;} + function setExceptionDetails(data) {variables.instance.exceptionDetails = left(arguments.data,5000); return this;} + function setCFID(data) {variables.instance.CFID = left(arguments.data,255); return this;} + function setCFTOKEN(data) {variables.instance.CFTOKEN = left(arguments.data,255); return this;} + function setUserAgent(data) {variables.instance.userAgent = left(arguments.data,500); return this;} + function setTemplatePath(data) {variables.instance.templatePath = left(arguments.data,500); return this;} + function setHTMLReport(data) {variables.instance.HTMLReport = arguments.data; return this;} + function setCreatedOn(data) {variables.instance.createdOn = arguments.data; return this;} + function setUpdatedOn(data) {variables.instance.updatedOn = arguments.data; return this;} + function setIsProcessed(data) {variables.instance.isProcessed = arguments.data; return this;} + function setUUID(data) {variables.instance.UUID = arguments.data; return this;} + function setApplicationCode(data) {variables.instance.applicationCode = arguments.data; return this;} + function setSeverityCode(data) {variables.instance.severityCode = arguments.data; return this;} + function setHostName(data) {variables.instance.hostName = arguments.data; return this;} function getEntryID() {return variables.instance.ID;} function getDateTime() {return variables.instance.mydateTime;} @@ -53,7 +63,12 @@ component output="false" { function getTemplate_Path() {return variables.instance.templatePath;} function getHTMLReport() {return variables.instance.HTMLReport;} function getCreatedOn() {return variables.instance.createdOn;} + function getUpdatedOn() {return variables.instance.updatedOn;} + function getIsProcessed() {return variables.instance.isProcessed;} function getUUID() {return variables.instance.UUID;} + function getApplicationCode() {return variables.instance.applicationCode;} + function getSeverityCode() {return variables.instance.severityCode;} + function getHostName() {return variables.instance.hostName;} function getID() {return variables.instance.ID;} @@ -65,6 +80,7 @@ component output="false" { } void function save(){ + variables.instance.updatedOn = Now(); variables.instance.ID = variables.oDAO.save(argumentCollection = variables.instance); } @@ -104,4 +120,15 @@ component output="false" { .findByID(variables.instance.severityID); } + struct function getMemento() { + return variables.instance; + } + + entry function setMemento( + required struct data + ) { + variables.instance = duplicate(arguments.data); + return this; + } + } diff --git a/components/entryFinder.cfc b/components/entryFinder.cfc index 9566840..48db53c 100644 --- a/components/entryFinder.cfc +++ b/components/entryFinder.cfc @@ -4,35 +4,55 @@ var qry = variables.oDAO.get(arguments.id); - var o = 0; + var beans = createBeansFromQuery(qry); - if(qry.recordCount gt 0) { - o = createObject("component","bugLog.components.entry").init( variables.oDAO ); - o.setEntryID(qry.entryID); - o.setDateTime(qry.mydateTime); - o.setMessage(qry.message); - o.setApplicationID(qry.ApplicationID); - o.setSourceID(qry.SourceID); - o.setSeverityID(qry.SeverityID); - o.setHostID(qry.HostID); - o.setExceptionMessage(qry.exceptionMessage); - o.setExceptionDetails(qry.exceptionDetails); - o.setCFID(qry.cfid); - o.setCFTOKEN(qry.cftoken); - o.setUserAgent(qry.userAgent); - o.setTemplatePath(qry.templatePath); - o.setHTMLReport(qry.HTMLReport); - o.setCreatedOn(qry.createdOn); - o.setUUID(qry.UUID); - return o; + if(arrayLen(beans)) { + return beans[1]; } else { throw("ID not found","entryFinderException.IDNotFound"); } + + + + var rtn = []; + for(var i=1;i lte entries.recordCount;i++) { + var o = createObject("component","bugLog.components.entry").init( variables.oDAO ) + .setEntryID(entries.entryID[i]) + .setDateTime(entries.mydateTime[i]) + .setMessage(entries.message[i]) + .setApplicationID(entries.ApplicationID[i]) + .setSourceID(entries.SourceID[i]) + .setSeverityID(entries.SeverityID[i]) + .setHostID(entries.HostID[i]) + .setExceptionMessage(entries.exceptionMessage[i]) + .setExceptionDetails(entries.exceptionDetails[i]) + .setCFID(entries.cfid[i]) + .setCFTOKEN(entries.cftoken[i]) + .setUserAgent(entries.userAgent[i]) + .setTemplatePath(entries.templatePath[i]) + .setCreatedOn(entries.createdOn[i]) + .setUpdatedOn(entries.updatedOn[i]) + .setIsProcessed(entries.isProcessed[i]) + .setUUID(entries.UUID[i]); + if(structKeyExists(entries,"HTMLReport")) + o.setHTMLReport(entries.HTMLReport[i]); + if(structKeyExists(entries,"ApplicationCode")) + o.setApplicationCode(entries.applicationCode[i]); + if(structKeyExists(entries,"SeverityCode")) + o.setSeverityCode(entries.severityCode[i]); + if(structKeyExists(entries,"HostName")) + o.setHostName(entries.hostName[i]); + rtn.add(o); + } + return rtn; + + + - + @@ -51,6 +71,7 @@ + @@ -81,7 +102,8 @@ SELECT e.entryID, e.message, e.cfid, e.cftoken, e.mydateTime, e.exceptionMessage, e.exceptionDetails, e.templatePath, e.userAgent, a.code as ApplicationCode, h.hostName, s.code AS SeverityCode, - src.name AS SourceName, e.applicationID, e.hostID, e.severityID, e.sourceID, e.createdOn, e.UUID, + src.name AS SourceName, e.applicationID, e.hostID, e.severityID, e.sourceID, e.createdOn, + e.updatedOn, e.isProcessed, e.UUID, datePart(year, e.createdOn) as entry_year, @@ -189,6 +211,9 @@ AND e.UUID = + + AND e.isProcessed = + ) a GROUP BY diff --git a/components/ruleProcessor.cfc b/components/ruleProcessor.cfc index 1c22d99..03b674a 100644 --- a/components/ruleProcessor.cfc +++ b/components/ruleProcessor.cfc @@ -15,18 +15,17 @@ - - + - + - + @@ -80,4 +79,4 @@ - \ No newline at end of file + diff --git a/config/buglog-config.xml.cfm b/config/buglog-config.xml.cfm index b52998f..8f983f0 100644 --- a/config/buglog-config.xml.cfm +++ b/config/buglog-config.xml.cfm @@ -67,6 +67,11 @@ --> + + @@ -74,4 +94,4 @@ - \ No newline at end of file + diff --git a/extensions/rules/firstMessageAlert.cfc b/extensions/rules/firstMessageAlert.cfc index f23001f..966d245 100644 --- a/extensions/rules/firstMessageAlert.cfc +++ b/extensions/rules/firstMessageAlert.cfc @@ -9,8 +9,7 @@ - - + @@ -19,19 +18,54 @@ - - - - - - - - - - - + + arguments.timespan = val(arguments.timespan); + arguments.includeHTMLReport = (isBoolean(arguments.includeHTMLReport) && arguments.includeHTMLReport); + super.init(argumentCollection = arguments); + return this; + + + + + var oEntryDAO = getDAOFactory().getDAO("entry"); + var oEntryFinder = createObject("component","bugLog.components.entryFinder").init(oEntryDAO); + + var args = { + message = arguments.rawEntry.getMessage(), + startDate = dateAdd("n", variables.config.timespan * (-1), now() ), + endDate = now() + }; + + for(var key in structKeyArray(scope)) { + var ids = []; + for(var item in scope[key]["items"]) { + ids.add( scope[key]["items"][item] ); + } + if(arrayLen(ids)) { + args[key & "id"] = (scope[key]["not_in"] ? "-" : "") & listToArray(ids) + } + } + + var qry = oEntryFinder.search(argumentCollection = args); + + return (qry.recordCount == 1 + || (qry.recordCount > 1 + && dateDiff("n", variables.lastEmailTimestamp, now()) > variables.config.timespan)); + + + + + + + sendEmail(qry, entry); + variables.lastEmailTimestamp = now(); + return true; + + + + - + @@ -112,7 +147,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -183,4 +179,4 @@ - \ No newline at end of file + diff --git a/extensions/rules/frequencyAlert.cfc b/extensions/rules/frequencyAlert.cfc index 8d556bb..b06da2c 100644 --- a/extensions/rules/frequencyAlert.cfc +++ b/extensions/rules/frequencyAlert.cfc @@ -9,9 +9,9 @@ - - - + + + @@ -22,23 +22,67 @@ - - - - - - - - - - - - - - - + + arguments.sameMessage = (isBoolean(arguments.sameMessage) && arguments.sameMessage); + super.init(argumentCollection = arguments); + return this; + + + + + var matches = false; + var oEntryDAO = getDAOFactory().getDAO("entry"); + var oEntryFinder = createObject("component","bugLog.components.entryFinder").init(oEntryDAO); + + // only evaluate this rule if the amount of timespan minutes has passed after the last email was sent + if( dateDiff("n", variables.lastEmailTimestamp, now()) gt variables.config.timespan ) { + + var args = { + startDate = dateAdd("n", variables.config.timespan * (-1), now() ), + endDate = now() + }; + + for(var key in structKeyArray(scope)) { + var ids = []; + for(var item in scope[key]["items"]) { + ids.add( scope[key]["items"][item] ); + } + if(arrayLen(ids)) { + args[key & "id"] = (scope[key]["not_in"] ? "-" : "") & listToArray(ids) + } + } + + var qry = oEntryFinder.search(argumentCollection = args); + + if(qry.recordCount gt 0) { + if(variables.sameMessage) { + qry = groupMessages(qry, variables.config.count); + + if(qry.recordCount gt 0) { + matches = true; + } + + } else if(qry.recordCount gt variables.config.count) { + matches = true; + } + } + } + + return matches; + + + + + + + sendEmail(qry); + sendAlert(qry); + return true; + + + @@ -147,45 +191,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -270,4 +275,4 @@ - \ No newline at end of file + diff --git a/extensions/rules/heartbeatMonitor.cfc b/extensions/rules/heartbeatMonitor.cfc index 48b6437..94897a2 100644 --- a/extensions/rules/heartbeatMonitor.cfc +++ b/extensions/rules/heartbeatMonitor.cfc @@ -9,6 +9,8 @@ + + @@ -16,19 +18,58 @@ - - - - - - - - - - - + + arguments.alertInterval = val(arguments.alertInterval); + super.init(argumentCollection = arguments); + return this; + - + + + + + var matches = false; + var oEntryDAO = getDAOFactory().getDAO("entry"); + var oEntryFinder = createObject("component","bugLog.components.entryFinder").init(oEntryDAO); + + // only evaluate this rule if 'alertInterval' minutes has passed after the last email was sent + if( dateDiff("n", variables.lastEmailTimestamp, now()) gt variables.config.alertInterval ) { + + var args = { + message = arguments.rawEntry.getMessage(), + startDate = dateAdd("n", variables.config.timespan * (-1), now() ), + endDate = now() + }; + + for(var key in structKeyArray(scope)) { + var ids = []; + for(var item in scope[key]["items"]) { + ids.add( scope[key]["items"][item] ); + } + if(arrayLen(ids)) { + args[key & "id"] = (scope[key]["not_in"] ? "-" : "") & listToArray(ids) + } + } + + var qry = oEntryFinder.search(argumentCollection = args); + + matches = (qry.recordCount == 0); + } + + return matches; + + + + + + + sendEmail(); + variables.lastEmailTimestamp = now(); + return true; + + + + + @@ -112,45 +154,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -175,4 +178,4 @@ - \ No newline at end of file + diff --git a/extensions/rules/mailAlert.cfc b/extensions/rules/mailAlert.cfc index ab0a723..46787b4 100644 --- a/extensions/rules/mailAlert.cfc +++ b/extensions/rules/mailAlert.cfc @@ -2,7 +2,7 @@ hint="This rule sends an email everytime a bug matching a given set of conditions is received"> - + @@ -15,20 +15,44 @@ - - - - - - - + + arguments.includeHTMLReport = (isBoolean(arguments.includeHTMLReport) && arguments.includeHTMLReport); + super.init(argumentCollection = arguments); + return this; + - + + + + + var stEntry = arguments.entry.getMemento(); + var matches = !(arrayLen(listToArray(variables.config.keywords)) > 0); + + for(var keyword in listToArray(variables.config.keywords)) { + matches = matches || findNoCase(keyword, stEntry.message); + } + + return matches; + + + + + + + sendToEmail(entry = arguments.entry, + recipient = variables.config.recipientEmail, + subject = "BugLog: #arguments.entry.getMessage()#", + comment = getAlertMessage(), + entryID = arguments.entry.getEntryID(), + includeHTMLReport = variables.config.includeHTMLReport); + return true; + + + @@ -106,4 +130,4 @@ - \ No newline at end of file + diff --git a/extensions/rules/mailRelay.cfc b/extensions/rules/mailRelay.cfc index 69b75cb..9c8f613 100644 --- a/extensions/rules/mailRelay.cfc +++ b/extensions/rules/mailRelay.cfc @@ -7,11 +7,37 @@ - - - + + arguments.includeHTMLReport = (isBoolean(arguments.includeHTMLReport) && arguments.includeHTMLReport); + super.init(argumentCollection = arguments); + return this; + + + + + + + + + + + + + + + sendToEmail(entry = arguments.entry, + recipient = variables.config.recipientEmail, + subject = "BugLog: #arguments.entry.getMessage()#", + entryId = arguments.entry.getEntryId(), + includeHTMLReport = variables.config.includeHTMLReport); + writeToCFLog("'mailRelay' rule fired. Email sent. Msg: '#arguments.entry.getMessage()#'"); + return true; + + + + + @@ -34,4 +62,4 @@ - \ No newline at end of file + diff --git a/hq/handlers/general.cfc b/hq/handlers/general.cfc index 0d68cf0..32747a4 100644 --- a/hq/handlers/general.cfc +++ b/hq/handlers/general.cfc @@ -321,7 +321,7 @@ setMessage("warning","You are not allowed to view this bug report"); setNextEvent("main"); - } catch(any e) { + } catch(lock e) { setMessage("error",e.message); getService("bugTracker").notifyService(e.message, e); setNextEvent("main"); diff --git a/install/migration/1_8_to_2_0/mysql.sql b/install/migration/1_8_to_2_0/mysql.sql index 6ab5c17..7bf6cbd 100644 --- a/install/migration/1_8_to_2_0/mysql.sql +++ b/install/migration/1_8_to_2_0/mysql.sql @@ -1,3 +1,8 @@ ALTER TABLE `bl_Entry` -ADD COLUMN `UUID` VARCHAR(50) NULL AFTER `createdOn`, +ADD COLUMN `UUID` VARCHAR(50) NULL, +ADD COLUMN `updatedOn` DATETIME NULL, +ADD COLUMN `isProcessed` TINYINT NOT NULL DEFAULT 0, +ADD INDEX `IX_Entry_isProcessed` (`isProcessed` ASC), ADD INDEX `IX_Entry_UUID` (`UUID` ASC); + + diff --git a/util/processRules.cfm b/util/processRules.cfm new file mode 100644 index 0000000..1ee692d --- /dev/null +++ b/util/processRules.cfm @@ -0,0 +1,13 @@ + + + + + + + + + + + + + From 6efeac236deeac6ea65c46203f61e3008dd75553 Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Sun, 28 Dec 2014 21:12:24 -0800 Subject: [PATCH 18/36] set existing entries as processed on migration --- install/migration/1_8_to_2_0/mysql.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/install/migration/1_8_to_2_0/mysql.sql b/install/migration/1_8_to_2_0/mysql.sql index 7bf6cbd..01c1ffd 100644 --- a/install/migration/1_8_to_2_0/mysql.sql +++ b/install/migration/1_8_to_2_0/mysql.sql @@ -5,4 +5,6 @@ ADD COLUMN `isProcessed` TINYINT NOT NULL DEFAULT 0, ADD INDEX `IX_Entry_isProcessed` (`isProcessed` ASC), ADD INDEX `IX_Entry_UUID` (`UUID` ASC); +UPDATE TABLE `bl_Entry` +SET isProcessed = 1; From 9728a3ec7bd7d40fd7f15ec097a64fe45eb9dd3f Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Mon, 29 Dec 2014 08:26:20 -0800 Subject: [PATCH 19/36] removed reference to rawentrybean --- extensions/rules/firstMessageAlert.cfc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/rules/firstMessageAlert.cfc b/extensions/rules/firstMessageAlert.cfc index 966d245..261651d 100644 --- a/extensions/rules/firstMessageAlert.cfc +++ b/extensions/rules/firstMessageAlert.cfc @@ -33,7 +33,7 @@ var oEntryFinder = createObject("component","bugLog.components.entryFinder").init(oEntryDAO); var args = { - message = arguments.rawEntry.getMessage(), + message = arguments.entry.getMessage(), startDate = dateAdd("n", variables.config.timespan * (-1), now() ), endDate = now() }; From e15ba572805ea5eac7ae2063d5bd75bb169985b9 Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Mon, 29 Dec 2014 10:00:34 -0800 Subject: [PATCH 20/36] updated argument expected by doAction internals --- extensions/rules/firstMessageAlert.cfc | 5 +-- extensions/rules/frequencyAlert.cfc | 54 +++++++++++++++----------- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/extensions/rules/firstMessageAlert.cfc b/extensions/rules/firstMessageAlert.cfc index 261651d..185ee1f 100644 --- a/extensions/rules/firstMessageAlert.cfc +++ b/extensions/rules/firstMessageAlert.cfc @@ -59,7 +59,7 @@ - sendEmail(qry, entry); + sendEmail(entry); variables.lastEmailTimestamp = now(); return true; @@ -119,10 +119,9 @@ ---> - - + diff --git a/extensions/rules/frequencyAlert.cfc b/extensions/rules/frequencyAlert.cfc index b06da2c..2e546e4 100644 --- a/extensions/rules/frequencyAlert.cfc +++ b/extensions/rules/frequencyAlert.cfc @@ -33,38 +33,21 @@ var matches = false; - var oEntryDAO = getDAOFactory().getDAO("entry"); - var oEntryFinder = createObject("component","bugLog.components.entryFinder").init(oEntryDAO); // only evaluate this rule if the amount of timespan minutes has passed after the last email was sent - if( dateDiff("n", variables.lastEmailTimestamp, now()) gt variables.config.timespan ) { - - var args = { - startDate = dateAdd("n", variables.config.timespan * (-1), now() ), - endDate = now() - }; - - for(var key in structKeyArray(scope)) { - var ids = []; - for(var item in scope[key]["items"]) { - ids.add( scope[key]["items"][item] ); - } - if(arrayLen(ids)) { - args[key & "id"] = (scope[key]["not_in"] ? "-" : "") & listToArray(ids) - } - } + if( dateDiff("n", variables.lastEmailTimestamp, now()) > variables.config.timespan ) { - var qry = oEntryFinder.search(argumentCollection = args); + var qry = findMessages(); - if(qry.recordCount gt 0) { + if(qry.recordCount > 0) { if(variables.sameMessage) { qry = groupMessages(qry, variables.config.count); - if(qry.recordCount gt 0) { + if(qry.recordCount > 0) { matches = true; } - } else if(qry.recordCount gt variables.config.count) { + } else if(qry.recordCount > variables.config.count) { matches = true; } } @@ -77,6 +60,7 @@ + var qry = findMessages(); sendEmail(qry); sendAlert(qry); return true; @@ -140,6 +124,32 @@ ---> + + + var oEntryDAO = getDAOFactory().getDAO("entry"); + var oEntryFinder = createObject("component","bugLog.components.entryFinder").init(oEntryDAO); + + var args = { + startDate = dateAdd("n", variables.config.timespan * (-1), now() ), + endDate = now() + }; + + for(var key in structKeyArray(scope)) { + var ids = []; + for(var item in scope[key]["items"]) { + ids.add( scope[key]["items"][item] ); + } + if(arrayLen(ids)) { + args[key & "id"] = (scope[key]["not_in"] ? "-" : "") & listToArray(ids) + } + } + + var qry = oEntryFinder.search(argumentCollection = args); + + return qry; + + + From 4d74b45e656c392e521268c5d5713fbfa5b45dd6 Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Mon, 29 Dec 2014 20:07:28 -0800 Subject: [PATCH 21/36] fix incorrect variable reference --- extensions/rules/firstMessageAlert.cfc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/rules/firstMessageAlert.cfc b/extensions/rules/firstMessageAlert.cfc index 185ee1f..dc124fc 100644 --- a/extensions/rules/firstMessageAlert.cfc +++ b/extensions/rules/firstMessageAlert.cfc @@ -150,7 +150,7 @@ recipient = variables.config.recipientEmail, subject= "BugLog: [First Message Alert][#q.ApplicationCode#][#q.hostName#] #q.message#", comment = intro, - entryID = q.EntryID, + entryID = q.id, includeHTMLReport = variables.config.includeHTMLReport)> From cd3fb9ae0206d744a28c681808a9337b19ec96d6 Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Mon, 29 Dec 2014 20:09:34 -0800 Subject: [PATCH 22/36] added error handling to procee queue/rules harnesses --- util/processQueue.cfm | 25 +++++++++++++++++-------- util/processRules.cfm | 25 +++++++++++++++++-------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/util/processQueue.cfm b/util/processQueue.cfm index cc1b27a..7e706ee 100644 --- a/util/processQueue.cfm +++ b/util/processQueue.cfm @@ -1,13 +1,22 @@ - + - - + + try { + // Handle service initialization if necessary + oService = createObject("component", "bugLog.components.service").init( instanceName = url.instance ); - - - - - + // process pending queue + if(oService.isRunning()) { + oBugLogListener = oService.getService(); + oBugLogListener.processQueue( url.key ); + } + + } catch(any e) { + // report the error + bugLogClient = createObject("component", "bugLog.client.bugLogService").init("bugLog.listeners.bugLogListenerWS"); + bugLogClient.notifyService(e.message, e); + } + diff --git a/util/processRules.cfm b/util/processRules.cfm index 1ee692d..3556be5 100644 --- a/util/processRules.cfm +++ b/util/processRules.cfm @@ -1,13 +1,22 @@ - + - - + + try { + // Handle service initialization if necessary + oService = createObject("component", "bugLog.components.service").init( instanceName = url.instance ); - - - - - + // process pending queue + if(oService.isRunning()) { + oBugLogListener = oService.getService(); + oBugLogListener.processRules( url.key ); + } + + } catch(any e) { + // report the error + bugLogClient = createObject("component", "bugLog.client.bugLogService").init("bugLog.listeners.bugLogListenerWS"); + bugLogClient.notifyService(e.message, e); + } + From d3d732d6ed335ada4ba049af67d2d0e87b785ee2 Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Tue, 30 Dec 2014 21:20:09 -0800 Subject: [PATCH 23/36] bug fixes to existing rules --- extensions/rules/discard.cfc | 44 ++------------ extensions/rules/firstMessageAlert.cfc | 63 ++------------------ extensions/rules/frequencyAlert.cfc | 82 ++++---------------------- extensions/rules/heartbeatMonitor.cfc | 76 +++++------------------- extensions/rules/mailAlert.cfc | 55 ++--------------- extensions/rules/mailRelay.cfc | 22 ------- 6 files changed, 42 insertions(+), 300 deletions(-) diff --git a/extensions/rules/discard.cfc b/extensions/rules/discard.cfc index 3d8e760..0a5c8d7 100644 --- a/extensions/rules/discard.cfc +++ b/extensions/rules/discard.cfc @@ -9,12 +9,11 @@ - - - + arguments.message = trim(arguments.message); + arguments.text = trim(arguments.text); super.init(argumentCollection = arguments); return this; @@ -27,7 +26,6 @@ var rtn = (config.message eq "" or (config.message neq "" and listFindNoCase(config.message, entry.getMessage()))) and (config.text eq "" or (config.text neq "" and findNoCase(config.text, alltext))); - return rtn; @@ -36,50 +34,18 @@ var oEntryDAO = getDAOFactory().getDAO("entry"); - oEntryDAO.delete(entry.getEntryID()); + oEntryDAO.delete( entry.getEntryID() ); return false; - - - + + diff --git a/extensions/rules/firstMessageAlert.cfc b/extensions/rules/firstMessageAlert.cfc index dc124fc..7d7030e 100644 --- a/extensions/rules/firstMessageAlert.cfc +++ b/extensions/rules/firstMessageAlert.cfc @@ -14,12 +14,9 @@ - - - - arguments.timespan = val(arguments.timespan); + arguments.timespan = max(val(arguments.timespan),1); arguments.includeHTMLReport = (isBoolean(arguments.includeHTMLReport) && arguments.includeHTMLReport); super.init(argumentCollection = arguments); return this; @@ -31,11 +28,12 @@ var oEntryDAO = getDAOFactory().getDAO("entry"); var oEntryFinder = createObject("component","bugLog.components.entryFinder").init(oEntryDAO); + var createdOn = entry.getCreatedOn(); var args = { message = arguments.entry.getMessage(), - startDate = dateAdd("n", variables.config.timespan * (-1), now() ), - endDate = now() + startDate = dateAdd("n", variables.config.timespan * (-1), createdOn ), + endDate = createdOn }; for(var key in structKeyArray(scope)) { @@ -65,59 +63,6 @@ - - diff --git a/extensions/rules/frequencyAlert.cfc b/extensions/rules/frequencyAlert.cfc index 2e546e4..1bdf6a6 100644 --- a/extensions/rules/frequencyAlert.cfc +++ b/extensions/rules/frequencyAlert.cfc @@ -15,14 +15,13 @@ - - - - - + + + arguments.count = val(arguments.count); + arguments.timespan = max(val(arguments.timespan),1); arguments.sameMessage = (isBoolean(arguments.sameMessage) && arguments.sameMessage); super.init(argumentCollection = arguments); return this; @@ -37,10 +36,10 @@ // only evaluate this rule if the amount of timespan minutes has passed after the last email was sent if( dateDiff("n", variables.lastEmailTimestamp, now()) > variables.config.timespan ) { - var qry = findMessages(); + var qry = findMessages(entry); if(qry.recordCount > 0) { - if(variables.sameMessage) { + if(variables.config.sameMessage) { qry = groupMessages(qry, variables.config.count); if(qry.recordCount > 0) { @@ -60,78 +59,23 @@ - var qry = findMessages(); + var qry = findMessages(entry); sendEmail(qry); sendAlert(qry); return true; - + var oEntryDAO = getDAOFactory().getDAO("entry"); var oEntryFinder = createObject("component","bugLog.components.entryFinder").init(oEntryDAO); + var createdOn = entry.getCreatedOn(); var args = { - startDate = dateAdd("n", variables.config.timespan * (-1), now() ), - endDate = now() + startDate = dateAdd("n", variables.config.timespan * (-1), createdOn ), + endDate = createdOn }; for(var key in structKeyArray(scope)) { @@ -171,7 +115,7 @@ BugLog has received more than #variables.config.count# bug reports - + with the same message @@ -187,7 +131,7 @@

- • [#qryEntries.severityCode#][#qryEntries.applicationCode#][#qryEntries.hostName#] #qryEntries.message# (#qryEntries.bugCount#)
+ • [#qryEntries.severityCode#][#qryEntries.applicationCode#][#qryEntries.hostName#] #qryEntries.message# (#qryEntries.bugCount#)
diff --git a/extensions/rules/heartbeatMonitor.cfc b/extensions/rules/heartbeatMonitor.cfc index 94897a2..811ef14 100644 --- a/extensions/rules/heartbeatMonitor.cfc +++ b/extensions/rules/heartbeatMonitor.cfc @@ -13,20 +13,18 @@ - - - - + - arguments.alertInterval = val(arguments.alertInterval); + arguments.timespan = max(val(arguments.timespan),1); + arguments.alertInterval = max(val(arguments.alertInterval),1); super.init(argumentCollection = arguments); return this; - - + + var matches = false; var oEntryDAO = getDAOFactory().getDAO("entry"); @@ -36,7 +34,6 @@ if( dateDiff("n", variables.lastEmailTimestamp, now()) gt variables.config.alertInterval ) { var args = { - message = arguments.rawEntry.getMessage(), startDate = dateAdd("n", variables.config.timespan * (-1), now() ), endDate = now() }; @@ -53,13 +50,20 @@ var qry = oEntryFinder.search(argumentCollection = args); - matches = (qry.recordCount == 0); - } + if(qry.recordCount == 0) { + sendEmail(); + variables.lastEmailTimestamp = now(); + } - return matches; + } + + + + + @@ -69,56 +73,6 @@ - - diff --git a/extensions/rules/mailAlert.cfc b/extensions/rules/mailAlert.cfc index 46787b4..d2d8358 100644 --- a/extensions/rules/mailAlert.cfc +++ b/extensions/rules/mailAlert.cfc @@ -10,9 +10,6 @@ - - - @@ -31,7 +28,6 @@ for(var keyword in listToArray(variables.config.keywords)) { matches = matches || findNoCase(keyword, stEntry.message); } - return matches; @@ -48,48 +44,7 @@ return true;
- @@ -102,8 +57,8 @@ - - + + @@ -113,12 +68,12 @@ - + - - + + diff --git a/extensions/rules/mailRelay.cfc b/extensions/rules/mailRelay.cfc index 9c8f613..7917568 100644 --- a/extensions/rules/mailRelay.cfc +++ b/extensions/rules/mailRelay.cfc @@ -13,11 +13,6 @@ return this;
- - - - - @@ -37,23 +32,6 @@ - - - From 320bd239f4d4dffd097f9540d20bc8190267d8be Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Tue, 30 Dec 2014 21:20:49 -0800 Subject: [PATCH 24/36] define scope config in base rule constructor --- components/baseRule.cfc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/components/baseRule.cfc b/components/baseRule.cfc index 754f7d6..681db22 100644 --- a/components/baseRule.cfc +++ b/components/baseRule.cfc @@ -15,7 +15,15 @@ + + + + // 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); @@ -48,7 +56,6 @@ The method returns a boolean value that can be used by the caller to determine if additional rules need to be evaluated."> - var rtn = true; updateScope(); From ac6ee77d7210456a08da65ad45e2ef638d1b716b Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Tue, 30 Dec 2014 21:21:05 -0800 Subject: [PATCH 25/36] rewrite as cfscript --- components/ruleProcessor.cfc | 179 +++++++++++++++++++---------------- 1 file changed, 98 insertions(+), 81 deletions(-) diff --git a/components/ruleProcessor.cfc b/components/ruleProcessor.cfc index 03b674a..a64430b 100644 --- a/components/ruleProcessor.cfc +++ b/components/ruleProcessor.cfc @@ -1,82 +1,99 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - var rtn = false; - var ruleName = ""; - var thisRule = 0; - - for(var i=1;i lte arrayLen(variables.aRules);i=i+1) { - ruleName = "Rule #i#"; // a temporary name just in case the getMetaData() call fails - thisRule = variables.aRules[i]; - try { - ruleName = getMetaData(thisRule).name; - - // process rule - rtn = invokeRule(thisRule, arguments.method, args); - - // if rule returns false, then that means that no more rules will be processed, so we exit - if(not rtn) break; - - } catch(any e) { - // if an error occurs while a rule executes, then write to normal log file - buglogClient.notifyService("RuleProcessor Error: #e.message#", e); - writeToCFLog(ruleName & ": " & e.message & e.detail); - } +component { + // This component is in charge of evaluating a set of rules + + variables.aRules = []; + variables.buglogClient = 0; + variables.bugLogListenerEndpoint = "bugLog.listeners.bugLogListenerWS"; + + // constructor + ruleProcessor function init() { + variables.aRules = []; + variables.buglogClient = createObject("component","bugLog.client.bugLogService").init(variables.bugLogListenerEndpoint); + return this; + } + + // adds a rule to be processed + void function addRule( + required baseRule rule + ) { + arrayAppend(variables.aRules, arguments.rule); + } + + // Process all rules with a given entry bean + void function processRules( + required entry entry + ) { + _processRules("processRule", arguments); + } + + // This method gets called BEFORE each processing of the queue + void function processQueueStart( + required array queue + ) { + _processRules("processQueueStart", arguments); + } + + // This method gets called AFTER each processing of the queue + void function processQueueEnd( + required array queue + ) { + _processRules("processQueueEnd", arguments); + } + + // clears all the loaded rules + void function flushRules() { + variables.aRules = arrayNew(1); + } + + + /** Private Methods **/ + + // internal function to process all rules + private void function _processRules( + required string method, + required struct args + ) { + var rtn = false; + var ruleName = ""; + var thisRule = 0; + + for(var i=1; i lte arrayLen(variables.aRules); i++) { + ruleName = "Rule #i#"; // a temporary name just in case the getMetaData() call fails + thisRule = variables.aRules[i]; + try { + ruleName = getMetaData(thisRule).name; + + // process rule + rtn = invokeMethod(thisRule, arguments.method, args); + + // if rule returns false, then that means that no more rules will be processed, so we exit + if(not rtn) break; + + } catch(any e) { + // if an error occurs while a rule executes, then write to normal log file + buglogClient.notifyService("RuleProcessor Error: #e.message#", e); + writeToCFLog(ruleName & ": " & e.message & e.detail); } - - - - - - - - - - - - - + } + } + + // dynamically calls a method on a rule instance + private boolean function invokeMethod( + required any instance, + required string method, + required struct args + ) { + local.rtn = arguments.instance[arguments.method](argumentCollection = arguments.args); + return structKeyExists(local,"rtn") ? local.rtn : true; + } + + // writes a message to the internal cf logs + private void function writeToCFLog( + required string message + ) { + writeLog(type="Info", file="bugLog_ruleProcessor", text="#arguments.message#", application=true); + writeDump(var="BugLog::RuleProcessor: #arguments.message#", output="console"); + } + +} + From 81c50d8056f1466a76dbb1fff126a66477000b54 Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Tue, 30 Dec 2014 23:05:09 -0800 Subject: [PATCH 26/36] revert processing of rules and queue into the same scheduled task, but as separate processes --- components/bugLogListener.cfc | 9 --------- util/processQueue.cfm | 10 ++++++++-- util/processRules.cfm | 22 ---------------------- 3 files changed, 8 insertions(+), 33 deletions(-) delete mode 100644 util/processRules.cfm diff --git a/components/bugLogListener.cfc b/components/bugLogListener.cfc index 5157eb4..eb4ee00 100644 --- a/components/bugLogListener.cfc +++ b/components/bugLogListener.cfc @@ -187,7 +187,6 @@ component output="false" { logMessage("Stopping BugLogListener (#instanceName#) service..."); logMessage("Stopping scheduled tasks..."); scheduler.removeTask("bugLogProcessQueue"); - scheduler.removeTask("bugLogProcessRules"); logMessage("Processing remaining elements in queue..."); processQueue(variables.key); logMessage("BugLogListener service (#instanceName#) stopped."); @@ -580,14 +579,6 @@ component output="false" { private void function configureRuleProcessor() { // load rule instances loadRules(); - - // create a task to process the run the configure rules - var schedulerIntervalSecs = oConfig.getSetting("service.schedulerIntervalSecs"); - scheduler.setupTask("bugLogProcessRules", - "util/processRules.cfm", - "00:01", - schedulerIntervalSecs, - [{name="key",value=variables.KEY}]); } private any function parseInternalException( diff --git a/util/processQueue.cfm b/util/processQueue.cfm index 7e706ee..f2853e8 100644 --- a/util/processQueue.cfm +++ b/util/processQueue.cfm @@ -4,6 +4,8 @@ + bugLogClient = createObject("component", "bugLog.client.bugLogService").init("bugLog.listeners.bugLogListenerWS"); + try { // Handle service initialization if necessary oService = createObject("component", "bugLog.components.service").init( instanceName = url.instance ); @@ -11,12 +13,16 @@ // process pending queue if(oService.isRunning()) { oBugLogListener = oService.getService(); + + // process all new incoming bug reports oBugLogListener.processQueue( url.key ); - } + + // process rules for newly added reports + oBugLogListener.processRules( url.key ); + } } catch(any e) { // report the error - bugLogClient = createObject("component", "bugLog.client.bugLogService").init("bugLog.listeners.bugLogListenerWS"); bugLogClient.notifyService(e.message, e); } diff --git a/util/processRules.cfm b/util/processRules.cfm deleted file mode 100644 index 3556be5..0000000 --- a/util/processRules.cfm +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - try { - // Handle service initialization if necessary - oService = createObject("component", "bugLog.components.service").init( instanceName = url.instance ); - - // process pending queue - if(oService.isRunning()) { - oBugLogListener = oService.getService(); - oBugLogListener.processRules( url.key ); - } - - } catch(any e) { - // report the error - bugLogClient = createObject("component", "bugLog.client.bugLogService").init("bugLog.listeners.bugLogListenerWS"); - bugLogClient.notifyService(e.message, e); - } - From df0acdea8277c97bbd7d4cbbb9fd1cb5ac16f96e Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Wed, 31 Dec 2014 01:19:32 -0800 Subject: [PATCH 27/36] separate stats into its own request --- hq/handlers/general.cfc | 50 +++++++++++----- hq/views/entryStats.cfm | 124 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+), 13 deletions(-) create mode 100644 hq/views/entryStats.cfm diff --git a/hq/handlers/general.cfc b/hq/handlers/general.cfc index 0d68cf0..9a8be4f 100644 --- a/hq/handlers/general.cfc +++ b/hq/handlers/general.cfc @@ -268,7 +268,6 @@ var entryID = getValue("entryID"); var entryUUID = getValue("uuid"); var argsSearch = {}; - var qryEntriesUA = queryNew(""); var currentUser = getValue("currentUser"); var oEntry = 0; @@ -282,6 +281,41 @@ setNextEvent("main"); } + // update lastread setting + if(structKeyExists(cookie, "lastbugread") and entryID gte cookie.lastbugread) { + cookie.lastbugread = entryID; + } + + // set values + setValue("ruleTypes", getService("app").getRules()); + setValue("jiraEnabled", getService("jira").getSetting("enabled")); + setValue("oEntry", oEntry); + setView("entry"); + + } catch(notAllowed e) { + setMessage("warning","You are not allowed to view this bug report"); + setNextEvent("main"); + + } catch(any e) { + setMessage("error",e.message); + getService("bugTracker").notifyService(e.message, e); + setNextEvent("main"); + } + + + + + + try { + var appService = getService("app"); + var entryID = getValue("entryID"); + var argsSearch = {}; + var qryEntriesUA = queryNew(""); + var currentUser = getValue("currentUser"); + + // get requested entry object + var oEntry = appService.getEntry(entryID, currentUser); + // search for recent ocurrences (last 24 hours) args.message = "__EMPTY__"; args.startDate = dateAdd("d", -1, now()); @@ -301,30 +335,20 @@ user = currentUser); } - - // update lastread setting - if(structKeyExists(cookie, "lastbugread") and entryID gte cookie.lastbugread) { - cookie.lastbugread = entryID; - } - // set values - setValue("ruleTypes", getService("app").getRules()); - setValue("jiraEnabled", getService("jira").getSetting("enabled")); + setLayout(""); setValue("oEntry", oEntry); setValue("qryEntriesLast24", qryEntriesLast24); setValue("qryEntriesAll", qryEntriesAll); setValue("qryEntriesUA", qryEntriesUA); - setValue("oEntry", oEntry); - setView("entry"); + setView("entryStats"); } catch(notAllowed e) { setMessage("warning","You are not allowed to view this bug report"); - setNextEvent("main"); } catch(any e) { setMessage("error",e.message); getService("bugTracker").notifyService(e.message, e); - setNextEvent("main"); } diff --git a/hq/views/entryStats.cfm b/hq/views/entryStats.cfm new file mode 100644 index 0000000..04f7e40 --- /dev/null +++ b/hq/views/entryStats.cfm @@ -0,0 +1,124 @@ + + + + + + + + + SELECT * + FROM rs.qryEntriesAll + WHERE entryID < + ORDER BY createdOn DESC + + + + SELECT hostID, hostName, count(hostID) as numEntries + FROM rs.qryEntriesAll + GROUP BY hostID, hostName + + + + + SELECT * + FROM rs.qryEntriesUA + WHERE entryID <> + ORDER BY createdOn DESC + + + + + + +
+

Stats

+ + +
+ Host Distribution:
+
Message:
Date/Time:#showDateTime(stEntry.receivedOn)##createdOn#
Application:
+ + + + + + + +
#hostName##numEntries# (#round(numEntries/totalEntries*100)#%)
+ +
+ + + From 72125c0c7b827e5633c14823d1494623623d4fb9 Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Wed, 31 Dec 2014 01:20:22 -0800 Subject: [PATCH 28/36] more changes --- hq/views/entry.cfm | 142 +++++++-------------------------------------- 1 file changed, 20 insertions(+), 122 deletions(-) diff --git a/hq/views/entry.cfm b/hq/views/entry.cfm index 1ec834b..306345d 100644 --- a/hq/views/entry.cfm +++ b/hq/views/entry.cfm @@ -21,33 +21,24 @@
+ + - - - - SELECT * - FROM rs.qryEntriesAll - WHERE entryID < - ORDER BY createdOn DESC - - - SELECT hostID, hostName, count(hostID) as numEntries - FROM rs.qryEntriesAll - GROUP BY hostID, hostName - - - - SELECT * - FROM rs.qryEntriesUA - WHERE entryID <> - ORDER BY createdOn DESC - - - - + + + + + + - @@ -58,7 +49,7 @@ - + @@ -198,78 +167,7 @@
- + Send to email @@ -112,12 +103,8 @@ DELETE BUG REPORT? - - - - - - + +
@@ -154,25 +141,7 @@
User Agent: - #oEntry.getUserAgent()# - -
#rs.qryEntriesUA.recordCount# other reports from the same user agent (last 24hrs) - -
-
#oEntry.getUserAgent()#
- -
-

Stats

- - -
- Host Distribution:
- - - - - - - - -
#hostName##numEntries# (#round(numEntries/totalEntries*100)#%)
-
-
-
- + From 2dd06ead9051c21d1f62f17091cc6ab31a517f5f Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Wed, 31 Dec 2014 10:32:02 -0800 Subject: [PATCH 29/36] moved rule processing logic into ruleProcessor.cfc --- components/bugLogListener.cfc | 26 +----------- components/ruleProcessor.cfc | 59 +++++++++++++-------------- extensions/rules/heartbeatMonitor.cfc | 4 +- 3 files changed, 33 insertions(+), 56 deletions(-) diff --git a/components/bugLogListener.cfc b/components/bugLogListener.cfc index eb4ee00..860d606 100644 --- a/components/bugLogListener.cfc +++ b/components/bugLogListener.cfc @@ -287,31 +287,9 @@ component output="false" { // convert to an array of entry beans var entries = oEntryFinder.createBeansFromQuery(qryEntries); - // process 'begin' event - try { - variables.oRuleProcessor.processQueueStart( entries ); - } catch(any e) { - logEntry( parseInternalException(e) ); - } - - // process rules for each entry - for(var oEntry in entries) { - try { - variables.oRuleProcessor.processRules( oEntry ); - oEntry.setIsProcessed(true); - oEntry.save(); - } catch(any e) { - logMessage("An error ocurred while processing rules for entry ###oEntry.getID()#: #e.message#"); - logEntry( parseInternalException(e) ); - } - } + // process rules for all the entries + variables.oRuleProcessor.processRules( entries ); - // process 'end' event - try { - variables.oRuleProcessor.processQueueEnd( entries ); - } catch(any e) { - logEntry( parseInternalException(e) ); - } } diff --git a/components/ruleProcessor.cfc b/components/ruleProcessor.cfc index a64430b..3f8668d 100644 --- a/components/ruleProcessor.cfc +++ b/components/ruleProcessor.cfc @@ -19,25 +19,22 @@ component { arrayAppend(variables.aRules, arguments.rule); } - // Process all rules with a given entry bean + // executes all rules for all entry bens in the given array void function processRules( - required entry entry + required array entries ) { - _processRules("processRule", arguments); - } + // process 'begin' event + process("queueStart", entries); + + // process rules for each entry + for(var oEntry in entries) { + process("rule", oEntry); + oEntry.setIsProcessed(true); + oEntry.save(); + } - // This method gets called BEFORE each processing of the queue - void function processQueueStart( - required array queue - ) { - _processRules("processQueueStart", arguments); - } - - // This method gets called AFTER each processing of the queue - void function processQueueEnd( - required array queue - ) { - _processRules("processQueueEnd", arguments); + // process 'end' event + process("queueEnd", entries); } // clears all the loaded rules @@ -49,9 +46,9 @@ component { /** Private Methods **/ // internal function to process all rules - private void function _processRules( - required string method, - required struct args + private void function process( + required string event, + required any arg ) { var rtn = false; var ruleName = ""; @@ -63,8 +60,18 @@ component { try { ruleName = getMetaData(thisRule).name; - // process rule - rtn = invokeMethod(thisRule, arguments.method, args); + // process rule + switch(arguments.event) { + case "queueStart": + rtn = thisRule.processQueueStart(arg); + break; + case "rule": + rtn = thisRule.processRule(arg); + break; + case "queueEnd": + rtn = thisRule.processQueueEnd(arg); + break; + } // if rule returns false, then that means that no more rules will be processed, so we exit if(not rtn) break; @@ -77,16 +84,6 @@ component { } } - // dynamically calls a method on a rule instance - private boolean function invokeMethod( - required any instance, - required string method, - required struct args - ) { - local.rtn = arguments.instance[arguments.method](argumentCollection = arguments.args); - return structKeyExists(local,"rtn") ? local.rtn : true; - } - // writes a message to the internal cf logs private void function writeToCFLog( required string message diff --git a/extensions/rules/heartbeatMonitor.cfc b/extensions/rules/heartbeatMonitor.cfc index 811ef14..46c1e50 100644 --- a/extensions/rules/heartbeatMonitor.cfc +++ b/extensions/rules/heartbeatMonitor.cfc @@ -23,7 +23,7 @@
- + var matches = false; @@ -56,6 +56,8 @@ } } + + return true; From f16c209bdd90d3db751fc74297acfeeeea67765f Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Tue, 6 Jan 2015 21:17:39 -0800 Subject: [PATCH 30/36] added missing semicolon --- extensions/rules/firstMessageAlert.cfc | 2 +- extensions/rules/frequencyAlert.cfc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/rules/firstMessageAlert.cfc b/extensions/rules/firstMessageAlert.cfc index 7d7030e..9b2e850 100644 --- a/extensions/rules/firstMessageAlert.cfc +++ b/extensions/rules/firstMessageAlert.cfc @@ -42,7 +42,7 @@ ids.add( scope[key]["items"][item] ); } if(arrayLen(ids)) { - args[key & "id"] = (scope[key]["not_in"] ? "-" : "") & listToArray(ids) + args[key & "id"] = (scope[key]["not_in"] ? "-" : "") & listToArray(ids); } } diff --git a/extensions/rules/frequencyAlert.cfc b/extensions/rules/frequencyAlert.cfc index 1bdf6a6..2c750d7 100644 --- a/extensions/rules/frequencyAlert.cfc +++ b/extensions/rules/frequencyAlert.cfc @@ -84,7 +84,7 @@ ids.add( scope[key]["items"][item] ); } if(arrayLen(ids)) { - args[key & "id"] = (scope[key]["not_in"] ? "-" : "") & listToArray(ids) + args[key & "id"] = (scope[key]["not_in"] ? "-" : "") & listToArray(ids); } } From 39ea30769c31acb1e7b38485360d825742043ba0 Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Sat, 10 Jan 2015 20:29:46 -0800 Subject: [PATCH 31/36] replaced wrong var reference --- components/bugLogListener.cfc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/bugLogListener.cfc b/components/bugLogListener.cfc index 860d606..7aa1404 100644 --- a/components/bugLogListener.cfc +++ b/components/bugLogListener.cfc @@ -166,7 +166,7 @@ component output="false" { } catch(any e) { // log error and save entry in another queue arrayAppend(errorQueue,myQueue[i]); - logMessage("ERROR: #cfcatch.message# #cfcatch.detail#. Original message in entry: '#myQueue[i].getMessage()#'"); + logMessage("ERROR: #e.message# #e.detail#. Original message in entry: '#myQueue[i].getMessage()#'"); } } } From 1c27f2febdba0758fdc2fb77b77429e88ce0d2d1 Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Mon, 18 Apr 2016 08:57:08 -0700 Subject: [PATCH 32/36] Fixed sql in migration script --- install/migration/1_8_to_2_0/mysql.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/migration/1_8_to_2_0/mysql.sql b/install/migration/1_8_to_2_0/mysql.sql index 01c1ffd..4d395c3 100644 --- a/install/migration/1_8_to_2_0/mysql.sql +++ b/install/migration/1_8_to_2_0/mysql.sql @@ -5,6 +5,6 @@ ADD COLUMN `isProcessed` TINYINT NOT NULL DEFAULT 0, ADD INDEX `IX_Entry_isProcessed` (`isProcessed` ASC), ADD INDEX `IX_Entry_UUID` (`UUID` ASC); -UPDATE TABLE `bl_Entry` +UPDATE `bl_Entry` SET isProcessed = 1; From 7da7c20e1e4c36c148d57994f7a73c467b4290d2 Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Mon, 18 Apr 2016 08:57:58 -0700 Subject: [PATCH 33/36] [FIX] fixed bug that caused HTMLReport to dissapear on rule processing --- components/entry.cfc | 11 +++++++++++ components/ruleProcessor.cfc | 3 +-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/components/entry.cfc b/components/entry.cfc index 621466c..f03f521 100644 --- a/components/entry.cfc +++ b/components/entry.cfc @@ -131,4 +131,15 @@ component output="false" { return this; } + void function flagAsProcessed() { + var id = getID(); + if(id) { + variables.instance.updatedOn = Now(); + variables.oDAO.save( + id = id, + isProcessed = 1 + ); + } + } + } diff --git a/components/ruleProcessor.cfc b/components/ruleProcessor.cfc index 3f8668d..0e3b242 100644 --- a/components/ruleProcessor.cfc +++ b/components/ruleProcessor.cfc @@ -29,8 +29,7 @@ component { // process rules for each entry for(var oEntry in entries) { process("rule", oEntry); - oEntry.setIsProcessed(true); - oEntry.save(); + oEntry.flagAsProcessed(); } // process 'end' event From 12dbedb0d4780e82cd32b44b65547b1bcb4d47de Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Mon, 18 Apr 2016 08:58:37 -0700 Subject: [PATCH 34/36] added .DS_Store to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 94673a6..dfa8cea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /*~ /deploy/ +.DS_Store From 02bda796ebc30f321f3825b5d1b8d5f1f6e5b904 Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Thu, 28 Mar 2019 10:23:32 -0700 Subject: [PATCH 35/36] removed old listeners/listener.cfc --- listeners/bugLogListenerREST.cfm | 2 +- listeners/bugLogListenerWS.cfc | 2 +- listeners/listener.cfc | 58 -------------------------------- 3 files changed, 2 insertions(+), 60 deletions(-) delete mode 100644 listeners/listener.cfc diff --git a/listeners/bugLogListenerREST.cfm b/listeners/bugLogListenerREST.cfm index e2bf9db..abdb9b0 100644 --- a/listeners/bugLogListenerREST.cfm +++ b/listeners/bugLogListenerREST.cfm @@ -39,7 +39,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - // get listener service wrapper - var serviceLoader = createObject("component", "bugLog.components.service").init( instanceName = variables.instanceName ); - - // get handle to bugLogListener service - var bugLogListener = serviceLoader.getService(); - - // create entry bean - var rawEntry = createObject("component","bugLog.components.rawEntryBean") - .init() - .setDateTime(arguments.dateTime) - .setMessage(arguments.message) - .setApplicationCode(arguments.applicationCode) - .setSourceName(arguments.source) - .setSeverityCode(arguments.severityCode) - .setHostName(arguments.hostName) - .setExceptionMessage(arguments.exceptionMessage) - .setExceptionDetails(arguments.exceptionDetails) - .setCFID(arguments.cfid) - .setCFTOKEN(arguments.cftoken) - .setUserAgent(arguments.userAgent) - .setTemplatePath(arguments.templatePath) - .setHTMLReport(arguments.HTMLReport) - .setReceivedOn(now()); - - // validate Entry - bugLogListener.validate(rawEntry, arguments.APIKey); - - // log entry - bugLogListener.logEntry(rawEntry); - - - - \ No newline at end of file From d173a7b962c3528574e15863db1f1ad598849748 Mon Sep 17 00:00:00 2001 From: Oscar Arevalo Date: Thu, 28 Mar 2019 10:40:15 -0700 Subject: [PATCH 36/36] allow overriding settings using environment variables --- core/eventHandler.cfc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/core/eventHandler.cfc b/core/eventHandler.cfc index 3f9c4c2..9b3a7aa 100644 --- a/core/eventHandler.cfc +++ b/core/eventHandler.cfc @@ -12,6 +12,8 @@ variables.APP_KEYS.SERVICES = "services"; variables.APP_KEYS.SETTINGS = "settings"; variables.APP_KEYS.PATHS = "paths"; + + variables.system = createObject("java", "java.lang.System"); // setters and getters function getView() {return variables.requestState.view;} @@ -89,7 +91,14 @@ - + + + + + + + +