Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Release PR for 2.1.0 #67

Merged
merged 2 commits into from
Jan 21, 2025
Merged
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: 4 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ History and Plan

- Making Raygun4CFML a Coldbox module OR building a Coldbox module wrapper using Raygun4CFML as a dependency.
- Add breadcrumbs (#46)

2.0.2 Plan

- Add support for response.statusCode (#48)
- Integration and adaptation to Coldbox error reporting, adding CB HMVC app/API samples
- Cleanup `legacy` directory

2.1.0 (Jan 21 2025)

- Add support for response.statusCode and statusDescription (#48)

2.0.1 (Jan 13 2025)

- Fixed issue with ACF Content filtering and CGI-Scope
Expand Down
4 changes: 2 additions & 2 deletions box.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name":"raygun4cfml",
"version":"2.0.1",
"location":"MindscapeHQ/raygun4cfml#2.0.1",
"version":"2.1.0",
"location":"MindscapeHQ/raygun4cfml#2.1.0",
"author":"Kai Koenig <[email protected]>",
"homepage":"https://github.com/MindscapeHQ/raygun4cfml/",
"documentation":"https://github.com/MindscapeHQ/raygun4cfml/blob/master/README.md",
Expand Down
1 change: 1 addition & 0 deletions samples/app-cfc-no-filter/missing_include.cfm
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<cfinclude template="i-am-missing.cfm">
3 changes: 2 additions & 1 deletion samples/app-cfc-settings/Application.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ component extends="samples.Application" {
.setFullName( "Tester" );

// Increase max payload size to ensure detailed error data is captured
var settings = new com.raygun.environment.RaygunSettings().setRawDataMaxLength( 10000 );
// Set default status code to 418 to indicate that the error responses are from a teapot
var settings = new com.raygun.environment.RaygunSettings().setRawDataMaxLength( 10000 ).setStatusCode( 418 );

var raygun = new com.raygun.RaygunClient(
apiKey = variables.RAYGUNAPIKEY,
Expand Down
3 changes: 3 additions & 0 deletions server-adobe-2023.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@
"http":{
"port":"9193"
}
},
"scripts":{
"onServerInitialInstall":"cfpm install debugger"
}
}
4 changes: 2 additions & 2 deletions src/com/raygun/RaygunClient.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,13 @@ component accessors="true" {
augmentedIssueData[ "groupingKey" ] = arguments.groupingKey;
}

var message = new message.RaygunMessage();
var raygunSettings = {};
// Only apply settings if they've been properly initialized
if ( isInstanceOf( getSettings(), "RaygunSettings" ) ) {
var raygunSettings = getSettings().getSettings();
}
var messageContent = message.build( augmentedIssueData, raygunSettings );

var messageContent = new message.RaygunMessage( settings = raygunSettings ).build( augmentedIssueData );

// Apply content filtering if configured to protect sensitive data
if (
Expand Down
12 changes: 11 additions & 1 deletion src/com/raygun/environment/RaygunConfig.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ component {

// Client identifiers used for error tracking and debugging
RAYGUN_CLIENT_NAME = "raygun4cfml";
RAYGUN_CLIENT_VERSION = "2.0.1";
RAYGUN_CLIENT_VERSION = "2.1.0";
RAYGUN_CLIENT_URL = "https://github.com/MindscapeHQ/raygun4cfml";

// New default status code
DEFAULT_STATUS_CODE = 500;
}

/**
Expand Down Expand Up @@ -51,4 +54,11 @@ component {
return static.RAYGUN_CLIENT_URL;
}

/**
* Returns the default HTTP status code for error responses.
*/
public static function getDefaultStatusCode() {
return static.DEFAULT_STATUS_CODE;
}

}
12 changes: 10 additions & 2 deletions src/com/raygun/environment/RaygunSettings.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,22 @@ component accessors="true" {
* This helps prevent memory issues and ensures consistent API payload sizes.
*/
property name="rawDataMaxLength" type="numeric";
property name="statusCode" type="numeric";

public RaygunSettings function init( numeric rawDataMaxLength = com.raygun.environment.RaygunConfig::RAW_DATA_MAX_LENGTH_DEFAULT ) {
public RaygunSettings function init(
numeric rawDataMaxLength = com.raygun.environment.RaygunConfig::RAW_DATA_MAX_LENGTH_DEFAULT,
numeric statusCode = com.raygun.environment.RaygunConfig::getDefaultStatusCode()
) {
setRawDataMaxLength( rawDataMaxLength );
setStatusCode( statusCode );
return this;
}

public struct function getSettings() {
return { "rawDataMaxLength" : getRawDataMaxLength() };
return {
"rawDataMaxLength" : getRawDataMaxLength(),
"statusCode" : getStatusCode()
};
}

}
27 changes: 16 additions & 11 deletions src/com/raygun/message/RaygunMessage.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,23 @@
*/
component accessors="true" {

property name="settings" type="struct";

// Handles the detailed error information including stack traces, request data, etc
property name="raygunMessageDetails" type="RaygunMessageDetails";

public RaygunMessage function init( RaygunMessageDetails raygunMessageDetails = new RaygunMessageDetails() ) {
setRaygunMessageDetails( arguments.raygunMessageDetails );
public RaygunMessage function init(
RaygunMessageDetails raygunMessageDetails,
struct settings = {}
) {
setSettings( arguments.settings );
setRaygunMessageDetails(
!isNull( arguments.raygunMessageDetails ) && isInstanceOf(
arguments.raygunMessageDetails,
"RaygunMessageDetails"
) ? arguments.raygunMessageDetails : new RaygunMessageDetails( settings = getSettings() )
);

return this;
}

Expand All @@ -21,21 +33,14 @@ component accessors="true" {
* consistent error chronology across different server timezones.
*
* @issueData The struct containing issue data augmented with Raygun-specific data
* @settings Optional configuration settings that modify message construction
*/
public struct function build(
required struct issueData,
struct settings = {}
) {
public struct function build( required struct issueData ) {
var returnContent = {};
// Convert to UTC for consistent timestamps across different server timezones
var ts = dateConvert( "local2Utc", now() );

returnContent[ "occurredOn" ] = ts.dateFormat( "yyyy-mm-dd" ) & "T" & ts.timeFormat( "HH:mm:ss" ) & "Z";
returnContent[ "details" ] = raygunMessageDetails.build(
arguments.issueData,
arguments.settings
);
returnContent[ "details" ] = raygunMessageDetails.build( arguments.issueData );

return returnContent;
}
Expand Down
60 changes: 45 additions & 15 deletions src/com/raygun/message/RaygunMessageDetails.cfc
Original file line number Diff line number Diff line change
@@ -1,25 +1,58 @@
/**
* Represents the core message details for a Raygun error report.
* This component aggregates all the different aspects of an error report including
* exception details, request information, client data, and environment specifics.
* exception details, request information, client data, environment specifics, and response data.
*/
component accessors="true" {

property name="settings" type="struct";

property name="raygunExceptionMessage" type="RaygunExceptionMessage";
property name="raygunRequestMessage" type="RaygunRequestMessage";
property name="raygunClientMessage" type="RaygunClientMessage";
property name="raygunEnvironmentMessage" type="RaygunEnvironmentMessage";
property name="raygunResponseMessage" type="RaygunResponseMessage";

public RaygunMessageDetails function init(
RaygunExceptionMessage raygunExceptionMessage = new RaygunExceptionMessage(),
RaygunRequestMessage raygunRequestMessage = new RaygunRequestMessage(),
RaygunClientMessage raygunClientMessage = new RaygunClientMessage(),
RaygunEnvironmentMessage raygunEnvironmentMessage = new RaygunEnvironmentMessage()
RaygunExceptionMessage raygunExceptionMessage,
RaygunRequestMessage raygunRequestMessage,
RaygunClientMessage raygunClientMessage,
RaygunEnvironmentMessage raygunEnvironmentMessage,
RaygunResponseMessage raygunResponseMessage,
struct settings = {}
) {
setRaygunExceptionMessage( arguments.raygunExceptionMessage );
setRaygunRequestMessage( arguments.raygunRequestMessage );
setRaygunClientMessage( arguments.raygunClientMessage );
setRaygunEnvironmentMessage( arguments.raygunEnvironmentMessage );
setSettings( arguments.settings );
setRaygunExceptionMessage(
!isNull( arguments.raygunExceptionMessage ) && isInstanceOf(
arguments.raygunExceptionMessage,
"RaygunExceptionMessage"
) ? arguments.raygunExceptionMessage : new RaygunExceptionMessage()
);
setRaygunRequestMessage(
!isNull( arguments.raygunRequestMessage ) && isInstanceOf(
arguments.raygunRequestMessage,
"RaygunRequestMessage"
) ? arguments.raygunRequestMessage : new RaygunRequestMessage()
);
setRaygunClientMessage(
!isNull( arguments.raygunClientMessage ) && isInstanceOf(
arguments.raygunClientMessage,
"RaygunClientMessage"
) ? arguments.raygunClientMessage : new RaygunClientMessage()
);
setRaygunEnvironmentMessage(
!isNull( arguments.raygunEnvironmentMessage ) && isInstanceOf(
arguments.raygunEnvironmentMessage,
"RaygunEnvironmentMessage"
) ? arguments.raygunEnvironmentMessage : new RaygunEnvironmentMessage()
);
setRaygunResponseMessage(
!isNull( arguments.raygunResponseMessage ) && isInstanceOf(
arguments.raygunResponseMessage,
"RaygunResponseMessage"
) ? arguments.raygunResponseMessage : new RaygunResponseMessage( settings = getSettings() )
);

return this;
}

Expand All @@ -29,12 +62,8 @@ component accessors="true" {
* that matches Raygun's API expectations.
*
* @issueData The core error data to be processed
* @settings Optional configuration settings that may affect how the request data is processed
*/
public struct function build(
required struct issueData,
struct settings = {}
) {
public struct function build( required struct issueData ) {
var returnContent = {};

// Grouping key allows for custom error grouping in Raygun's dashboard
Expand All @@ -58,9 +87,10 @@ component accessors="true" {

// Build the core components of the error report
returnContent[ "error" ] = raygunExceptionMessage.build( arguments.issueData );
returnContent[ "request" ] = raygunRequestMessage.build( arguments.settings );
returnContent[ "request" ] = raygunRequestMessage.build( getSettings() );
returnContent[ "client" ] = raygunClientMessage.build();
returnContent[ "environment" ] = raygunEnvironmentMessage.build();
returnContent[ "response" ] = raygunResponseMessage.build( arguments.issueData );

// Include any custom data if provided through a builder object
if ( arguments.issueData.keyExists( "userCustomData" ) && isObject( arguments.issueData.userCustomData ) ) {
Expand Down
14 changes: 8 additions & 6 deletions src/com/raygun/message/RaygunRequestMessage.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
* Captures essential request context including headers, form data, and raw request content
* while gracefully handling cases where certain scopes might be unavailable.
*/
component {
component accessors="true" {

property name="settings" type="struct";

public RaygunRequestMessage function init( struct settings = {} ) {
setSettings( arguments.settings );

public RaygunRequestMessage function init() {
return this;
}

Expand All @@ -14,10 +18,8 @@ component {
* Uses multiple data sources (CGI, FORM, URL scopes) with fallbacks to ensure
* we capture as much context as possible even if some scopes are restricted.
* Raw request data is truncated to prevent oversized payloads.
*
* @settings Optional configuration settings, primarily for controlling raw data length
*/
public struct function build( struct settings = {} ) {
public struct function build() {
var returnContent = {};

// Safely access request data - some environments restrict getHTTPRequestData()
Expand Down Expand Up @@ -69,7 +71,7 @@ component {
len( CGI.CONTENT_TYPE ) && len( CGI.REQUEST_METHOD ) && CGI.CONTENT_TYPE != "text/html" && CGI.CONTENT_TYPE != "application/x-www-form-urlencoded" && CGI.REQUEST_METHOD != "GET"
) {
var maxLength = (
arguments.settings.keyExists( "rawDataMaxLength" ) ? arguments.settings.rawDataMaxLength : com.raygun.environment.RaygunConfig::getRawDataMaxLengthDefault()
getSettings().keyExists( "rawDataMaxLength" ) ? getSettings().rawDataMaxLength : com.raygun.environment.RaygunConfig::getRawDataMaxLengthDefault()
);
returnContent[ "rawData" ] = left(
( httpRequest.keyExists( "content" ) ) ? httpRequest.content : javacast( "null", "" ),
Expand Down
101 changes: 101 additions & 0 deletions src/com/raygun/message/RaygunResponseMessage.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* Represents the response information to be included in the Raygun message.
*/
component accessors="true" {

property name="settings" type="struct";

static {
HTTP_STATUS_CODES = {
200 : "OK",
201 : "Created",
202 : "Accepted",
203 : "Non-Authoritative Information",
204 : "No Content",
205 : "Reset Content",
206 : "Partial Content",
207 : "Multi-Status",
208 : "Already Reported",
218 : "This is fine",
226 : "IM Used",
301 : "Moved Permanently",
302 : "Found",
303 : "See Other",
304 : "Not Modified",
305 : "Use Proxy",
306 : "Switch Proxy",
307 : "Temporary Redirect",
308 : "Permanent Redirect",
400 : "Bad Request",
401 : "Unauthorized",
402 : "Payment Required",
403 : "Forbidden",
404 : "Not Found",
405 : "Method Not Allowed",
406 : "Not Acceptable",
407 : "Proxy Authentication Required",
408 : "Request Timeout",
409 : "Conflict",
410 : "Gone",
411 : "Length Required",
412 : "Precondition Failed",
413 : "Payload Too Large",
414 : "URI Too Long",
415 : "Unsupported Media Type",
416 : "Range Not Satisfiable",
417 : "Expectation Failed",
418 : "I'm a teapot",
421 : "Misdirected Request",
422 : "Unprocessable Entity",
423 : "Locked",
424 : "Failed Dependency",
425 : "Too Early",
426 : "Upgrade Required",
428 : "Precondition Required",
429 : "Too Many Requests",
431 : "Request Header Fields Too Large",
451 : "Unavailable For Legal Reasons",
500 : "Internal Server Error",
501 : "Not Implemented",
502 : "Bad Gateway",
503 : "Service Unavailable",
504 : "Gateway Timeout",
505 : "HTTP Version Not Supported",
506 : "Variant Also Negotiates",
507 : "Insufficient Storage",
508 : "Loop Detected",
509 : "Bandwidth Limit Exceeded",
510 : "Not Extended",
511 : "Network Authentication Required"
};
}

public RaygunResponseMessage function init( struct settings = {} ) {
setSettings( arguments.settings );

return this;
}

/**
* Determines the status code based on the exception type.
* @param errorData The error structure from the crash report
*/
private numeric function determineStatusCode( required struct errorData ) {
if ( errorData.type == "MissingInclude" ) {
return 404;
}

return (
getSettings().keyExists( "statusCode" ) ? getSettings().statusCode : com.raygun.environment.RaygunConfig::getDefaultStatusCode()
);
}

public struct function build( required struct errorData ) {
var sc = determineStatusCode( arguments.errorData );
return {
"statusCode" : sc,
"statusDescription" : ( structKeyExists( static.HTTP_STATUS_CODES, sc ) ? static.HTTP_STATUS_CODES[ sc ] : "" )
};
}

}