Created
January 25, 2020 15:56
-
-
Save bennadel/844fcf526294169f043f5f9f0a0b1e4c to your computer and use it in GitHub Desktop.
Using The FusionReactor API (FRAPI) To Add Custom Instrumentation In Lucee CFML 5.2.9.40
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<cfscript> | |
// Get the running FusionReactor API (FRAPI) instance from the FRAPI factory class. | |
// -- | |
// Java Docs: https://www.fusion-reactor.com/frapi/7_0_0/com/intergral/fusionreactor/api/FRAPI.html | |
frapi = createObject( "java", "com.intergral.fusionreactor.api.FRAPI" ) | |
.getInstance() | |
; | |
// ------------------------------------------------------------------------------- // | |
// ------------------------------------------------------------------------------- // | |
// By default, FusionReactor will use the name of the application as defined in the | |
// Application.cfc ColdFusion framework component. However, we can set the name | |
// programmatically. | |
frapi.setTransactionApplicationName( "FRAPI-Testing" ); | |
// By default, FusionReactor will calculate the transaction name based on the request | |
// context. It actually seems to "understand" the fact that we're using Framework One | |
// (FW/1) in production and uses the "action" value as the transaction name. That's | |
// the beauty of using an APM product that is embedded within the ColdFusion and CFML | |
// community. That said, we can set the transaction name programmatically. | |
// -- | |
// See Framework Support: https://www.fusion-reactor.com/support/kb/frs-431/ | |
frapi.setTransactionName( "GET/feature-flag-test" ); | |
// ------------------------------------------------------------------------------- // | |
// ------------------------------------------------------------------------------- // | |
// SIMULATE FEATURE FLAG setting. | |
shouldUseExperiment = randRange( 0, 1 ); | |
// We can use the trace() method to aggregate arbitrary time-stamped data along with | |
// the request. This data gets placed in a "Traces" tab in the Request detail. | |
// -- | |
// CAUTION: At the time of this writing, this data is not accessible on the Cloud | |
// dashboard, only on the STANDALONE dashboard. And, this data SHOULD NOT be | |
// confused with the "Tracing" tab on the CLOUD dashboard, which is concerned with | |
// nested Transactions only. | |
frapi.trace( "Starting Experiment." ); | |
// Try tracing a Struct. | |
frapi.trace({ | |
usingExpeirment: yesNoFormat( shouldUseExperiment ) | |
}); | |
// Try tracing an Array. | |
frapi.trace([ | |
"DATE", | |
dateFormat( now() ), | |
"TIME (EST)", | |
timeFormat( now() ) | |
]); | |
// Let's imagine that this page is going to represent two different algorithms: the | |
// base one and an experimental one that we are testing with a feature flag. In | |
// order to see if our experiment is worthwhile, we're going to track the relative | |
// execution time of each approach. | |
startedAt = getTickCount(); | |
// CASE: Experiment. | |
if ( shouldUseExperiment ) { | |
frapi.trace( "Starting experimental case." ); | |
// We can associate arbitrary key-value pairs with the request. These will show | |
// up in the "Properties" tab of the request detail. | |
// -- | |
// NOTE: At the time of this writing, these properties are not accessible on the | |
// Cloud dashboard, only on the Standalone dashboard. | |
frapi.getActiveTransaction() | |
.setProperty( "Features - Optimizations - Test", "True" ) | |
; | |
// In addition to the timing metrics which we are going to record, we can also | |
// create segments, aka "sub transactions", that help us map the relative | |
// execution time for parts of the request. | |
// -- | |
// NOTE: In the CLOUD dashboard, these show up in the "Traces" tab of a | |
// Transaction detail. In the STANDALONE dashboard, these who up in the | |
// "Relations" tab of a Transaction detail. | |
// -- | |
// NOTE: In the CLOUD dashboard, these can also be found in the "Flavor" dropdown | |
// of the Transactions tab. In the STANDALONE dashboard, these can also be | |
// graphed under the Transactions section. | |
// -- | |
// NOTE: When naming a transaction, I was running into issues in the STANDALONE | |
// dashboard if I used any kind of path-style notation (either "/" or "."): only | |
// the last segment of the "path" would show up, but would have no values. | |
try { | |
subtransaction = frapi.createTrackedTransaction( "HeavyProcessing-Exp-Start" ); | |
// SIMULTATE algorithm time. | |
sleep( randRange( 25, 50 ) ); | |
} finally { | |
subtransaction.close(); | |
} | |
try { | |
subtransaction = frapi.createTrackedTransaction( "HeavyProcessing-Exp-End" ); | |
// SIMULTATE algorithm time. | |
sleep( randRange( 25, 50 ) ); | |
} finally { | |
subtransaction.close(); | |
} | |
// CASE: Default. | |
} else { | |
frapi.trace( "Starting default case." ); | |
// We can associate arbitrary key-value pairs with the request. These will show | |
// up in the "Properties" tab of the request detail. | |
// -- | |
// NOTE: At the time of this writing, these properties are not accessible on the | |
// Cloud dashboard, only on the Standalone dashboard. | |
frapi.getActiveTransaction() | |
.setProperty( "Features - Optimizations - Test", "False" ) | |
; | |
// In addition to the timing metrics which we are going to record, we can also | |
// create segments, aka "sub transactions", that help us map the relative | |
// execution time for parts of the request. | |
// -- | |
// NOTE: In the CLOUD dashboard, these show up in the "Traces" tab of a | |
// Transaction detail. In the STANDALONE dashboard, these who up in the | |
// "Relations" tab of a Transaction detail. | |
// -- | |
// NOTE: In the CLOUD dashboard, these can also be found in the "Flavor" dropdown | |
// of the Transactions tab. In the STANDALONE dashboard, these can also be | |
// graphed under the Transactions section. | |
// -- | |
// NOTE: When naming a transaction, I was running into issues in the STANDALONE | |
// dashboard if I used any kind of path-style notation (either "/" or "."): only | |
// the last segment of the "path" would show up, but would have no values. | |
try { | |
subtransaction = frapi.createTrackedTransaction( "HeavyProcessing-Base-Start" ); | |
// SIMULTATE algorithm time. | |
sleep( randRange( 1000, 2500 ) ); | |
} finally { | |
subtransaction.close(); | |
} | |
try { | |
subtransaction = frapi.createTrackedTransaction( "HeavyProcessing-Base-End" ); | |
// SIMULTATE algorithm time. | |
sleep( randRange( 1000, 2500 ) ); | |
} finally { | |
subtransaction.close(); | |
} | |
} // END: Processing experiment. | |
duration = ( getTickCount() - startedAt ); | |
// In addition to the Transaction-based recording we're doing above, we can also | |
// record custom metrics which we can then graph in the FusionReactor dashboard. | |
// -- | |
// NOTE: FusionReactor's documentation seems to use a path-style notation when naming | |
// custom metrics. As such, I am just following the same pattern in the following | |
// metric examples. | |
// -- | |
// NOTE: The metrics are always available in the STANDALONE version of the | |
// FusionReactor dashboard; but, to stream them to the CLOUD dashboard as well, the | |
// metric has to be explicitly enabled with .enableCloudMetric(), and only works for | |
// NUMERIC AGGREGATE metrics. | |
if ( shouldUseExperiment ) { | |
frapi.postNumericAggregateMetric( "/optimizations/exp/duration", duration ); | |
frapi.enableCloudMetric( "/optimizations/exp/duration" ); | |
} else { | |
frapi.enableCloudMetric( "/optimizations/base/duration" ); | |
frapi.postNumericAggregateMetric( "/optimizations/base/duration", duration ); | |
} | |
frapi.trace( "Ending Experiment." ); | |
</cfscript> | |
<!--- ------------------------------------------------------------------------------ ---> | |
<!--- ------------------------------------------------------------------------------ ---> | |
<script> | |
// Simulate regular throughput / traffic to this endpoint by refreshing. | |
setTimeout( | |
function() { | |
window.location.reload(); | |
}, | |
1000 | |
); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment