Can't dump? Try a fake dump.

This post is more than 2 years old.

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>

&lt;cfoutput&gt;
&lt;table&gt;
	&lt;tr&gt;
		&lt;th colspan="#listLen(cols)+1#"&gt;query (#attributes.var.recordcount# rows)&lt;/th&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;&nbsp;&lt;/td&gt;
		&lt;cfloop index="c" list="#cols#"&gt;
		&lt;th&gt;#c#&lt;/th&gt;
		&lt;/cfloop&gt;
	&lt;/tr&gt;
	&lt;cfloop query="attributes.var" endrow="#min(attributes.var.recordCount,attributes.top)#"&gt;
		&lt;tr&gt;
			&lt;td class="key"&gt;#currentRow#&lt;/td&gt;
			&lt;cfloop index="c" list="#cols#"&gt;
			&lt;td&gt;
				&lt;!--- could be complex ---&gt;
				&lt;cfset theVal = attributes.var[c][currentRow]&gt;
				&lt;cfif isSimpleValue(theVal)&gt;
					#theVal#
				&lt;cfelse&gt;
					&lt;cf_fakedump var="#theVal#" top="#attributes.top#"&gt;
				&lt;/cfif&gt;
			&lt;/td&gt;
			&lt;/cfloop&gt;
		&lt;/tr&gt;
	&lt;/cfloop&gt;
&lt;/table&gt;
&lt;/cfoutput&gt;

<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>

Download attached file.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Robin Hilliard posted on 10/7/2010 at 4:51 AM

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

Comment 2 by Raymond Camden posted on 10/7/2010 at 4:56 AM

Well, don't forget cfdump added a text version in cf8 as well. :)

Comment 3 by Robin Hilliard posted on 10/7/2010 at 5:09 AM

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.

Comment 4 by Raymond Camden posted on 10/7/2010 at 5:11 AM

I'm Mr. "TailView" for debugging. :)

Comment 5 by Sam Hamilton posted on 10/7/2010 at 6:32 PM

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! ;)

Comment 6 by Raymond Camden posted on 10/7/2010 at 6:34 PM

Heh, I'm no great coder - just a fast typist. ;)

Comment 7 by Sam Hamilton posted on 10/7/2010 at 8:01 PM

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!

Comment 8 by Joeri Bijl posted on 10/8/2010 at 12:39 PM

It also looks smaller (HTML) than the real cfdump. Really cool for error mailing.

Comment 9 by Patrick Galligan posted on 1/9/2011 at 11:57 PM

WOW!! perfect... host company just took away cfdump!

this is PERFECT

thanks

Comment 10 by DanGle neck posted on 1/10/2011 at 4:42 AM

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?

Comment 11 by Raymond Camden posted on 1/10/2011 at 5:06 PM

Good changes there DanGle. I'll leave it to folks to paste in your changes if they want. (Yeah, being lazy here. ;)

Comment 12 by todd sharp posted on 1/10/2011 at 7:09 PM

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>

Comment 13 by Raymond Camden posted on 1/10/2011 at 7:12 PM

Hmmm. You could always support both. Ie, click to switch from deserialized to serialized and back again.

Comment 14 by todd sharp posted on 1/10/2011 at 7:31 PM

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.

Comment 15 by Raymond Camden posted on 1/10/2011 at 7:35 PM

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.

Comment 16 by Phillip Senn posted on 6/25/2014 at 12:14 AM

The cfoutput on line 22 needs to be moved down 1.

Comment 17 by Raymond Camden posted on 6/25/2014 at 12:17 AM

Good catch - thanks.