Created
January 18, 2023 20:07
-
-
Save JamoCA/539f14ed547ab8824c221b082ffb1bda to your computer and use it in GitHub Desktop.
ColdFusion 2016 compatible version of Ben Nadel's Instrumenter.CFC
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
component | |
output = false | |
hint = "I instrument a given ColdFusion component." | |
{ | |
/* | |
2023-01-18 ColdFusion 2016 compatible version of Ben Nadel's Instrumenter.CFC | |
https://www.bennadel.com/blog/4390-dynamically-instrumenting-coldfusion-component-methods-with-gettickcount-to-locate-performance-bottlenecks.htm | |
https://gist.github.com/bennadel/2dac819075dfce0d9b993f8ea343707c#file-instrumenter-cfc | |
timings = new Instrumenter().instrument( CFCInstance ); | |
writeDump( | |
var = timings, | |
label = "CFCInstance Execution Times" | |
); | |
*/ | |
/** | |
* I instrument each method within the given target ColdFusion component and return a | |
* struct that will contain the aggregate execution times for each method, by name. By | |
* default, the timings will be recorded in Milliseconds; but, you can pass-in "nano" | |
* for high-precision readings. | |
*/ | |
public struct function instrument( | |
required any target, | |
string units = "milli" | |
) { | |
var timings = {}; | |
// Slide the injector method right into the public scope and invoke it. | |
target.__inspector__ = variables.inspector; | |
target.__inspector__( timings, units ); | |
return( timings ); | |
} | |
// --- | |
// PRIVATE METHODS. | |
// --- | |
/** | |
* I inspect the CURRENT CONTEXT, replacing each method with one that proxies the | |
* original methods and records the execution times. | |
*/ | |
private void function inspector( | |
required struct targetMethodTimings, | |
required string targetMethodUnits | |
) { | |
private numeric function getTickCount2(string unit="milli") hint="Returns the native system time in milliseconds." { | |
if (!structkeyexists(request, "udf_instrumenter_nanotime")) request.udf_instrumenter_nanotime = createObject("java", "java.lang.System"); | |
return (arguments.unit eq "nano") ? request.udf_instrumenter_nanotime.nanoTime() : getTickCount(); | |
// return (arguments.unit eq "nano") ? createObject("java", "java.lang.System").nanoTime() : getTickCount(); | |
} | |
// CAUTION: This method is being invoked IN THE CONTEXT OF THE TARGET COMPONENT. | |
// As such, both the THIS and VARIABLES references here are scoped to the target | |
// component, not to the Instrumenter component! | |
getMetadata( this ).functions.each( | |
function( method ){ | |
var scope = ( method.access eq "public" ) | |
? this | |
: variables | |
; | |
var originalName = method.name; | |
var proxyName = ( "__" & method.name & "__" ); | |
// Setup the default timings entry for this method. | |
targetMethodTimings[ originalName ] = 0; | |
// COPY the original function reference into the private scope where it | |
// can be invoked from our proxy function without muddying-up the public | |
// API of the original component. | |
variables[ proxyName ] = scope[ originalName ]; | |
// OVERRIDE the original function with our properly-scoped proxy. | |
scope[ originalName ] = function(){ | |
var startedAt = getTickCount2( targetMethodUnits ); | |
try { | |
return( invoke( variables, proxyName, arguments ) ); | |
} finally { | |
targetMethodTimings[ originalName ] += ( getTickCount2( targetMethodUnits ) - startedAt ); | |
} | |
}; | |
} | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment