BoxLang 🚀 A New JVM Dynamic Language Learn More...
errorAlerts emails developers when a ColdBox application
has an unhandled exception or logs an ERROR or
FATAL message. Alerts include error details, request
data, source code, stack frames, and runtime versions when available.
By default, the first three occurrences of a matching error produce separate alerts within ten minutes. Further occurrences appear in one summary email, called a digest, after that time window ends. Email delivery runs in the background, so messages may not arrive right away.
The module registers itself with ColdBox. You do not need to change the configuration for LogBox, ColdBox's logging system.
cbmailservices installs automatically. Its default mailer uses your engine's SMTP settings. If your application already configures cbmailservices, errorAlerts uses its default mailer.
Run this command from your ColdBox application's root folder:
box install erroralerts
Create config/modules/errorAlerts.cfc:
component {
function configure(){
return {
to : "[email protected]",
from : "[email protected]",
subjectPrefix : "[My App]",
maskKeys : [ "password", "token", "apiKey" ]
};
}
}
Replace the addresses with addresses your mail server accepts.
to is required for email alerts. If from is
blank, the module uses to as the sender.
Masking is off by default. The example enables it for
three field names. Add every sensitive field name your application
uses before sending real request data in alerts. Matching ignores case
but requires the full name: password does not match
newPassword or passwordConfirm.
Matching values become *** masked *** in the request
collection (rc, which includes form and URL fields), JSON
request bodies, logged extraInfo, and query strings. See
the masking
guide for limits and patterns.
Alerts are enabled in every environment, including development.
Restart the application or reinitialize ColdBox with
?fwreinit=true after changing settings.
Add this temporary action to an existing ColdBox handler:
function testError( event, rc, prc ){
throw(
type = "MyApp.TestError",
message = "Testing errorAlerts"
);
}
Visit the action in your browser. For example, if you added it to
handlers/Main.cfc, open
index.cfm?event=Main.testError. You should receive an
alert containing the test error. Allow time for queued delivery.
Remove the action after confirming delivery. If no email arrives, use the checklist below.
Exceptions that reach ColdBox's exception handler are reported automatically. You do not need to add reporting code to each handler.
To report a caught error, inject a LogBox logger and log at
ERROR or FATAL. For example, add a method
like this to a component created by WireBox, such as a handler or service:
component {
property name="log" inject="logbox:logger: {this}";
function reportError( required any exception ){
log.error(
message = "Order could not be saved",
extraInfo = arguments.exception
);
}
}
Call reportError( exception ) from your catch block and
choose a message that describes the failed operation.
extraInfo carries the caught exception so the alert can
include its type, detail, and stack frames.
For pages that run outside ColdBox, use the Application.cfc example.
ColdBox catches a failing scheduled task and logs two
ERROR messages, one with the message and one with the
stack trace, under the category
coldbox.system.web.tasks.ColdBoxScheduledTask. Those
messages reach errorAlerts only when that category uses the root
logger's appenders. ColdBox's application template sets info : [
"coldbox.system" ] in its LogBox config, which ties
coldbox.system to the appenders that exist at startup,
before errorAlerts adds its own. With that config, report task
failures yourself in onFailure(). This also gives the
alert the exception's stack frames:
task( "cleanup" )
.call( () => getInstance( "UploadService" ).deleteOldTempFiles() )
.onFailure( function( task, exception ){
log.error( "Scheduled task cleanup failed: #arguments.exception.message#", arguments.exception );
} )
.everyDay();
ColdBox copies the request that started the application into every
scheduled task and async thread. Without special handling, a task
alert would show that unrelated request's URL, event, and visitor.
errorAlerts detects these threads and reports them like code outside
ColdBox: the alert has a "not a ColdBox request" row and
leaves out the URL, user, routing rows, CGI rows, headers, session ID,
request body, and rc. The origin, machine, memory,
runtime versions, message, and stack trace remain. Digests for these
errors list no routes or client addresses.
The context row in the Request section says where the
error ran, followed by the Java thread name:
context
|
Meaning |
|---|---|
web request (XNIO-1 task-3)
| A web request. |
scheduled task (pool-2-thread-1)
| A ColdBox scheduled task. |
async thread (ForkJoinPool.commonPool-worker-1)
| Other ColdBox async work, such as
asyncManager.newFuture() or Adobe's runAsync(). |
background thread (...)
| Any other thread without a request, such as some
cfthread threads. |
Adobe ColdFusion, Lucee, and BoxLang each mark these threads
differently, and errorAlerts checks the mark for the running engine.
If that check fails, it falls back to the Java executor's default
thread names. cfthread keeps its previous label on every engine.
Set these values in the struct returned by configure()
in config/modules/errorAlerts.cfc.
| Setting | Default | Purpose |
|---|---|---|
to
| ""
| Recipient address. Required for email alerts. |
from
| ""
| Sender address. Uses to when blank. |
subjectPrefix
| ""
| Text before the subject. Uses the app name when blank. |
enabled
| true
| Set to false to stop alerts. |
deliveryMode
| "queue"
| "queue" sends later;
"send" waits for delivery. |
maskKeys
| []
| Full field names whose values should be hidden. |
throttle.maxPerWindow
| 3
| Separate alerts allowed per matching error in a time window. |
throttle.windowSeconds
| 600
| Length of that time window in seconds. |
See the complete settings reference for other options.
Add this function inside the same configuration component, after configure():
function development( settings ){
settings.enabled = false;
}
ColdBox calls it when the detected environment is
development. Use the matching environment name for
another environment, such as staging. Change
settings directly; ColdBox ignores the function's return value.
Add this entry to the struct returned by configure() to
allow one separate alert per matching error in five minutes:
throttle : {
maxPerWindow : 1,
windowSeconds : 300
}
Further occurrences still appear in a digest. You can override only the nested keys you need; missing throttle settings keep their defaults.
Masking does not remove secrets from plain text, SQL parameter values, or fields inside objects and queries. Log only the data you need. Request bodies are off by default; non-JSON bodies can appear as sent if you enable them. Review the masking limits before including sensitive data.
Check these items in order:
to and confirm enabled is
true, including any environment override.ERROR or
FATAL. Logged WARN messages are excluded
by default.errorAlerts.Email is queued by default. Repeated errors may wait for a digest instead of producing another separate email. See troubleshooting for delivery delays, ignored errors, and missing digests.
MIT
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
web request and
showed that request's URL, event, routing rows, rc, template path, client
address, user agent, and headers, all unchanged until the next reinit. The
module now detects these threads on Adobe ColdFusion, Lucee, and BoxLang
before it reads any request data. It labels them
scheduled task (<thread name>), or async thread (<thread name>) for other
ColdBox async work such as newFuture(). They get the same "not a ColdBox
request" handling as code outside ColdBox, and the request rows, user, elapsed
time, session ID, rc, and request body are left out. The origin, machine,
memory, runtime, message, and stack trace are kept. Digest emails no longer
list the copied route and client address for these errors. Grouping of
repeated errors is unchanged, because signatures never included the URL or
event. cfthread keeps its previous label./trigger/taskFailure). A new integration spec uses it.CaptureService.captureException( exception ) reports an exception from code
that runs outside ColdBox, such as an Application.cfc onError() method for
an older folder that shares the ColdBox application. The method checks ignore
lists, limits repeated alerts, hides values selected by the masking settings,
and calls the notifier.
Repeated errors can appear in a digest email for the same time window. The
method never throws an exception back to the caller. It ignores aborts and
redirects, reports the original error from Adobe's EventHandlerException
wrapper, and prevents duplicate reports of the same error within a request.
On Lucee and BoxLang, the method reloads module mappings before reporting an
error. If onError() runs before ColdBox starts handling the request, those
mappings are missing. Without them, the first mail build fails and later
alerts fail until the application is reinitialized. See "Pages outside
ColdBox" in the README for the onError() example. The module's interceptor
now calls this method too..cfm
files under test-harness/legacy/. The harness's Application.cfc onError()
method reports errors from these files.coldbox row with "not a ColdBox request" instead of nine
N/A routing rows. For web requests, the request collection (rc) combines
FORM and URL values and applies maskKeys. Building an alert no longer
creates a ColdBox request context.CaptureService.capture() checks enabled each time it runs. WireBox can
create the service even when the module did not register its appender and
interceptor. This check prevents direct calls from sending alerts when the
module is disabled.EmailNotifier sends no email when to is blank. It prints a console warning
once.rc entries use the full width instead of a squeezed column.
Section padding shrinks from 20 to 12 pixels, and long source lines and stack
frame paths wrap instead of running off the side. Measured against the test
harness on a 360 pixel screen, the email went from 1249 pixels wide to 344.<table> fragment, which left phones to guess the layout width.exceptionFieldMaxLength, default 2048, controls how many characters of
the exception detail and extendedInfo fields the email shows. Both
limits were hardcoded at 1024 before. Set 0 to disable the cap;
maxBodyBytes still limits the whole email.<style> block holding a single
@media only screen and (max-width:600px) rule, and elements carry ea-
prefixed classes. Every element still carries the inline styles it had
before, so a mail client that ignores embedded style blocks, such as Outlook
on Windows, renders the same two-column layout as earlier versions.assemble() now subtracts the measured length of the document head and
footer from the maxBodyBytes budget instead of assuming a fixed 600
characters. Without this the larger head could push an email past the cap.white-space:pre and a table cannot shrink below its content. This behavior
is unchanged from earlier versions and only affects wide screens; small
screens now wrap those lines.maskKeys defaults to an empty array. The old
defaults were password, passwd, token, secret, apikey, api_key,
authorization, creditcard, and cvv. Applications that relied on those
defaults must now list their own sensitive fields. Applications that already
set maskKeys are not affected. The README provides a starter list.rc values and JSON bodies. Earlier
versions only checked top-level keys. JSON array bodies are also walked.includeScopes override
could remove rc, cgi, and extraInfo, which made alert building fail. A
partial throttle override could also disable throttling. Module startup now
restores missing keys without replacing application values.maskKeyPatterns adds wildcard field matching. * matches any number of
characters. Matching ignores case, covers the complete field name, and runs
only when maskKeys has no exact match.longValueMaxLength, default 500, limits each string in extraInfo or a
JSON body. Truncated values include their original size. Set it to 0 to
disable the per-value limit.longValueExemptKeys, default [ "_stacktrace", "stacktrace" ], keeps
selected values from using the per-value limit.extraInfoMaxLength, default 2000, makes the old hardcoded section limit
configurable.tests/specs/perf measures alert building with large payloads. Run it with
?directory=tests.specs.perf.[N more keys]. Every displayed
value still passes the mask check.rc
key and header.rc table now end with the size label instead of a
bare ellipsis.rc value now renders [null] instead of hiding the complete rc
table.maskKeys now checks extraInfo values up to five levels deep. This also
protects request headers that RestHandler places inside extraInfo, including
Authorization. The Request section's header blocklist does not cover this
separate path.detail, and exception extendedInfo remain
unmasked because they have no field names. The README now states this limit.onException. The reports use different categories
and messages, so throttling could not combine them. Category rules also could
not match every handler category.onException announcement contains the
tagContext needed for stack frames, application attribution, and source code.RestHandler.onError() logs without announcing, so its report is kept. This
covers errors in preHandler, postHandler, and aroundHandler.suppressDuplicateFrameworkLogs, default true, controls RestHandler
duplicate removal. Set it to false only after unregistering the module's
UnhandledExceptionCapture interceptor.maxPerWindow extra emails.relativePaths now defaults to true: paths inside the application root
render as /handlers/Main.cfc instead of the full absolute path. Paths
outside the root are still shown in full. Set the flag back to false for
absolute paths.stackFrames limit. applicationFramePrefixes defines application
paths.url row in the Request section, e.g.
GET https://example.com/posts/hello?page=2, with maskKeys applied to the
query string.elapsed row showing how long the request had been running when it
failed, on engines that expose the request start time (Lucee, BoxLang).digestSampleSize
limits both lists, and 0 disables them. A client can control
X-Forwarded-For, so the IP address is a hint rather than proof.These changes only affect project development and release tasks.
release:existing-tag publishes a tag that already points to HEAD. This
supports CI jobs triggered after a tag is created.release:hotfix is now also available under the clearer name
release:skip-tests. Both names still work.test:engines runs and reports every engine even after one fails.git pull --ff-only instead of git checkout -f. Starting a
release can no longer discard uncommitted work.maskKeys now matches complete field names and ignores case.
It no longer uses substring matching. For example, token does not match
apiToken or tokenCount. List every sensitive field name explicitly.sqlState, which is what Adobe reports for database errors.codeSnippetLines
controls its size.includeQueryParams).extendedInfo (where cbValidation puts
field-level errors).emptyValueText. New rows:
origin (the file and line that broke, even for a plain log.error() call),
user (via the new userProvider closure setting), routePattern,
routeName, routedModule, routedNamespace, view, layout, isAjax,
machine, context (web request / scheduled task / background thread),
memory (JVM heap use), sessionId (opt-in via includeScopes.session),
templatePath, forwardedFor, forwardedProto, accept,
acceptLanguage, https, and serverPort.includeHeaders, which defaults to Content-Type.
Cookie and Authorization are always blocked.includeRequestBody, off by default)
for JSON and API endpoints, masked with maskKeys and capped at
requestBodyMaxLength.maskQueryString applies maskKeys to GET query strings.relativePaths trims the application root from file paths.environments setting. Alerts now run in every environment. To disable
one environment, add a matching function to config/modules/errorAlerts.cfc
and set settings.enabled = false. The README contains an example.
$
box install erroralerts