Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/*~
/deploy/
.DS_Store
.DS_Store
/Vagrantfile
/vagrantscripts/
/database/
/.vagrant/
/WEB-INF/
/*.log
26 changes: 26 additions & 0 deletions components/SlackService.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<cfcomponent>

<cfset variables.endpoint = "">
<cfset variables.enabled = "">

<cffunction name="init" access="public" returntype="slackService">
<cfargument name="configObj" type="config" required="true">
<cfscript>
variables.endpoint = arguments.configObj.getSetting("slack.endpoint", variables.endpoint);
variables.enabled = arguments.configObj.getSetting("slack.enabled", variables.enabled);
return this;
</cfscript>
</cffunction>

<cffunction name="postIssue" access="public" returntype="void">
<cfargument name="text">
<cfset var body = { "text" = arguments.text }>
<cfset var response = {}>

<cfhttp url="#variables.endpoint#" result="response" method="POST" throwonerror="false">
<cfhttpparam type="header" name="Content-Type" value="application/json">
<cfhttpparam type="body" value="#serializeJson(body)#">
</cfhttp>
</cffunction>

</cfcomponent>
29 changes: 29 additions & 0 deletions components/baseRule.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,29 @@

</cffunction>

<cffunction name="sendToSlack" access="public" returntype="void">
<cfargument name="rawEntryBean" type="bugLog.components.rawEntryBean" required="false">
<cfargument name="comment" type="string" required="false" default="">
<cfargument name="entryId" type="numeric" required="false" default="0">

<cfscript>
var buglogHref = getBaseBugLogHREF();
var bugReportURL = "";
var text = "";

if( arguments.entryID gt 0 ){
bugReportURL = getBugEntryHREF(arguments.entryID);
}
</cfscript>

<!--- build contents of slack message --->
<cfsavecontent variable="text"><cfoutput>#arguments.comment#
Bug Report URL: #bugReportURL#
</cfoutput></cfsavecontent>

<cfset slackService.postIssue( text = text ) />
</cffunction>

<cffunction name="writeToCFLog" access="private" returntype="void" hint="writes a message to the internal cf logs">
<cfargument name="message" type="string" required="true">
<cflog application="true" file="bugLog_ruleProcessor" text="#arguments.message#">
Expand Down Expand Up @@ -168,6 +191,12 @@
<cfreturn this />
</cffunction>

<cffunction name="setSlackService" access="public" returntype="baseRule" hint="Adds a reference to the mailer service">
<cfargument name="slackService" type="any" required="true">
<cfset variables.slackService = arguments.slackService>
<cfreturn this />
</cffunction>

<cffunction name="setExtensionID" access="public" returntype="baseRule" hint="Sets the ID of this extension instance">
<cfargument name="id" type="numeric" required="true">
<cfset variables._id_ = arguments.id>
Expand Down
6 changes: 5 additions & 1 deletion components/bugLogListener.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
// load the mailer service
variables.mailerService = createObject("component","bugLog.components.MailerService").init( variables.oConfig );

// load the slack service
variables.slackService = createObject("component","bugLog.components.SlackService").init(variables.oConfig);

// load rules
loadRules();

Expand Down Expand Up @@ -412,7 +415,8 @@
thisRule.instance
.setListener(this)
.setDAOFactory( variables.oDAOFactory )
.setMailerService( variables.mailerService );
.setMailerService( variables.mailerService )
.setSlackService( variables.slackService );

// add rule to processor
variables.oRuleProcessor.addRule(oRule);
Expand Down
41 changes: 41 additions & 0 deletions components/hq/slackService.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<cfcomponent>

<cfset variables.instance = {
slack = 0,
endpoint = "",
enabled = 0
}>

<cffunction name="init" access="public" returntype="slackService">
<cfargument name="appService" type="any" required="true">

<cfset variables.instance.app = arguments.appService>
<cfset variables.instance.slack = createObject("component","bugLog.components.slackService").init(
variables.instance.app.getConfig()
)>
<cfreturn this>
</cffunction>

<cffunction name="reinit" access="public" returntype="void">
<cfset init( variables.instance.app )>
</cffunction>

<cffunction name="postIssue" access="public" returntype="void">
<cfargument name="text" type="string" required="true">
<cfset variables.instance.slack.postIssue(argumentCollection = arguments)>
</cffunction>

<cffunction name="getSetting" returnType="string" access="public" hint="Returns the given config setting, if doesnt exist, returns empty or default value">
<cfargument name="settingName" type="string" required="true">
<cfargument name="defaultValue" type="string" required="false" default="">
<cfreturn variables.instance.app.getConfig().getSetting("slack." & arguments.settingName, arguments.defaultValue)>
</cffunction>

<cffunction name="setSetting" returntype="slackService" access="public">
<cfargument name="settingName" type="string" required="true">
<cfargument name="settingValue" type="string" required="true">
<cfset variables.instance.app.getConfig().setSetting("slack." & arguments.settingName, arguments.settingValue)>
<cfreturn this>
</cffunction>

</cfcomponent>
5 changes: 4 additions & 1 deletion config/buglog-config.xml.cfm
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@
<setting name="jira.username"></setting>
<setting name="jira.password"></setting>

<setting name="slack.enabled">false</setting>
<setting name="slack.endpoint"></setting>

<setting name="purging.numberOfDays">90</setting>
<setting name="purging.enabled">false</setting>

<setting name="digest.enabled">false</setting>
<setting name="digest.recipients"></setting>
<setting name="digest.schedulerIntervalHours">24</setting>
Expand Down
80 changes: 69 additions & 11 deletions extensions/rules/firstMessageAlert.cfc
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<cfcomponent extends="bugLog.components.baseRule"
<cfcomponent extends="bugLog.components.baseRule"
displayName="First Message Alert"
hint="This rule checks for the first time a given bug report is received on the last X minutes and send an email">

<cfproperty name="recipientEmail" type="string" buglogType="email" displayName="Recipient Email" hint="The email address to which to send the notifications">
<cfproperty name="timespan" type="numeric" displayName="Timespan" hint="The number in minutes for which to count the amount of bug reports received">
<cfproperty name="application" type="string" buglogType="application" displayName="Application" hint="The application name that will trigger the rule. Leave empty to look for all applications">
<cfproperty name="host" type="string" buglogType="host" displayName="Host Name" hint="The host name that will trigger the rule. Leave empty to look for all hosts">
<cfproperty name="severity" type="string" buglogType="severity" displayName="Severity Code" hint="The severity that will trigger the rule. Leave empty to look for all severities">
<cfproperty name="includeHTMLReport" type="boolean" displayName="Include HTML Report?" hint="When enabled, the HTML Report section of the bug report is included in the email body">
<cfproperty name="sendEmailAlert" type="boolean" displayName="Send Email Alert?" hint="When enabled, the alert is sent via email" default="true">
<cfproperty name="sendSlackAlert" type="boolean" displayName="Send Slack Alert?" hint="When enabled, the alert is sent via slack" default="false">

<cfset variables.ID_NOT_SET = -9999999 />
<cfset variables.ID_NOT_FOUND = -9999990 />
Expand All @@ -19,19 +21,24 @@
<cfargument name="host" type="string" required="false" default="">
<cfargument name="severity" type="string" required="false" default="">
<cfargument name="includeHTMLReport" type="string" required="false" default="">
<cfargument name="sendEmailAlert" type="string" required="false" default="true">
<cfargument name="sendSlackAlert" type="string" required="false" default="false">

<cfset variables.config.recipientEmail = arguments.recipientEmail>
<cfset variables.config.timespan = val(arguments.timespan)>
<cfset variables.config.application = arguments.application>
<cfset variables.config.host = arguments.host>
<cfset variables.config.severity = arguments.severity>
<cfset variables.config.includeHTMLReport = (isBoolean(arguments.includeHTMLReport) and arguments.includeHTMLReport)>
<cfset variables.config.sendEmailAlert = (isBoolean(arguments.sendEmailAlert) and arguments.sendEmailAlert)>
<cfset variables.config.sendSlackAlert = (isBoolean(arguments.sendSlackAlert) and arguments.sendSlackAlert)>
<cfset variables.applicationID = variables.ID_NOT_SET>
<cfset variables.hostID = variables.ID_NOT_SET>
<cfset variables.severityID = variables.ID_NOT_SET>
<cfset variables.lastEmailTimestamp = createDateTime(1800,1,1,0,0,0)>
<cfreturn this>
</cffunction>

<cffunction name="processRule" access="public" returnType="boolean">
<cfargument name="rawEntry" type="bugLog.components.rawEntryBean" required="true">
<cfargument name="entry" type="bugLog.components.entry" required="true">
Expand Down Expand Up @@ -72,13 +79,20 @@
if(variables.severityID neq ID_NOT_SET) args.severityID = variables.severityID;

qry = oEntryFinder.search(argumentCollection = args);

if(qry.recordCount eq 1 or (qry.recordCount gt 1 and dateDiff("n", variables.lastEmailTimestamp, now()) gt variables.config.timespan)) {
logTrigger(entry);
sendEmail(qry, rawEntry);
if( variables.config.sendEmailAlert ){
sendEmail(qry, rawEntry);
}

if( variables.config.sendSlackAlert ){
sendSlack(qry, rawEntry);
}

variables.lastEmailTimestamp = now();
}

return true;
</cfscript>
</cffunction>
Expand All @@ -104,24 +118,68 @@
<cfif variables.config.severity neq "">
with a severity of <strong>#variables.config.severity#</strong>
</cfif>
on the last
on the last
<b>
<cfif numHours gt 0> #numHours# hour<cfif numHours gt 1>s</cfif> <cfif numMinutes gt 0> and </cfif></cfif>
<cfif numMinutes gt 0> #numMinutes# minute<cfif numMinutes gt 1>s</cfif></cfif>
</b>
</cfoutput>
</cfsavecontent>
</cfsavecontent>

<cfset sendToEmail(rawEntryBean = arguments.rawEntry,
recipient = variables.config.recipientEmail,
subject= "BugLog: [First Message Alert][#q.ApplicationCode#][#q.hostName#] #q.message#",
subject= "BugLog: [First Message Alert][#q.ApplicationCode#][#q.hostName#] #q.message#",
comment = intro,
entryID = q.EntryID,
includeHTMLReport = variables.config.includeHTMLReport)>

<cfset writeToCFLog("'firstMessageAlert' rule fired. Email sent. Msg: '#q.message#'")>
</cffunction>

<cffunction name="sendSlack" access="private" returntype="void" output="true">
<cfargument name="data" type="query" required="true" hint="query with the bug report entries">
<cfargument name="rawEntry" type="bugLog.components.rawEntryBean" required="true">

<cfset var q = arguments.data>
<cfset var numHours = int(variables.config.timespan / 60)>
<cfset var numMinutes = variables.config.timespan mod 60>
<cfset var intro = "BugLog has received a new bug report">

<cfif variables.config.application neq "">
<cfset intro &= " for application #variables.config.application#">
</cfif>
<cfif variables.config.host neq "">
<cfset intro &= " on host #variables.config.host#">
</cfif>
<cfif variables.config.severity neq "">
<cfset intro &= " with a severity of #variables.config.severity#">
</cfif>
<cfset intro &= " in the last ">
<cfif numHours gt 0>
<cfset intro &= "#numHours# hour">
<cfif numHours gt 1>
<cfset intro &= "s">
</cfif>
<cfif numMinutes gt 0>
<cfset intro &= " and">
</cfif>
</cfif>
<cfif numMinutes gt 0>
<cfset intro &= " #numMinutes# minute">
<cfif numMinutes gt 1>
<cfset intro &= "s">
</cfif>
</cfif>

<cfset sendToSlack(
rawEntryBean = arguments.rawEntry,
comment = intro,
entryID = q.EntryID
)>

<cfset writeToCFLog("'firstMessageAlert' rule fired. Slack msg sent. Msg: '#q.message#'")>
</cffunction>

<cffunction name="getApplicationID" access="private" returntype="numeric">
<cfset var oDAO = getDAOFactory().getDAO("application")>
<cfset var oFinder = createObject("component","bugLog.components.appFinder").init(oDAO)>
Expand Down
12 changes: 11 additions & 1 deletion extensions/rules/frequencyAlert.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
<cfproperty name="severity" type="string" displayName="Severity Code" buglogType="severity" hint="The severity that will trigger the rule. Leave empty to look for all severities">
<cfproperty name="sameMessage" type="boolean" displayName="Same Message?" hint="Set to True to counts only bug reports that have the same text on their message. Leave empty or False to count all messages">
<cfproperty name="oneTimeAlertRecipient" type="string" hint="An email address to receive a one time short notification. This is sent only up to once per day.">
<cfproperty name="sendEmailAlert" type="boolean" displayName="Send Email Alert?" hint="When enabled, the alert is sent via email" default="true">
<cfproperty name="sendSlackAlert" type="boolean" displayName="Send Slack Alert?" hint="When enabled, the alert is sent via slack" default="false">


<cfset ID_NOT_SET = -9999999 />
<cfset ID_NOT_FOUND = -9999990 />
Expand All @@ -22,6 +25,9 @@
<cfargument name="severity" type="string" required="false" default="">
<cfargument name="sameMessage" type="string" required="false" default="">
<cfargument name="oneTimeAlertRecipient" type="string" required="false" default="">
<cfargument name="sendEmailAlert" type="string" required="false" default="true">
<cfargument name="sendSlackAlert" type="string" required="false" default="false">

<cfset variables.config.recipientEmail = arguments.recipientEmail>
<cfset variables.config.count = arguments.count>
<cfset variables.config.timespan = arguments.timespan>
Expand All @@ -30,15 +36,19 @@
<cfset variables.config.severity = arguments.severity>
<cfset variables.config.sameMessage = arguments.sameMessage>
<cfset variables.config.oneTimeAlertRecipient = arguments.oneTimeAlertRecipient>
<cfset variables.config.sendEmailAlert = (isBoolean(arguments.sendEmailAlert) and arguments.sendEmailAlert)>
<cfset variables.config.sendSlackAlert = (isBoolean(arguments.sendSlackAlert) and arguments.sendSlackAlert)>

<cfset variables.lastEmailTimestamp = createDateTime(1800,1,1,0,0,0)>
<cfset variables.lastOneTimeEmailTimestamp = createDateTime(1800,1,1,0,0,0)>
<cfset variables.applicationID = ID_NOT_SET>
<cfset variables.hostID = ID_NOT_SET>
<cfset variables.severityID = ID_NOT_SET>
<cfset variables.sameMessage = (isBoolean(variables.config.sameMessage) and variables.config.sameMessage)>

<cfreturn this>
</cffunction>

<cffunction name="processRule" access="public" returnType="boolean">
<cfargument name="rawEntry" type="bugLog.components.rawEntryBean" required="true">
<cfargument name="entry" type="bugLog.components.entry" required="true">
Expand Down
12 changes: 10 additions & 2 deletions extensions/rules/heartbeatMonitor.cfc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<cfcomponent extends="bugLog.components.baseRule"
<cfcomponent extends="bugLog.components.baseRule"
displayName="Heartbeat Monitor"
hint="This rule checks if we have received a matching message in X minutes. If not, an alert is sent.">

Expand All @@ -8,6 +8,8 @@
<cfproperty name="host" type="string" buglogType="host" displayName="Host Name" hint="The host name that will trigger the rule. Leave empty to look for all hosts">
<cfproperty name="severity" type="string" buglogType="severity" displayName="Severity Code" hint="The severity that will trigger the rule. Leave empty to look for all severities">
<cfproperty name="alertInterval" type="numeric" displayName="Alert Interval" hint="The number of minutes to wait between alert messages">
<cfproperty name="sendEmailAlert" type="boolean" displayName="Send Email Alert?" hint="When enabled, the alert is sent via email" default="true">
<cfproperty name="sendSlackAlert" type="boolean" displayName="Send Slack Alert?" hint="When enabled, the alert is sent via slack" default="false">

<cffunction name="init" access="public" returntype="bugLog.components.baseRule">
<cfargument name="recipientEmail" type="string" required="true">
Expand All @@ -16,19 +18,25 @@
<cfargument name="host" type="string" required="false" default="">
<cfargument name="severity" type="string" required="false" default="">
<cfargument name="alertInterval" type="string" required="false" default="">
<cfargument name="sendEmailAlert" type="string" required="false" default="true">
<cfargument name="sendSlackAlert" type="string" required="false" default="false">

<cfset variables.config.recipientEmail = arguments.recipientEmail>
<cfset variables.config.timespan = arguments.timespan>
<cfset variables.config.application = arguments.application>
<cfset variables.config.host = arguments.host>
<cfset variables.config.severity = arguments.severity>
<cfset variables.config.alertInterval = val(arguments.alertInterval)>
<cfset variables.config.sendEmailAlert = (isBoolean(arguments.sendEmailAlert) and arguments.sendEmailAlert)>
<cfset variables.config.sendSlackAlert = (isBoolean(arguments.sendSlackAlert) and arguments.sendSlackAlert)>

<cfset variables.applicationID = -1>
<cfset variables.hostID = -1>
<cfset variables.severityID = -1>
<cfset variables.lastEmailTimestamp = createDateTime(1800,1,1,0,0,0)>
<cfreturn this>
</cffunction>

<cffunction name="processQueueEnd" access="public" returntype="boolean" hint="This method gets called AFTER each processing of the queue (only invoked when using the asynch listener)">
<cfargument name="queue" type="array" required="true">
<cfscript>
Expand Down
5 changes: 5 additions & 0 deletions hq/config/config.xml.cfm
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@
<init-param name="appService" serviceName="app" />
</service>

<!-- Slack service -->
<service name="slack" class="bugLog.components.hq.slackService">
<init-param name="appService" serviceName="app" />
</service>

<!-- RSS service -->
<service name="rss" class="bugLog.components.lib.rss">
</service>
Expand Down
Loading