Skip to main content
Ben Nadel at the NYC Node.js Meetup (Sep. 2018) with: Manon Metais
Ben Nadel at the NYC Node.js Meetup (Sep. 2018) with: Manon Metais ( @NYCReact )

Adding A Description To FusionReactor Tracked Transactions In Lucee CFML 5.2.9.40

By on
Tags:

A few weeks ago, I looked at using the FusionReactor API (FRAPI) to add custom instrumentation in Lucee CFML. In that post, one of the features that I explored was the ability to wrap a portion of your code in a "Tracked Transaction" such that the execution of said code would show up in the "Tracing" and "Relations" insights within the various FusionReactor dashboards. So far, this has been a great feature! However, I've been wanting even more insight into what a particular Transaction is doing; and, I think I can use the "Transaction Description" as a place to store that information in Lucee CFML 5.2.9.40.

CAUTION: The API for the FusionReactor "Transaction" class isn't documented in their JavaDocs. As such, what I'm outlining here is the result of me dump()-ing out the Transaction object, seeing what methods it exposes, and then - through trial-and-error - seeing how those methods affect the various Cloud and Standalone dashboards.

Wrapping a portion of code in a Tracked Transaction shows me how long the code took to execute; and, where it executed within the overall request processing timeline. However, that alone doesn't give me much information as to why the code took as long as it did. Really, it would be great to have some additional insight into the context in which the code executed.

For example, if I had an algorithm that was processing values, it might be nice to know how many values were being processed. Or, if I had a response payload that was being GZipped, it might be nice to know the length of the payload that was being compressed.

The obvious place to put this would be in the Properties collection associated with each Transaction. However, Properties - at the time of this writing - only show up Standalone dashboard, not the Cloud dashboard. As such, I'm going to try stuffing that arbitrary, open-ended data in the Description field using the .setDescription() method of the tracked transaction.

In the following experiment, I am creating three transactions:

  • One with a Name only.
  • One with a Name and a Description.
  • One with a Name and a co-opted Description that contains a serialized data structure.

In each case, I am using a User Defined Function (UDF) that wraps the execution of a ColdFusion Callback / Closure in a Tracked Transaction:

<cfscript>
	
	/**
	* CAUTION: VARIADIC FUNCTION, accepts 2 or 3 arguments. I wrap the given callback in
	* a FusionReactor tracked transaction, assigning the given name and OPTIONAL
	* description. The results of the callback are returned to the calling context.
	*/
	public any function segmentWrap() {

		// PROXY: Name, Description, Callback.
		if ( arguments.len() == 3 ) {

			var proxyArguments = {
				"name": arguments[ 1 ],
				"description": arguments[ 2 ],
				"callback": arguments[ 3 ]
			};

		// PROXY: Name, Callback.
		} else {

			var proxyArguments = {
				"name": arguments[ 1 ],
				"description": "",
				"callback": arguments[ 2 ]
			};

		}
		
		return( segmentWrapWithNameAndDescription( argumentCollection = proxyArguments ) );

	}

	/**
	* I wrap the given callback in a FusionReactor tracked transaction, assigning the
	* given name and description. The results of the callback are returned to the calling
	* context.
	* 
	* @name I am the transaction name.
	* @description I am the transaction description.
	* @callback I am the callback to wrap.
	*/
	public any function segmentWrapWithNameAndDescription(
		required string name,
		required string description,
		required function callback
		) {

		// CAUTION: In a production setting, you'd have to check to make sure this class
		// is available AND that the .getInstance() call returns a non-null value.
		// However, for the sake of simplicity, I'm keeping this demo naive.
		var frapi = createObject( "java", "com.intergral.fusionreactor.api.FRAPI" )
			.getInstance()
		;

		var segment = frapi.createTrackedTransaction( name );
		// Set the "tracked transaction" description - this provides additional detail in
		// the "Tracing" and "Relations" tabs in the Cloud and Standalone dashboards,
		// respectively.
		segment.setDescription( description );
		// NOTE: This is only available in the Standalone dashboard - NOT THE CLOUD.
		segment.setProperty( "Test Property", "Test Value" );

		try {

			return( callback() );

		} finally {

			segment.close();

		}

	}

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //

	// Wrapping transaction with NAME only.
	segmentWrap(
		"MyFirstSegment",
		() => {

			sleep( randRange( 50, 100 ) );

		}
	);

	// Wrapping transaction with NAME and DESCRIPTION.
	segmentWrap(
		"MySecondSegment",
		"This is my second segment, starting at #getTickCount()#",
		() => {

			sleep( randRange( 50, 100 ) );

		}
	);

	// EXPERIMENT: Since the Description is just a string, we can stuff any data we want
	// into it as long as its a string. Like a serialized struct.
	// --
	// NOTE: The are ways to set "properties" on a transaction. However, those do not
	// show up in the CLOUD dashboard - only the Standalone dashboard transactions.
	segmentWrap(
		"MyThirdSegment",
		serializeJson({
			httpMethod: cgi.request_method,
			httpServer: cgi.server_name,
			httpReferer: cgi.http_referer,
			httpUserAgent: cgi.http_user_agent,
			ancientChineseSecret: "Small pull-requests get reviewed with more clarity."
		}),
		() => {

			sleep( randRange( 50, 100 ) );

		}
	);

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //	

	echo( "Done with demo." );

</cfscript>

<script type="text/javascript">

	// Refresh page to generate traffic (the Cloud dashboard seems to have trouble
	// syncing from my local system unless there is a steady stream of traffic).
	setTimeout(
		function() {

			window.location.reload();

		},
		1000
	);

</script>

As you can see, I am making three calls to segmentWrap(), each with an increasing amount of data. And by using the .setDescription() method on the Tracked Transaction, we end-up seeing this in the Standalone dashboard:

As you can see, all of the "descriptions" that we set show up alongside the individual tracked transactions that we outlined in our code.

And, in the Cloud dashboard, here's what we get:

Ideally, I'd be using transaction Properties to be setting this kind of key-value data. However, since I primarily use the Cloud dashboard, which does not currently support Properties, I think co-opting the transaction Description will at least get me some of the at-a-glance insight that I am looking for in my FusionReactor tracing.

Want to use code from this post? Check out the license.

Reader Comments

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel