Earlier today on Twitter @samhamilton and I shared a few messages about finding a replacement for cfdump. Apparently, some ColdFusion hosts lock down access to internal Java components which unfortunately breaks cfdump. He linked to this forum posting as an example. I wasn't able to replicate that on my ColdFusion 901 server so it may be an issue with 8 only. Either way I thought it would be fun to see if I could recreate cfdump quickly. I wrote the following tag in about 20 minutes (10 minutes for the initial version, then about 10 more minutes later on for small changes). It works best with arrays, structs, and queries, but will try to display a component as well. It also sniffs for JSON strings and will automatically deserialize them. The layout is - of course - not optimal. But it gets the job done. Here is a sample screen shot:

I've attached a zip of the custom tag to the bottom of this entry. But for those who just want to see the code, I've pasted it below. Basically it just does a few type checks and iterates through the data, recursively calling itself where it makes sense. Definitely not rocket science but maybe it will be useful. Of course, normally you don't want to ever use cfdump in production. But I've often used it for error emails and other logging/reporting needs.
Edit on October 7: I added basic XML support.
<cfparam name="attributes.var">
<cfparam name="attributes.top" default="">
<cfif len(attributes.top) and not isNumeric(attributes.top)>
<cfset attributes.top = "">
<cfelseif isNumeric(attributes.top) and (attributes.top lte 0 or round(attributes.top) neq attributes.top)>
<cfset attributes.top = "">
</cfif>
<cfif not structKeyExists(request, "_cffakedump")>
<cfoutput>
<style>
table, th, td {
border: 1px solid black;
}
td {
padding: 5px;
}
th, .key {
background-color: ##e3e392;
}
</style>
</cfoutput>
<cfset request["_cffakedump"] = 0>
<cfelse>
<!--- quick sanity check for pointers --->
<cfset request["_cffakedump"]++>
<cfif request["_cffakedump"] gt 1000>
<cfabort/>
</cfif>
</cfif>
<cfif isSimpleValue(attributes.var)>
<cfif attributes.var is "">
<cfoutput>[empty string]</cfoutput>
<cfelseif isJSON(attributes.var)>
<cfset newVar = deserializeJSON(attributes.var)>
<cfoutput><table><tr><th>JSON</th></tr><tr><td></cfoutput>
<cf_fakedump var="#newVar#" top="#attributes.top#">
<cfoutput></td></tr></table></cfoutput>
<cfelse>
<cfoutput>#attributes.var#</cfoutput>
</cfif>
<cfelseif isArray(attributes.var)>
<cfoutput>
<table>
<tr>
<th colspan="2">array (#arraylen(attributes.var)# items)</th>
</tr>
<cfif not len(attributes.top)>
<cfset attributes.top = arrayLen(attributes.var)>
</cfif>
<cfloop index="x" from="1" to="#min(arrayLen(attributes.var), attributes.top)#">
<tr valign="top">
<td class="key">#x#</td>
<td>
<cfif isSimpleValue(attributes.var[x])>
#attributes.var[x]#
<cfelse>
<cf_fakedump var="#attributes.var[x]#" top="#attributes.top#">
</cfif>
</td>
</tr>
</cfloop>
</table>
</cfoutput>
<cfelseif isObject(attributes.var)>
<cfset data = getMetadata(attributes.var)>
<cfoutput><table><tr><th>Component: #data.fullname#</th></tr><tr><td></cfoutput>
<cf_fakedump var="#data#">
<cfoutput></td></tr></table></cfoutput>
<cfelseif isStruct(attributes.var)>
<cfoutput>
<table>
<tr>
<th colspan="2">struct</th>
</tr>
<cfloop item="key" collection="#attributes.var#">
<tr valign="top">
<td class="key">#key#</td>
<td>
<cfif isSimpleValue(attributes.var[key])>
#attributes.var[key]#
<cfelse>
<cf_fakedump var="#attributes.var[key]#" top="#attributes.top#">
</cfif>
</td>
</tr>
</cfloop>
</table>
</cfoutput>
<cfelseif isQuery(attributes.var)>
<cfset cols = attributes.var.columnlist>
<cfif not len(attributes.top)>
<cfset attributes.top = attributes.var.recordCount>
</cfif>
<cfoutput>
<table>
<tr>
<th colspan="#listLen(cols)+1#">query (#attributes.var.recordcount# rows)</th>
</tr>
<tr>
<td> </td>
<cfloop index="c" list="#cols#">
<th>#c#</th>
</cfloop>
</tr>
<cfloop query="attributes.var" endrow="#min(attributes.var.recordCount,attributes.top)#">
<tr>
<td class="key">#currentRow#</td>
<cfloop index="c" list="#cols#">
<td>
<!--- could be complex --->
<cfset theVal = attributes.var[c][currentRow]>
<cfif isSimpleValue(theVal)>
#theVal#
<cfelse>
<cf_fakedump var="#theVal#" top="#attributes.top#">
</cfif>
</td>
</cfloop>
</tr>
</cfloop>
</table>
</cfoutput>
<cfelseif isXMLDoc(attributes.var) or isXMLNode(attributes.var)>
<cfoutput>
<table>
<tr>
<th colspan="2"><cfif isXMLDoc(attributes.var)>xml<cfelseif isXmLNode(attributes.var)>xml element</cfif></th>
</tr>
<cfif isXMLDoc(attributes.var)>
<cfset child = attributes.var.xmlRoot>
<tr>
<td>#child.xmlName#</td>
<td><cf_fakedump var="#child#"></td>
</tr>
<cfelseif isXMLNode(attributes.var)>
<tr>
<td>xmlText</td>
<td>#attributes.var.xmlText#</td>
</tr>
<cfset kids = attributes.var.xmlChildren>
<cfloop index="x" from="1" to="#arrayLen(kids)#">
<tr>
<td>#kids[x].xmlname#</td>
<td><cf_fakedump var="#kids[x]#"></td>
</tr>
</cfloop>
</cfif>
</table>
</cfoutput>
<cfelse>
<cfoutput>[FakeDump] Sorry, I couldn't handle this data.</cfoutput>
</cfif>
Archived Comments
If people are interested I wrote a text version of cfdump many years ago that can be used to dump into a log - or I suppose displayed in <pre> tags. If you'd like the source email me at robin (at) rocketboots.com and I'll send it through, if enough people ask for it I'll put it up on riaforge.
Cheers,
Robin
Well, don't forget cfdump added a text version in cf8 as well. :)
Ah, there you go then - I think I wrote this around the time of MX :-). Mine appends to a log, so if you're in the habit of tailing logs it might still be useful.
I'm Mr. "TailView" for debugging. :)
Ray, this is a great post on this alternative to cfdump.
Further more... to have coded this in 20 minutes is amazing.
We're not worthy, we're not worthy! ;)
Heh, I'm no great coder - just a fast typist. ;)
This is a real gem of a custom tag for people on cheaper coldfusion servers which don't support cfdump for scalar variables like XML and queries.
All developed at the speed of light!
It also looks smaller (HTML) than the real cfdump. Really cool for error mailing.
WOW!! perfect... host company just took away cfdump!
this is PERFECT
thanks
Great solution, Thanks, I was looking for a way to customize my own cfdump and this code helped get me started.
Suggested improvements; The css styles effect the entire page. To fix this, I :
<!--- changed style on line 13 to 21 to --->
<style>
.fakedump {
width:100%;font-size:0.85em; }
.fakedump table,
.fakedump th,
.fakedump td {
border: 1px solid black; }
.fakedump td {
padding: 5px; }
.fakedump th,
.fakedump .key {
background-color: ##e3e392; }
.fakedump .fakedump_title {
background-color: ##FFD700; }
<!--- change line 33 to: --->
<span class="fakedump"><cfif isSimpleValue(attributes.var)>
<!--- change line 156, to : --->
</cfif></span>
<!--- I wanted my titles to be in a different color, so I also changed all the <th ...> tags to read
<th class="fakedump_title ...>
--->
Suggestion: Maybe you could merge this code with jquery to create collapsable sections?
Good changes there DanGle. I'll leave it to folks to paste in your changes if they want. (Yeah, being lazy here. ;)
Just my opinion: I'd not auto deserialize the JSON. IMHO dump should just output what it is given. Sometimes I _want_ to see the JSON as a string. </twocents>
Hmmm. You could always support both. Ie, click to switch from deserialized to serialized and back again.
That'd be cool. What would be _really_ cool is if the JSON was a string it actually _formatted_ it (pretty printed). Same with XML strings.
I'd imagine doing a pretty print for JSON wouldn't be terribly hard. You've got arrays and objects in JS and that's pretty much it - I mean in terms of what you can send over the wire. You can't send functions.
The cfoutput on line 22 needs to be moved down 1.
Good catch - thanks.