OK, it's my first try with ColdFusion and I'm having some serious trouble.
I just tried
writeDump(someVar);
within a <cfscript> block, and it keeps throwing errors.
What's going on?
P.S. What I'm trying to do is something similar to php's print_r or var_dump; so if you know of a better/alternative way to achieve the very same thing, I'm all ears! :-)
Taking James's idea forward, what is your CF version, because writedump will work only in version 9 & later. If you are on version 8 or earlier, you have to use <cfdump var="#someVar#">
If you have to use it in cfscript, then you can write your own custom function
<cffunction name="myDump" returntype="void">
<cfargument name="arg" required="true" type="any">
<cfdump var="#Arguments.arg#">
</cffunction>
and then call this function inside cfscript block
mydump(somevar);
Related
I'd like to initiate some logging of requests and, more importantly, queries within my application. Lucee makes this pretty easy, and I'm able to log all queries within any given page run with a few lines of code within the onRequestEnd function:
<cfset getQueries = getPageContext().variablesScope()>
<cfset queryArr = ArrayNew(2)>
<cfset x = 0>
<cfloop collection="#getQueries#" item="key">
<cfif IsQuery(getQueries[key])>
<cftry>
<cfset thisQ = getQueries[key]>
<cfset thisQT = thisQ.getExecutionTime() / 1000000>
<cfset thisSQL = thisQ.getSQL().getSQLString()>
<cfset x = x + 1>
<cfset queryArr[x][1] = thisQT>
<cfset queryArr[x][2] = thisSQL>
<cfcatch>
</cfcatch>
</cftry>
</cfif>
</cfloop>
This gives me an array with each query's SQL (with bind variables represented with ?) and the execution time. I can insert this into a logging database and have all kinds of fun with it.
The problem is that I have many pages that call CFCs, which run queries and then return data:
<cfset someVar = createObject("component","cfc.test").getSomeData(ID=7)>
After I wrote my logging code I realized that the queries within CFCs (the bulk of my data processing) were not being recorded as, of course, they run in their own scope. I'm looking for an easy way to record queries within CFCs as well.
I had never heard of onCFCRequest before a few minutes ago (when Google enlightened me), and I cannot find much information on its use. I was hoping it may work like onRequest, and I could do something like this:
<cffunction name="onCFCRequest">
<cfargument type="String" name="cfcName" required=true/>
<cfargument type="String" name="methodName" required=true/>
<cfargument type="struct" name="args" required=true/>
<cfset caller.getLog = getLog>
</cffunction>
However, that does not work, nor do any of the other variations I tried. In fact, this function doesn't appear to work at all, as this code did not insert anything into my test table:
<cffunction name="onCFCRequest">
<cfargument type="String" name="cfcName" required=true/>
<cfargument type="String" name="methodName" required=true/>
<cfargument type="struct" name="args" required=true/>
<cfquery>
insert into testTable (f1, f2)
values (<cfqueryparam cfsqltype="cf_sql_varchar" value="test1">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="test2">)
</cfquery>
</cffunction>
What is the proper way to use onCFCRequest, and can it be used to write data from a cfc's local scope to the parent/caller request scope? If not, is there another function (like an onCFCRequestEnd) that can be used to do so? I do not want to have to make largescale changes to every cfQuery or cfc file in my application to accomplish this goal.
I am aware there are some commercial applications designed to do this far better that I can with just a few hours of coding. However, this is for a personal site that I can't afford to spend money on, and the experience of doing it myself is enlightening.
As your actual question is how to log all query executions no matter where they happened, I think there are two approaches to do that.
Enabling Logging
Whenever a query is executed, you can write the SQL string and the parameters into a log.
You can set up the logging to a database table of your wish within the Lucee Admin.
Once that's set up, you need to call <cflog> and use the name of the specified log within the file attribute.
Downside to this approach is that you have to manually place a <cflog> whenever you execute a query. This might be mitigated by centralizing all database communication in a function which also does the logging. So you whenever you want to query something, you'd call that function instead of calling <cfquery> manually.
Enabling debugging
Lucee's debugging functionality allows you to output all queries executed within a page run besides other useful information.
You first need to add a debugging template:
Important note: The predefined templates only output the data to the generated HTML of your website. Though you have the possibility to create your own debugging template. In order to do that you need to create your own template CFC within lucee-server/context/context/admin/debug/ and provide it with a unique name. There's also a slide show of how to create your own debugging template of the head behind Lucee, Gert Franz, which explains in more detail how to do that.
When done correctly, you should see the name of your template listed in the template types drop-down list.
Then, all you need to do is to enable debugging of database activity:
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to do a cfdump inside a cfscript tag?
I'm new to Coldfusion and wondered if anyone knew of a way to access function when inside a script block. I cant find way of calling it from there.
Why have Adobe removed it? Or have they just called it something else now?
Sorry - I know this is probably really basic question, but like I said I'm new.
The script equivalent of cfdump is writeDump().
The same pattern has been used for cfoutput and cflog, with writeOutput() and writeLog() functions.
writeDump()
Apparently in ColdFusion 9 it has been added as writeDump() so you should be able to use this from a cfscript block.
So if you have CF9, then you are fine.
If you using an older legacy system which does not support this newly introduced writeDump() function, as we are, you could write a wrapper for it and put it somewhere accessible to all your files.
This is the wrapper I have written, which for short term debugging use can be put on the cfc file you are working on (although remove it before you commit your code - otherwise it's just a mess), or you can put it somewhere global so you can call it from shared scopes.
Here is an example of a wrapper function you can use:
<cffunction name="dump" access="private" returntype="void" hint="dump wrapper">
<cfargument name="stuffToDump" type="any" required="true" hint="what you want to dump">
<cfargument name="abort" type="any" default="false" hint="abort after dump">
<cfargument name="expand" type="any" default="false" hint="expand output">
<cfdump var="#arguments.stuffToDump#" expand="#arguments.expand#">
<cfif #arguments.abort# EQ 1>
<cfabort>
</cfif>
</cffunction>
There are probably better ways around this problem, but this is what I currently use.
You can put it (temporarily) on the cfc file that you are currently working on, but obviously don't commit this to your code base as you don't want the dump function on all your files.
Or you could put it permanently onto a cfinclude file, and just include that (again - temporarily) to files you are debugging.
Another alternative I guess is to put it onto the Application.cfc. This file has a number of standard methods but you can also define your own additional methods to be included into it. Then you have your dump() function available in the APPLICATION scope.
There's a good info page on the Application.cfc file here. http://www.bennadel.com/blog/726-ColdFusion-Application-cfc-Tutorial-And-Application-cfc-Reference.htm
I have even seen it used in the Server scope, but this should never be done on production code. OK for debugging I guess as a last resort, just make sure you remember to remove it.
Personally I think both these options are probably far from ideal, and it's a great shame it took Adobe so long to provide a script alternative to the function. Would have saved a lot of pain.
But hopefully if you are using CF9 then all this will be irrelevent to you and you can just use the new writedump() function now they have finally added it in.
To be a little clearer and provide an example:
<cfscript>
SomeString = 'ABC';
writeDump(SomeString);
</cfscript>
Peter's answer is 100% correct.
Here's a weird one. I haven't had any luck finding any information about this on Google, so I'm wondering if any of you have seen this before?
I've got a CFC in the request scope and then in the onRequestEnd event I grab that CFC out of the request scope and do some end-of-request stuff with it. The problem is I can't reference the variable in my onRequestEnd event because it produces an error that says it's not defined in the scope... but here's where it gets really weird and why I KNOW this is a bug (it's not just a suspicion)... If I DUMP the variable, the cfdump tag successfully displays the CFC and all its stuff... and then produces the same "is undefined in scope" error. Here's a screen-capture.
So... anybody seen this before? 'Cause I'm totally stumped. I've already installed the 9.0.1 updater and both of the cumulative hot fixes.
p.s. Yeah, I know it says OnRequestEnd.cfm, but this is actually inside the Application.cfc onRequestEnd method -- it's a legacy from the framework dating back originally to CF5, just go with it. ;P
EDIT: Okay, it's gotten weirder... I tried using evaluate() to set it to a local variable, which apparently works, because then I dump the local variable. The dump still works, even though it's on line 2 AFTER the line on which the error occurred?!!
EDIT 2: EDIT: Here's the code from the Application.cfc that includes the file where the error occurs:
<cffunction name="onRequestEnd" access="public" output="true">
<cfinclude template="OnRequestEnd.cfm">
</cffunction>
It appears to have something to do with the combination of the method and the include file.
It still fails if I execute the method in the onRequestStart like this:
<cffunction name="onRequestStart" access="public" output="true">
<cfset onRequestEnd() />
</cffunction>
But it works fine if I include the file in onRequestStart like this.
<cffunction name="onRequestStart" access="public" output="true">
<cfinclude template="OnRequestEnd.cfm">
</cffunction>
HOWEVER! There's obviously more to this because I can't create a simple test case. If I create a new project with a very simple application.cfc in it and replicate all these details, it works fine. So there's something else in the framework that's contributing to it beyond just the method names and file names.
And the file name doesn't seem to actually contribute, since it still fails in the same way if I change the name of the file like this:
<cffunction name="onRequestEnd" access="public" output="true">
<cfinclude template="reqend.cfm">
</cffunction>
EDIT 3: Okay it has nothing to do with the file... well it does, but not with the file name... At the bottom of the onRequestEnd.cfm is this code
<!--- this seems to help resolve a leaky-memory issue in CF/JRun --->
<cfset structClear(variables) />
<cfset structClear(request) />
<cfabort />
If I comment out those StructClear statements, then the error goes away, which told me that it was executing the OnRequestEnd.cfm twice... and I THINK that means that CF9 changed the behavior of the CFABORT tag and it now executes the onRequestEnd event when the tag is used... it didn't in previous versions of cf...
I didn't find documentation of this, but I did find this blog from Ben Nadel about this behavior with the CFLOCATION tag, so it seems to be more generally about the onRequestEnd event. It seems now in CF9, no matter how a page finishes executing, the onRequestEnd event is executed at the end... That's a change from all the previous versions and so it mucks with code I had created in order to actually cause this to happen on previous versions. Since I was causing execution of the onRequestEnd and then aborting the page early, CF now executes the onRequestEnd, aborts and then executes the onRequestEnd again because of the abort.
Luckily, this fairly simple bit of code at the top of the template seems to fix the problem:
<cfif structIsEmpty(request)>
<cfexit method="exittemplate" />
</cfif>
I decided to leave this question here (rather than deleting it) since it may help some other folks, though I found the source of the problem and a workaround while working with some of the comments and it wasn't what we thought. Ben Nadel also posted a blog about the change to CFABORT a while ago as well... and Adam Cameron tells me that although this change was added by-design in CF9 (without warning anyone apparently, since I saw no mention in the LiveDocs and added 2 comments about it), it was then removed in the (not yet released?) CF10. I think Adam might be in the beta, I'm not sure.
I have a search function that executes a stored procedure and returns results. If there are no results, I want to try running the function one more time with a more generalized search. So, I put a cfif into my code -
<cfif results.recordCount EQ 0 And Not arguments.searchForPotentialMatches>
<cfset arguments.searchForPotentialMatches = True />
<cfinvoke method="thisMethod" argumentCollection="#arguments#" />
</cfif>
Basically, if there were no results AND I haven't already tried a generalized search, it should invoke this method again. Then, in the beginning of the method, before calling the stored procedure, I check if searchForPotentialMatches is true, and if it is, I generalize the search query.
There seems to be a problem, though... When I try to run this, it hangs - until there's a timeout with the stored procedure. Through debugging and outputting variables, I've been able to see that it gets to the stored procedure, and then gets stuck trying to execute it. However, using the original function before these rerun changes, if I do the regular search and then the generalized search in 2 separate calls, it executes correctly. So I'm not sure why it fails when I try to build this in programmatically... What am I doing wrong?
Could really be any number of things. Is all of this code inside of a cfc? Is that cfc in a persistent scope and have you properly var'd all your variables?
Can you execute the stored proc under both normal and generalized conditions standalone without issue?
Try pasting in more of your code (including the first call to the stored proc) so we can try to trace your data flow a bit more.
Recursion is:
seductively simple in theory and a pain in the ass in practice - to debug.
often necessary to walk trees or traverse graphs, but when one can do without, do without.
So as you wrote, I'd lose the recursion, and do it sequentially. Absent any more code as #scrittler requested, I'd rewrite as such:
<cfcomponent output="false">
<cffunction name="search" output="false" access="public" returntype="any" hint="I am called by the client">
<!--- <cfargument/> tags --->
<!--- what ever you need to do with the arg before actually searching --->
<cfset var results = doSearch(argumentCollection=arguments)>
<cfif NOT results.recordcount>
<!--- whatever you need to change about the args to perform a generalized search --->
<cfset var results = doSearch(argumentCollection=arguments)>
</cfif>
<cfreturn results>
</cffunction>
<cffunction name="doSearch" output="false" access="private" returntype="query" hint="I run the query">
<!--- <cfargument/> tags --->
<!--- results query (i.e. call to sproc)--->
<cfreturn results>
</cffunction>
</cfcomponent>
What is your access attribute on the function tag, have you given it a value that leaves the function unable to call itself?
This feels unfair... But the issue was with something completely different. The recursive call works correctly, but there was another field that was getting changed due to a check in the function before calling the stored procedure and causing the stored proc to hang. Sorry about that, and thanks for all your help!
I want to handle a scenario where user hits a url of /somePage.cfm when that template doesn't exist and use a template from another directory. I know I can do this via rewrites in apache etc. but I don't really want to store logic in there so I gave trying to override onTemplateMissing behaviour in my Application.cfc.
It seems to be working fine in my testing but I'm worried by doing this hacky solution I'm short cutting some parts that I haven't seen yet (e.g. methods that I'm not currently using such as onSessionStart etc.) and may run into issues in the future.
Here is what I'm currently doing:
<cffunction name="onMissingTemplate">
<cfargument name="targetPage" />
<!--- Handle any templates that we're really loading from elsewhere --->
<cfif isFooTemplate(arguments.targetPage)>
<cfset onRequestStart(arguments.targetPage) />
<cfset onRequest(arguments.targetPage) />
<cfset onRequestEnd(arguments.targetPage) />
<cfreturn true />
</cfif>
<cfreturn false />
</cffunction>
Note that also in my onRequest method I'm doing further handling for templates that isFooTemplate() would return true to.
I don't think this is a hacky solution. This is what the method is for, and on returning false, ColdFusion will invoke the standard error handler you setup in the administrator if you want.
The only case were onSessionStart() hasn't run is if the user hits the onMissingTemplate() on the first ever page request. If you for some reason need the user to have a session, you can check for the existence of the session scope, since the session scope is supposed to be available in the onMissingTemplate() method and handle appropriately.
It's actually onMissingTemplate not onTemplateMissing; and this is a recommended practice, not 'hacky' at all. You're fine doing it this way.