Skip to content
neoneo edited this page Sep 3, 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.
  • All of the above also applies to threads (using <thread> in the configuration), as long as the thread is joined.

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

application.context = new cflow.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>.

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 example on the configuration page would result in debug output that looks something like this.

Example debug output

Suppressing debug output partially or entirely

By default, debug output is generated and displayed for every request. To only show debug output to a whitelist of ip's, use the remoteAddresses property:

application.context.setRemoteAddresses(["127.0.0.1", "::1"]);

Further, you can suppress the output based on the server name (as in cgi.server_name) that received the request so that people viewing your site on another domain will never see it:

application.context.setServerName("localhost");

You can also suppress display output in certain cases, using the generateOutput property. Its setter accepts one of the following strings:

  • always: debug output is always generated (default).
  • noredirect: debug output is always generated, except when the request ends with a redirect (the redirect is followed instead of delayed).
  • exception: debug output is only generated in the case of an exception.
  • never: debug output is never generated (this is not very useful at the moment).
  • [a number]: debug output is only generated if the event cycle 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).

Other uses of debug information

By default, debug information is displayed at the end of the generated content (in case of html output, within the body). This is not the only thing you can do with it. It is in fact quite easy to extend the default functionality so that instead of displaying the output, it is sent to a dedicated inbox. Another use case is storing the debug information in a database for later analysis.

The default functionality is implemented in DefaultOutputStrategy (in the debug folder). This component implements the OutputStrategy interface. You can write your own strategy and tell the context to use it instead:

application.context.setOutputStrategy(myOutputStrategy);

The generate() method receives an array of debug messages, which are structs. The method returns a string. This string is actually appended to the response. So, for example, in order to see what's in the array, you could write the following function inside your strategy component:

component implements="cflow.debug.OutputStrategy" {
    public string function generate(required array messages) {
        savecontent variable="local.output" {
            WriteDump(arguments.messages);
        }
        return local.output;
    }
}

Now you know what's inside, you can write other logic against this array.

If you just want to send the output to an e-mail address, it's easier to extend the default implementation and call super like this:

component extends="cflow.debug.DefaultOutputStrategy" {
    public string function generate(required array messages) {
        var output = super.generate(arguments.messages);
        // send e-mail (Railo specific)
        mail from="app@mycompany" to="inbox@mycompany" subject="Debug info" type="html" {
            WriteOutput(output);
        }
        return "";
    }
}

The method returns an empty string, so no debug output is actually displayed on the page itself. This is nice if you want to receive reports of usage by non-technical users.

Combined with the generateOutput property, this can be quite powerful. For instance:

application.context.setGenerateOutput(200);

will now send you an e-mail for every exception and every request that took more than 200 ms. Nice!

Be careful in production

The overhead in collecting debug information is (probably) not very big, but it's there. So don't use the debug context in production without thinking.

Clone this wiki locally