Skip to content

Instantly share code, notes, and snippets.

@JamoCA
Created January 18, 2023 20:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JamoCA/539f14ed547ab8824c221b082ffb1bda to your computer and use it in GitHub Desktop.
Save JamoCA/539f14ed547ab8824c221b082ffb1bda to your computer and use it in GitHub Desktop.
ColdFusion 2016 compatible version of Ben Nadel's Instrumenter.CFC
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