Skip to content
neoneo edited this page Jun 18, 2012 · 9 revisions

Debugging is an integral part of developing software, and it should be made as easy as possible. CFlow includes a nice debug output, that makes a few things available for you to gain insight quickly. It includes:

  • The task hierarchy of the request, grouped into the execution phases (start, before, event, after, and end). If an event is canceled or dispatched, the corresponding tasks are displayed as children of the originating task. Laid out from top to bottom, you can follow the order of task execution easily.
  • Execution times of all tasks, making locating bottlenecks easy. Since well-designed tasks are atomic, this gives you a very detailed idea of the performance of your application.
  • Exception details, displayed within the task where the error occurred. Exceptions within threads are displayed if the thread is joined by the page thread later.
  • Clear indication where an event is canceled.
  • Custom messages, with or without some data you want to see displayed.

This output is enabled if you use the debug context object. So in onApplicationStart(), you write:

application.context = new cflow.request.debug.Context();

Custom messages

Often you will want to display intermediate results. To display those results, you put a message on the event. In debug mode, the event object exposes an additional method record(). It accepts two arguments: the data you want to log, and optionally a message. In the debug output, your message will appear at the position where you invoked record(). The data you passed in will be dumped at the same spot.
An advantage of this approach is that you don't have to output those results in your view template, or even stop execution altogether, using <cfabort>. Displaying data in a loop used to be a hassle. This feature ensures that this information is now nicely laid out.

Unfortunately, views have no access to the event object and can therefore not put messages in the debug output. The need for that should be little though, because views shouldn't contain much logic. However, debugging a view will be necessary sometimes, and you may have to resort to old tactics here. As a tip, use <cfexit> instead of <cfabort> when you need to stop the rendering process for some reason. This will leave the debug output available.

Example output

The configuration page includes this example configuration (check back over there if this is new to you):

<?xml version="1.0"?>
<targets>
    <target name="session" defaultcontroller="Session">
        <start>
             <invoke handler="authenticate">
                 <dispatch event="failed" />
             </invoke>
        </start>
        <event type="failed">
             <invoke controller="global.Logger" handler="write" />
             <render view="failed" />
        </event>
    </target>

    <target name="world" defaultcontroller="World">
        <include target="template" />
        <include target="session" />
        <before>
            <invoke controller="global.Logger" handler="write" />
        </before>
        <event type="hello">
            <render view="hello" />
            <dispatch event="goodbye" />
        </event>
        <event type="goodbye">
            <dispatch target="session" event="exit" />
            <render view="goodbye" />
        </event>
    </target>

    <target name="template">
        <start>
            <render view="header" />
        </start>
        <end>
            <render view="footer" />
        </end>
    </target>
</targets>

In the case where access is allowed, the debug output looks something like this.

Example debug output

We see here that the global.Logger controller added a custom message. Since two events are dispatched on the world target, the global.Logger is invoked twice because it is defined in the before phase of the world target.
Notice also that the session.exit event has no associated tasks (conventions are not used).

When access is denied, the debug output looks as follows:

Example debug output

We see that the event is canceled and what happens afterwards, and also what doesn't happen (no before, event, and after phases).

Suppressing debug output partially or entirely

By default, debug output is generated for every request. This includes requests that end with a redirect (defined with <redirect>): the redirect is delayed and instead a link is provided to the next page, so that you can inspect the application flow. This information would be lost if the redirect was actually issued.

Especially in the redirect case, but also in general, you probably don't want the debug output to appear all the time. The debug context provides you with some control over when debug output should appear, by means of the setDisplayOutput() method. This method accepts a string:

  • always: debug output is always displayed (default).
  • noredirect: debug output is always displayed, except when the request ends with a redirect.
  • exception: debug output is only generated in the case of an exception.
  • never: debug output is never displayed (this is not very useful at the moment, but in the near future this option can be used to make the debug output available through other means such as e-mail or a log file).
  • [a number]: debug output is only generated if the request takes longer to process than the given number of milliseconds.

In the case of a timeout argument, debug output will always be generated if an exception occurs, even if that happens within the set timeout. In other words, exceptions are never suppressed, unless you specify 'never'. If the timeout is exceeded, redirects will be displayed (delayed).

Clone this wiki locally