Articles

CF_Timer Revisited

Timer functionality without enabling CF Debugging

Developer watching a stop watch to capture the performance.
May 1, 2025

If you wanted to use the built-in CFTimer function, Adobe required devs to specifically add their current IP address to the list of debugging IP addresses. This is inconvenient given that the task that it performs shouldn't really require special security... unless it's possible that Adobe's implementation of it could be compromised somehow. (Not sure, just guessing as I can't see any reason why this function requires special rules when you should be able to pass an option to suppress output under desired conditions.) Some third-party developers may not have access to CFAdmin or admin-based API functions, so they are left without any options other than to manually capture and output getTickCount().

It also supports "nanoTime" as an option as an attempt to provide accuracy down to the "one billionth of a second". (It's not perfect, but it is at least an option.)

A feature "fix" was submitted to Adobe back in 2022 to decouple the CFTimer from the admin debug output feature (CF-4214324), but it doesn't appear that there's been any movement. I recommend going to the tracker, adding a comment and upvoting it.

Source Code

<cfsilent><!---
CF_Timer (save to custom tag directory as CF_Timer.cfm)
Author: James Moberg
Date:	9/20/2013
Notes:	Called like cftimer but without enabling debugging. (NOTE: "debug" mode not supported.)
		Types: outline (default),
		Added "AllowedIPs".  Enter a list of IPs. Masks acceptable (127.0.0.1,192.168.*.*)
CREDITS: Initial "myTimer" CFTag by Chris Phillips ( http://www.cfchris.com/cfchris/index.cfm/2007/4/5/My-CFTIMER-Custom-Tag )
CFTIMER FUNCTION: https://cfdocs.org/cftimer
https://helpx.adobe.com/coldfusion/developing-applications/developing-cfml-applications/debugging-and-troubleshooting-applications/using-the-cftimer-tag-to-time-blocks-of-code.html
https://helpx.adobe.com/coldfusion/cfml-reference/coldfusion-tags/tags-t/cftimer.html
--->

<cfparam name="attributes.label" default="cftimer">
<cfparam name="attributes.type" default="outline"><!--- comment, inline, outline ("debug" not supported) --->
<cfparam name="attributes.variable" default="">
<cfparam name="attributes.allowedIPs" default="">
<cfparam name="attributes.nano" type="boolean" default="false">
<cfparam name="attributes.returnType" type="string" default=""> <!--- "ns", "μs", "ms", "s" --->

<cfscript>
numeric function getNano() hint="returns nano time (more accurate)" {
	return createobject("java", "java.lang.System").nanoTime();
}
numeric function getTick() hint="Railo or OpenBD may not return ms since EPOC" {
	local.tickCount = gettickcount();
	if (!findnocase("ColdFusion", server.coldfusion.productname)){
		try {
			local.tickCount = createobject("java", "java.lang.System").currentTimeMillis();
		} catch(any e){};
	}
	return local.tickCount;
}
string function prettyDecimal(numeric inputString=0){
	local.response = trim(rereplace(numberformat(val(arguments.inputString), '9,999.999999999999'), "0+$", ""));
	return local.response.replaceAll("\.$", "");
}
</cfscript>

</cfsilent>
<cfif thistag.executionmode eq "Start">
	<cfset variables.startTimer = (attributes.nano) ? getNano() : getTick()>
<cfelseif thistag.executionmode eq "End">
	<cfset variables.endTimer = (attributes.nano) ? getNano() : getTick()>
	<cfset variables.final = (variables.endTimer - variables.startTimer)>
	<cfif len(attributes.returnType)>
		<cfif attributes.nano>
			<cfset attributes.returnType = (variables.final gt 10000000000) ? "s" : "ms">
		<cfelse>
			<cfset attributes.returnType = (variables.final gt 10000) ? "s" : "ms">
		</cfif>
	</cfif>
	<cfswitch expression="#attributes.returnType#">
		<cfcase value="ns nanoseconds" delimiters=" ">
			<cfif attributes.nano>
				<cfset variables.PerfTime = (prettyDecimal(variables.final) & " ns")>
			<cfelse>
				<cfset variables.PerfTime = (prettyDecimal(variables.final * 1000000) & " ns")>
			</cfif>
		</cfcase>
		<cfcase value="μs microseconds" delimiters=" ">
			<cfif attributes.nano>
				<cfset variables.PerfTime = (prettyDecimal(variables.final / 1000) & " μs")>
			<cfelse>
				<cfset variables.PerfTime = (prettyDecimal(variables.final * 1000) & " μs")>
			</cfif>
		</cfcase>
		<cfcase value="ms milliseconds" delimiters=" ">
			<cfif attributes.nano>
				<cfset variables.PerfTime = (prettyDecimal(variables.final / 1000000) & " ms")>
			<cfelse>
				<cfset variables.PerfTime = (prettyDecimal(variables.final) & " ms")>
			</cfif>
		</cfcase>
		<cfcase value="s seconds" delimiters=" ">
			<cfif attributes.nano>
				<cfset variables.PerfTime = (prettyDecimal(variables.final / 1000000000) & " s")>
			<cfelse>
				<cfset variables.PerfTime = (prettyDecimal(variables.final / 1000) & " s")>
			</cfif>
		</cfcase>
		<cfdefaultcase>
			<cfset variables.PerfTime = variables.final>
		</cfdefaultcase>
	</cfswitch>

	<cfif len(attributes.allowedIPs)>
		<cfset ipAddress = CGI.REMOTE_ADDR>
		<cfset validIP = 0>
		<cfloop index="i" from="4" to="1" step="-1">
			<cfset ipAddress = listsetat(ipAddress, i, "*", ".")>
			<cfif listfind(attributes.allowedIPs, ipAddress)>
				<cfset validIP = 1>
				<cfbreak>
			</cfif>
		</cfloop>
		<cfif !validIP>
			<cfexit>
		</cfif>
	</cfif>
	<cfif len(trim(attributes.variable)) && isvalid("variableName", attributes.variable)>
		<cfset caller[attributes.variable] = variables.PerfTime>
	<cfelseif attributes.type eq "outline">
		<cfoutput><fieldset style="word-break:break-word;"><legend><cfif len(attributes.Label)>#attributes.label#: </cfif>#variables.PerfTime#</legend>#THISTAG.GeneratedContent#</fieldset></cfoutput>
		<cfset THISTAG.GeneratedContent = "">
	<cfelseif attributes.type eq "inline">
		<cfoutput><cfif len(attributes.Label)>#attributes.label#: </cfif>#variables.PerfTime# ms</cfoutput>
	<cfelseif attributes.type eq "comment">
		<cfoutput><!-- <cfif len(attributes.Label)>#attributes.label#: </cfif>#variables.PerfTime# ms --></cfoutput>
	<cfelse>
		<!--- unknown, just output the content --->
	</cfif>
</cfif>

Example Usage

<cf_timer label="Sleep Test (tag syntax)" type="outline">
	<!--- Do some stuff --->
	<cfset sleep(50)>
</cf_timer>

<cfscript>
cf_timer(label="Sleep Test (cfscript syntax)"){
	sleep(50);
}
</cfscript>

<h2>STORE EXECUTION TIME (MILLISECONDS) AS VARIABLE "variables.TimeLapsed"</h2>

<cf_timer variable="TimeLapsed">
	<!--- Do some stuff --->
	<cfset sleep(50)>
</cf_timer>
<cfoutput><p>Time Lapsed = #TimeLapsed# ms</p></cfoutput>

<h2>STORE EXECUTION TIME (MILLISECONDS) AS VARIABLE "variables.TimeLapsed" with IP filter</h2>

<cf_timer allowedips="127.0.0.1,192.168.*.*">
	<!--- Do some stuff --->
	<cfset sleep(50)>
</cf_timer>