Finding Template Execution Stack in ColdFusion
There have been times when I wanted to get a list of all the ColdFusion templates that were executed during a single page call. I wanted to get a list similar to that of the debugging information that shows at the bottom of the page when debugging output is turned on. To figure it out, I looked at the template that handles the current debugging. When you look in the file, getting this information is actually really simple. To get a stack trace, you just have to create a ColdFusion service factory and ask for the debugging service:
<!--- Create ColdFusion service factory instance. --->
<cfset objFactory = CreateObject(
"java",
"coldfusion.server.ServiceFactory"
) />
<!--- Get the debugging service from the service factory. --->
<cfset objDebugging = objFactory.GetDebuggingService() />
<!---
Get the events table. This includes all events
that have taken place, not just template executions.
This is returned as a query.
--->
<cfset qEvents = objDebugging.GetDebugger().GetData() />
<!---
Now that we have all the events in query format, do a
query of queries to get only events that were template
executions event.
--->
<cfquery name="qTemplates" dbtype="query">
SELECT
line,
parent,
template,
endtime,
starttime
FROM
qEvents
WHERE
type = 'Template'
ORDER BY
template ASC
</cfquery>
<!--- Dump out the query of template. --->
<cfdump var="#qTemplates#" />
That's all there is to it. As far as I know, the ServiceFactory stuff was not documented in ColdFusion MX 6, and was considered "unsupported", but I think that in ColdFusion 7, this is a fully supported factory object and is available for programmers to leverage.
Want to use code from this post? Check out the license.
Reader Comments
Hi Ben,
Thanks for posting this -- just this morning I needed code to solve this problem (for my debugging library) and your sample works great. I've refactored it into a UDF, and made a few tweaks. Would you mind if I submitted the UDF to CFLib.org? I'll of course give you credit for the original work.
Thanks again.
Nolan
@Nolan,
I am sure I learned this trick somewhere else (not sure I deserve credit).
Hey Ben,
When I try implementing this I keep getting a "Value Must Be Initialized Before Use" error. I am assuming this may have to do with the fact that I do not have debug turned on inside CF Administrator. Is it possible in invoke this without turning on debug in the administrator...or is there a way to turn on debug for one page instance and not the entire server? There are times when I only want to debug for a single page that is giving me trouble but I don't want to risk showing debug details to everyone, especially in a production environment.
Any insight would be most excellent and much appreciated.
Ron
@Ron,
From what I remember, you *might* need to have debugging turned on.
You could try to throw an error and then catch it and look at the tag context... this is an "expensive" approach; but, if you're doing it for debugging, it should work.
Once again Ben, You Da Man!
@Paul,
Awesome - glad this helped.
I am doing code cleanup on a project and need to collect the templates that are unused. If we have the set of templates used then obviously the unused are just the complement of that set. I use a similar method the collect the templates(and if a cfc, the method called):
<cftry>
<cfobject action="CREATE" type="JAVA" class="coldfusion.server.ServiceFactory" name="factory">
<cfset cfdebugger = factory.getDebuggingService()>
<cfcatch type="Any"></cfcatch>
</cftry>
<cfobject action="CREATE" type="JAVA" class="java.lang.String" name="ST">
<cfset qEvents = cfdebugger.getDebugger().getData()>
<cfquery dbType="query" name="cfdebug_templates_summary" debug="false">
SELECT distinct template
FROM qEvents
WHERE type = 'Template'
</cfquery>
<cfloop query="cfdebug_templates_summary">
<cfscript>
//need a java string so we can split on a regex
temp=ST.init(template);
a=temp.split("\sfrom\s");
temp2=trim(a[arraylen(a)]);
re="\|(.*?)(\(|\])";
//now find the method called
sRet=refind(re,temp,1,true);
if(arraylen(sRet.pos)>1){
temp3=trim(mid(temp,sRet.pos[2],sRet.len[2]));
}else{
temp3="";
}
a=ST.init(temp2).split("wwwroot\\");
out=a[arraylen(a)];
if(temp3!=""){
out&=":" & temp3;
}
</cfscript>
<cflog file="templatesused" text="#out#">
</cfloop>
Hi Ben,
I need to know if I can reach a serviceFactory Class but per coldfusion instance, specifically.
Any ideas on how to pull the template list if you *don't* have debugging turned on? I can't get debugging turned on but it would sure save my sanity if I could see that template stack.
@Ben
"@Ron, From what I remember, you *might* need to have debugging turned on."
I was getting the same error as Ron, on top of Enable Request Debugging Output, I had to add my IP address to the Debugging IP Addresses.
Also the Post Comment button no worky in FireFox 4.
You can also get the tag context via:
<cfset oException = createObject("java","java.lang.Exception").init() >
<cfdump var="#oException#">
Now, what I want is to get my hands on the cfquery /cfstoredproc information including the starttime/enddimt values so I can instrument our DB interactions and run some coverage analysis. This information is available in the bjDebugging.GetDebugger().GetData() object but, as has been noted, I need to have "Enable Request Debugging" turned on. I'm sure this info is maintained somewhere in the java bowels.