Computed Columns MS SQL

A table with a Totals column is a perfect use case for computed columns in MSSQL. Making the Totals column a computed column allows MSSQL to do all the preassigned math for me, eliminating the need to write SQL or ColdFusion code to sum up the monthly totals. Every time I update the table I would have to run other code or SQL to again obtain the correct total.

Here's my table, MonthlyPay.

view plain print about
1SELECT [ID]
2 ,[Year]
3 ,[January]
4 ,[February]
5 ,[March]
6 ,[April]
7 ,[May]
8 ,[June]
9 ,[July]
10 ,[August]
11 ,[September]
12 ,[October]
13 ,[November]
14 ,[December]
15 ,[Totals]
16 FROM [MonthlyPay]

By turning Totals into a computed column, my life is easier. Here's how:

view plain print about
1ALTER TABLE [MonthlyPay] DROP COLUMN [Totals];
2GO
3ALTER TABLE [MonthlyPay] ADD [Totals] AS (
4[January] + [February] + [March] + [April] + [May] + [June] + [July] + [August] + [September] + [October] + [November] + [December]) PERSISTED;

I could add even more calculations including the math to get tax percentages and including those in the total. So much easier!

Breaking Up a String into Equal Parts

Presented with a string of concatenated ID's, I had to break them up into a comma delimited list. These ID's were all stored in a database field concatenated in one long string with no breaks, tabs or other delimiters. Why, I don't know.

Fortunately, I knew that all the ID's were 6 characters each. I just had to come up with the regular expression to separate them and insert a comma and a space.

Here's the ColdFusion code to do that:

view plain print about
1<cfset MyLongStringOfConcatenatedIDs = "1234567890qwertyuiopasdfghjklz" />
2
3<cfset CommaDelimList =
4REReplace(Trim(MyLongStringOfConcatenatedIDs), "(.{6})\B", "\1, ", "ALL") /
>

5
6output: "123456, 7890qw, ertyui, opasdf, ghjklz"

Integers Only, Remove Dollar Signs and Periods

Looking to remove "$" and "." (dollar signs, commas and periods/decimal points) from numbers before inserting into a database, storing integers. This means decimals need to be rounded into an integer.

ColdFusion only... We have to use ColdFusion to Round the decimals.

view plain print about
1<cfset cleanNumber = Round(Replace(Replace(DirtyNumber, "$", ""), ",", "", "ALL")) />
The first inside, Replace removes the $. The second or outer Replace removes all commas, while the Round function converts the decimal to an integer.

Here is the use of regex, regular expressions and ColdFusion:

view plain print about
1<cfset cleanNumber = Round(REReplace(DirtyNumber, "[^0-9.]", "", "ALL")) />
With this filter, the regex removes all non-numeric characters except the decimal points. The Round function does it's job rounding the closest integer.

I wanted to use only regex but regex cannot do math, i.e., round the decimal. The first statement only removed "$" and "." and ",". With JavaScript filtering on the front end the first example addresses 99% of the cases but the second is more thorough and removes all occurrences of non-numeric characters except decimal points. The only downfall to both is if there is more than one period / decimal point.

convert UTC date in Unix timestamp format

Converting a date in ColdFusion into a UNIX timestamp format is not straight forward if you don't know what the UNIX timestamp format is. Unix time is defined as the number of seconds that have elapsed since midnight Coordinated Universal Time (UTC), 1 January 1970. Here's how you convert using ColdFusion.

view plain print about
1DateDiff("s", CreateDate(1970,1,1), CreateODBCDateTime(Now()))

Google Custom Search Results

Want to find out what Google has on your web site? Do you wish to tabulate a list of all the pages Google has searched and indexed? Here's how to document it! Caveat: you must have a google business account to use the API and not have ads returned with your results.

To view what google has indexed, go to Google, of course, and type in your site's domain, prefixed with "site:", e.g., "site:mysite.com". This will give you the first 10 results. You could copy and paste, but you are too smart and lazy for that.

This tutorial will NOT go into how to set up Google Custom Search or access the API. I'm assuming you either already know how to do that or will look that up on your own. The Google Custom Search API returns a whole bunch of stuff, but we only want in this case, the URLs of every page indexed.

I have the CFM template in its entirety below, but we'll step through what's going on here first. The form which calls itself for the action.

view plain print about
1<p class="welcome">Googe Custom Search</p>
2<form action="joe-google-custom-search.cfm">
3    <label>
4        Search Terms:
5        <input type="text" name="googlesearchterms" id="googlesearchterms" size="30" />
6    </label>
7    <button id="googlesearch" name="googlesearch" type="submit">Search</button>
8</form>
To submit the form, you would want to enter, "site:yourdomain.com". Or you could put in whatever search terms you wish. Once the form is submitted, we want to format the search terms by converting the spaces to plus signs (+). The query string will be appended to the API call's URL.
view plain print about
1<cfset searchterms = Replace(trim(form.googlesearchterms, " ", "+", "all")) />

We want to set our counter and make sure we get a maximum of 1000 returns, if there are that many. This will error out at the end. We are going to go in increments of 10 because that's the default number of results Google will return.

view plain print about
1<!--- set our result counter --->
2    <cfset CountVar = 1 />
3    <!--- limit returns to 1000 --->
4    <cfloop condition="CountVar LESS THAN 1000">

This is where the action happens. We add our query string, our API key and our incremented count.

view plain print about
1<!--- hit up Google --->
2        <cfhttp url="https://www.googleapis.com/customsearch/v1?key=#myGoogleAPIKey#&q=#searchterms#&start=#CountVar#"
3                resolveURL="yes"
4                result="variables.response"
5                method="get">

We will have to convert the returned JSON to an easily usable format:
view plain print about
1<cfset variables.GoogleSearchResults = deserializeJSON(variables.response.Filecontent) />

Then pluck out our array of returned result items:

view plain print about
1<cfset variables.GoogleSearchResults = deserializeJSON(variables.response.Filecontent) />

The results come back in groups of 10, so we have to loop this set and write to our file, which should already be created and in place. You can output to the screen too, but that's a bit too much. For testing I set the CountVar to 100 and returned to the screen, a reasonable amount for verification.

view plain print about
1<!--- loop thru and add to list (dump to screen) --->
2        <cfloop array="#variables.GoogleArray#" index="arrayitem">
3            <cffile file="C:\path\to\my\text\file\googlesearchtext.txt" action="append" output="#arrayitem.link#" />
4            <!--- <cfoutput>#arrayitem.link#</cfoutput><br/> --->
5        </cfloop>

Check your text file, it should be much larger now. From here you can cut and paste into Excel or whatever format you need. If desired, you could create a SQL insert statement too.

Here's all the code in one piece, it's called joe-google-custom-search.cfm. So if you change the name, don't forget the form action attribute.

view plain print about
1<p class="welcome">Googe Custom Search</p>
2<form action="joe-google-custom-search.cfm">
3    <label>
4        Search Terms:
5        <input type="text" name="googlesearchterms" id="googlesearchterms" size="30" />
6    </label>
7    <button id="googlesearch" name="googlesearch" type="submit">Search</button>
8</form>
9
10
11<!--- form submitted? --->
12<cfif StructKeyExists(form, "googlesearchterms") />
13    
14    <!--- format search terms --->
15    <cfset searchterms = Replace(trim(form.googlesearchterms, " ", "+", "all")) />
16    <cfset myGoogleAPIKey = "123412342342341234:oiuo12341234" />
17    
18    <!--- set our result counter --->
19    <cfset CountVar = 1 />
20    <!--- limit returns to 1000 --->
21    <cfloop condition="CountVar LESS THAN 1000">
22        
23        <!--- hit up Google --->
24        <cfhttp url="https://www.googleapis.com/customsearch/v1?key=#myGoogleAPIKey#&q=#searchterms#&start=#CountVar#"
25                resolveURL="yes"
26                result="variables.response"
27                method="get">

28    
29        <!--- pick out what we want --->
30        <cfset variables.GoogleSearchResults = deserializeJSON(variables.response.Filecontent) />
31        <cfset variables.GoogleArray = variables.GoogleSearchResults.items />
32    
33        <!--- loop thru and add to list (dump to screen) --->
34        <cfloop array="#variables.GoogleArray#" index="arrayitem">
35            <cffile file="C:\path\to\my\text\file\googlesearchtext.txt" action="append" output="#arrayitem.link#" />
36            <!--- <cfoutput>#arrayitem.link#</cfoutput><br/> --->
37        </cfloop>
38    
39        <!--- Google returns results in groups of 10 --->
40        <cfset CountVar = CountVar + 10 />
41    </cfloop>
42</cfif>

Trim characters off of right on a string

Trim characters off of right on a string. Trim off a certain number of characters on the right side of a string. Here's a quick easy method to cut off an exact number off the right portion without reversing the string and using various other string manipulation techniques. The concept is simple, find the length of the string

view plain print about
1len(MyString)
and then subtract the number of characters you want removed, e.g., 3
view plain print about
1len(MyString) - 3
We just use this result to count thenumber of characters from the left and keep those using left() function.
view plain print about
1left(MyString, (len(MyString) - 3))

ColdFusion CLIENT cookie issue

MSSQL error: The data types text and varchar are incompatible in the equal to operator.

In the ColdFusion administrator under there is the option to save CLIENT variables as cookies, in the registry, a datasource or not at all.

If the client variables are stored as cookies, it is fairly easy to retrieve and parse to determine things like the identity of a returning user from the cookie. This cookie is called CFCLIENT_<cfapplication name>. If the client variables are stored in the database, it's not so easy. Actually impossible, the cookie variables are NOT stored in the datasource.

First to locate the CLIENT variables, which are stored in a table called CDATA. CFGLOBALS client variables are session variables, like CFID, CFTOKEN and timestamp details and are stored in the table CGLOBAL.

So what about the error? The data types text and varchar are incompatible in the equal to operator. I ran some queries to try and see if there was some information I could glean from the client variables table.

view plain print about
1SELECT TOP 1000 [cfid]
2 ,[app]
3 ,[data]
4 FROM [hoton].[dbo].[cdata]
5 where [data] != ''
Error: The data types text and varchar are incompatible in the not equal to operator.

[data] is a text field permitting nulls. So I checked for null:

view plain print about
1SELECT TOP 1000 [cfid]
2 ,[app]
3 ,[data]
4 FROM [hoton].[dbo].[cdata]
5 where [data] is null
pulled up everything, to test, I ran this:
view plain print about
1SELECT TOP 1000 [cfid]
2 ,[app]
3 ,[data]
4 FROM [hoton].[dbo].[cdata]
5 where [data] is not null
which turned up nothing! So I added a space in the quotes:
view plain print about
1SELECT TOP 1000 [cfid]
2 ,[app]
3 ,[data]
4 FROM [hoton].[dbo].[cdata]
5 where [data] != ' '
The data types text and varchar are incompatible in the not equal to operator.
No go! But this worked:
view plain print about
1SELECT TOP 1000 [cfid]
2 ,[app]
3 ,[data]
4 FROM [hoton].[dbo].[cdata]
5 where [data] not like ''
which pulled up what I wanted to look at, but unfortunately could not use. No cookie info is stored in with the datasource client session storage option in the ColdFusion administrator.

Custom Logging Template for SQL queries and Stored Procedures

We'll combine the last two blog posts, Where Does CF Debug Information Come from Mommy? Or How to use the ColdFusion Service Factory and ColdFusion Custom Debugging Template. The task is to create a template to log any database activity, specifically looking for consistently long running queries and to make a baseline for our code optimization.

Let's take a look at the data we are gathering. I created this table to keep data which I thought would be pertinent.

Hey this image will be loaded soon!

Here's a key explaining the basics on this table:
ATTUID = individual user id
RequestID = Individual ID per request (more below)
Body = the actual query
Line = line number of query or stored procedure
Name = assigned name of query or stored procedure
CFScript = not what you think, this is the script name calling the query or stored procedure
CFTimeStamp = ColdFusion's time stamp
SQLTimeStamp = MS SQL's time stamp
Type = query or stored procedure

Now the code...
First we'll utilize the ColdFusion Service Factory:

view plain print about
1<!--- initialize the CF Server's Service Factory --->
2<cfset factory = createObject("java","coldfusion.server.ServiceFactory") />
3<!--- get all the debugging data --->
4<cfset queries = factory.getDebuggingService().getDebugger().getData() />
We create the logging service by calling Illudium PU-36 generated beans and DAO (Data Access Objects). Basically these are the getters and setters.
view plain print about
1<!--- Set up Logging Service - call it once and reuse it --->
2<cfset CFLog = createObject("component","optimization.cfc.Optimization_Code_Log").init() />
3<cfset CFLogger = createObject("component","optimization.cfc.Optimization_Code_LogDAO").init(application.sqlSource) />

Add this in to help keep individual page requests identified together.

view plain print about
1<!--- Unique ID per request --->
2<!--- keep all requests per page call together --->
3<cfset RequestID = Val(Replace(Rand(),"0.","")) />
A little detail about this... I desired a random number be generated but wanted it to be a whole number. The Rand() function creates a decimal. I am treating the Rand() produced number as string, removing the preceding "0.", and converting to a number with the Val() function. The number length varied, so multiplication would not be work.

To get the data we want we are going to query the debugging data, "queries" called above, as shown below:

view plain print about
1<cfquery dbType="query" name="cfdebug_queries" debug="false">
2    SELECT *, (endTime - startTime) AS executionTime
3    FROM queries
4    WHERE type = 'SqlQuery'
5</cfquery>

Two things of note. One is the calculation of Execution Time, really what we are after, and the type = 'SqlQuery'. For Stored Procedures, we replace this with "StoredProcedure".

view plain print about
1<cfquery dbType="query" name="cfdebug_storedproc" debug="false">
2    SELECT *, (endTime - startTime) AS executionTime
3    FROM queries
4    WHERE type = 'StoredProcedure'
5</cfquery>

Loop the query containing all the debug data for queries run on your template, then set the data in the bean using the Illudium PU-36 setters, e.g. CFLog.setBlahBlah(variablescope.blahblah). Once they are all set, then we save using the generated DAO.

view plain print about
1<cfloop query="cfdebug_queries">
2
3    <cfset CFLog.setATTUID(client.ATTUID) />
4    <cfset CFLog.setRequestID(RequestID) />
5    <cfset CFLog.setBody(cfdebug_queries.Body) />
6    <cfset CFLog.setDatasource(cfdebug_queries.Datasource) />
7    <cfset CFLog.setIPAddress(CGI.REMOTE_ADDR) />
8    <cfset CFLog.setEndTime(cfdebug_queries.EndTime) />
9    <cfset CFLog.setExecutionTime(cfdebug_queries.ExecutionTime) />
10    <cfset CFLog.setLine(cfdebug_queries.Line) />
11    <cfset CFLog.setName(cfdebug_queries.Name) />
12    <cfset CFLog.setCFRowCount(cfdebug_queries.RowCount) />
13    <cfset CFLog.setStartTime(cfdebug_queries.StartTime) />
14    <cfset CFLog.setCFScript(CGI.SCRIPT_NAME) />
15    <cfset CFLog.setURLString(CGI.QUERY_STRING) />
16    <cfset CFLog.setTemplate(cfdebug_queries.Template) />
17    <cfset CFLog.setCFTimeStamp(cfdebug_queries.TimeStamp) />
18    <cfset CFLog.setSQLTimeStamp(Now()) />
19    <cfset CFLog.setType(cfdebug_queries.Type) />
20
21    <cfset CFLogger.save(CFLog) />
22</cfloop>

You can see that we are gathering more information than what was provided from the service factory, for example, the client user id, and several CGI variables.

We do the same thing for the Stored Procedures, except for one small but important change. Note also that I do not have to create another set of bean and DAO objects, I just reuse the existing ones, CFLog and CFLogger.

view plain print about
1<cfloop query="cfdebug_storedproc">
2
3    <cfset CFLog.setATTUID(client.ATTUID) />
4    <cfset CFLog.setRequestID(RequestID) />
5    <cfset CFLog.setBody(cfdebug_storedproc.Body) />
6    <cfset CFLog.setDatasource(cfdebug_storedproc.Datasource) />
7    <cfset CFLog.setIPAddress(CGI.REMOTE_ADDR) />
8    <cfset CFLog.setEndTime(cfdebug_storedproc.EndTime) />
9    <cfset CFLog.setExecutionTime(cfdebug_storedproc.ExecutionTime) />
10    <cfset CFLog.setLine(cfdebug_storedproc.Line) />
11    <cfset CFLog.setName(cfdebug_storedproc.result[cfdebug_storedproc.currentRow][1].name) />
12    <cfset CFLog.setCFRowCount(cfdebug_storedproc.RowCount) />
13    <cfset CFLog.setStartTime(cfdebug_storedproc.StartTime) />
14    <cfset CFLog.setCFScript(CGI.SCRIPT_NAME) />
15    <cfset CFLog.setURLString(CGI.QUERY_STRING) />
16    <cfset CFLog.setTemplate(cfdebug_storedproc.Template) />
17    <cfset CFLog.setCFTimeStamp(cfdebug_storedproc.TimeStamp) />
18    <cfset CFLog.setSQLTimeStamp(Now()) />
19    <cfset CFLog.setType(cfdebug_storedproc.Type) />
20
21    <cfset CFLogger.save(CFLog) />
22</cfloop>

That change is for Name. Inexplicably, in successive Stored Procedure calls in my template, the query output name remained the same for the following Stored Procs, ignoring the actual name. I had to dig down into the result column output to get the correct name for the Stored Procedure called.

view plain print about
1<cfset CFLog.setName(cfdebug_storedproc.result[cfdebug_storedproc.currentRow][1].name) />

If you pull a <cfdump> on the query output, you'll readily see from where this came, and possibly the situation in to which I ran.

Here is the entire chunk of code for my logging template. I'll not provide the Illudium PU-36 generated bean and DAO unless there are some requests. It's easy enough to generate your own.

view plain print about
1<!--- Get the Debugger Service --->
2<cfset factory = createObject("java","coldfusion.server.ServiceFactory") />
3<cfset queries = factory.getDebuggingService().getDebugger().getData() />
4
5<!--- Set up Logging Service - call it once and reuse it --->
6<cfset CFLog = createObject("component","optimization.cfc.Optimization_Code_Log").init() />
7<cfset CFLogger = createObject("component","optimization.cfc.Optimization_Code_LogDAO").init(application.sqlSource) />
8
9<!--- Unique ID per request --->
10<cfset RequestID = Val(Replace(Rand(),"0.","")) />
11
12<!--- EVENT: SQL Queries --->
13<cftry>
14    <cfquery dbType="query" name="cfdebug_queries" debug="false">
15        SELECT *, (endTime - startTime) AS executionTime
16        FROM queries
17        WHERE type = 'SqlQuery'
18    </cfquery>
19    <cfscript>
20        if( cfdebug_queries.recordCount eq 1 and not len(trim(cfdebug_queries.executionTime)) )
21        {
22            querySetCell(cfdebug_queries, "executionTime", "0", 1);
23        }
24    
</cfscript>
25    <cfcatch type="Any">
26        <cfscript>
27            cfdebug_queries = queryNew('ATTRIBUTES, BODY, CACHEDQUERY, CATEGORY, DATASOURCE, ENDTIME, EXECUTIONTIME, LINE, MESSAGE, NAME, PARENT, PRIORITY, RESULT, ROWCOUNT, STACKTRACE, STARTTIME, TEMPLATE, TIMESTAMP, TYPE, URL, et');
28        
</cfscript>
29    </cfcatch>
30</cftry>
31
32<!--- EVENT: Stored Procedures --->
33<cftry>
34    <cfquery dbType="query" name="cfdebug_storedproc" debug="false">
35        SELECT *, (endTime - startTime) AS executionTime
36        FROM queries
37        WHERE type = 'StoredProcedure'
38    </cfquery>
39    <cfscript>
40        if( cfdebug_storedproc.recordCount eq 1 and not len(trim(cfdebug_storedproc.executionTime)) )
41        {
42            querySetCell(cfdebug_storedproc, "executionTime", "0", 1);
43        }
44    
</cfscript>
45    <cfcatch type="Any">
46        <cfscript>
47            cfdebug_storedproc = queryNew('ATTRIBUTES, BODY, CACHEDQUERY, CATEGORY, DATASOURCE, ENDTIME, EXECUTIONTIME, LINE, MESSAGE, NAME, PARENT, PRIORITY, RESULT, ROWCOUNT, STACKTRACE, STARTTIME, TEMPLATE, TIMESTAMP, TYPE, URL');
48        
</cfscript>
49    </cfcatch>
50</cftry>
51
52<!--- SQL Queries Logging --->
53<cftry>
54    <cfloop query="cfdebug_queries">
55
56        <cfset CFLog.setATTUID(client.ATTUID) />
57        <cfset CFLog.setRequestID(RequestID) />
58        <cfset CFLog.setBody(cfdebug_queries.Body) />
59        <cfset CFLog.setDatasource(cfdebug_queries.Datasource) />
60        <cfset CFLog.setIPAddress(CGI.REMOTE_ADDR) />
61        <cfset CFLog.setEndTime(cfdebug_queries.EndTime) />
62        <cfset CFLog.setExecutionTime(cfdebug_queries.ExecutionTime) />
63        <cfset CFLog.setLine(cfdebug_queries.Line) />
64        <cfset CFLog.setName(cfdebug_queries.Name) />
65        <cfset CFLog.setCFRowCount(cfdebug_queries.RowCount) />
66        <cfset CFLog.setStartTime(cfdebug_queries.StartTime) />
67        <cfset CFLog.setCFScript(CGI.SCRIPT_NAME) />
68        <cfset CFLog.setURLString(CGI.QUERY_STRING) />
69        <cfset CFLog.setTemplate(cfdebug_queries.Template) />
70        <cfset CFLog.setCFTimeStamp(cfdebug_queries.TimeStamp) />
71        <cfset CFLog.setSQLTimeStamp(Now()) />
72        <cfset CFLog.setType(cfdebug_queries.Type) />
73
74        <cfset CFLogger.save(CFLog) />
75    </cfloop>
76    <cfcatch type="Any">
77        <!--- Error reporting query event --->
78    </cfcatch>
79</cftry>
80
81<!--- Stored Procedures Logging --->
82<cftry>
83    <cfloop query="cfdebug_storedproc">
84
85        <cfset CFLog.setATTUID(client.ATTUID) />
86        <cfset CFLog.setRequestID(RequestID) />
87        <cfset CFLog.setBody(cfdebug_storedproc.Body) />
88        <cfset CFLog.setDatasource(cfdebug_storedproc.Datasource) />
89        <cfset CFLog.setIPAddress(CGI.REMOTE_ADDR) />
90        <cfset CFLog.setEndTime(cfdebug_storedproc.EndTime) />
91        <cfset CFLog.setExecutionTime(cfdebug_storedproc.ExecutionTime) />
92        <cfset CFLog.setLine(cfdebug_storedproc.Line) />
93        <cfset CFLog.setName(cfdebug_storedproc.result[cfdebug_storedproc.currentRow][1].name) />
94        <cfset CFLog.setCFRowCount(cfdebug_storedproc.RowCount) />
95        <cfset CFLog.setStartTime(cfdebug_storedproc.StartTime) />
96        <cfset CFLog.setCFScript(CGI.SCRIPT_NAME) />
97        <cfset CFLog.setURLString(CGI.QUERY_STRING) />
98        <cfset CFLog.setTemplate(cfdebug_storedproc.Template) />
99        <cfset CFLog.setCFTimeStamp(cfdebug_storedproc.TimeStamp) />
100        <cfset CFLog.setSQLTimeStamp(Now()) />
101        <cfset CFLog.setType(cfdebug_storedproc.Type) />
102
103        <cfset CFLogger.save(CFLog) />
104    </cfloop>
105    <cfcatch type="Any">
106        <!--- Error reporting query event --->
107    </cfcatch>
108</cftry>

ColdFusion Custom Debugging Template

Build your own ColdFusion Custom Debugging Template

You can create your own ColdFusion custom debugging template. Build it and drop it into your CF web root WEB-INF/debug folder.

In my case on Windows the folder is C:\ColdFusion10\cfusion\wwwroot\WEB-INF\debug

However, it's not so easy to build your own template! You'll have to use the ColdFusion Service Factory explained in this blog post.

The easiest way to start is to take classic.cfm in the debug folder and experiment.

If you don't believe me, believe Ben Forta! Displaying Query Contents In Debug Output

Why would you want to create your own custom debugging template? In my case, I wanted to create a feature that would log actions from a particular template when accessed only by particular users. It's on a shared development server where debugging is normally not utilized, thus no conflicting debugging usage. I dropped in my custom debugging (logging) template, turned on debugging in the CF Admin and added the appropriate IP addresses of the developers we were logging. There are certainly other ways to skin this cat, for example, we could have dropped in the debug/log template into any directory and added a <cfinclude> in the template we were tracking. But, in my particular case, we didn't want to make any changes to existing code.

Next, I'll go into detail on how I created a Custom Debugging template (logging template) utilizing the ColdFusion Service Factory.

Where Does CF Debug Information Come from Mommy? Or How to use the ColdFusion Service Factory

Have you ever wondered where ColdFusion gets the information presented when debugging is turned on in the CF Admin? It comes from the ColdFusion Server's Service Factory. Of the many things that this produces, one is the debugging service.

Drop this code into one of your templates:

view plain print about
1<cfdump var="#createObject("java","coldfusion.server.ServiceFactory").getDebuggingService().getDebugger().getData()#" label="Debug Data" />

You can see first that we are calling the Service Factory, then the Debugging service, then the Debugger, and finally the data produced. If you care to investigate what each does, simply eliminate calls from the right end of the createObject statement and dump it.

More Entries

BlogCFC was created by Raymond Camden. This blog is running version 5.9.7. Contact Blog Owner