Transaction tracking user exit sample

The AceSpanTraceExample user exit sample demonstrates how you can use a user exit to capture flow context information, which could then be used to implement a simple span trace for message flows that contain HTTP, Kafka, or MQ transport nodes.

A span trace can be used to track transactions across multiple components, so that you can see how the transactions have been routed through the components. This approach typically passes a span identifier from one component to another, through transport headers, to allow the transaction to be correlated; this enables you to see a broader view of the transactions than you could see if you used a separate trace for each individual component.

If you run an integration server with this sample user exit enabled, an MQ queue on the integration server's default queue manager (AceSpanTraceExample) is used to write a span summary message each time a flow with an MQ, Kafka, or HTTP transport input node processes a message. The user exit reads or writes a span identifier when the flow inputs or outputs an MQ, Kafka, or HTTP message.

The AceSpanTraceExample sample is in the install_dir\sample\extensions\userexits\transports directory, and the loadable exit library is called AceSpanTraceExample.lel.

The AceSpanTraceExample is a C++ implementation that uses the App Connect Enterprise interface defined through the MqsiTransportUserExitBase class, which uses C user exit callbacks to show how a message flow span trace might be implemented. The logic of the exit is implemented in the AceSpanTraceExample.hpp/cpp.

The sample implements the following transport-specific callback functions, which are invoked by IBM® App Connect Enterprise when HTTP, Kafka, and MQ transport nodes process messages:
  • onTransportInputMessage

    This callback informs the user exit that an MQInput, HTTPInput, SOAPInput, HTTPAsyncResponse, SOAPAsyncResponse, or KafkaConsumer node has received a message, and signals the start of a message flow processing span. The callback provides the message flow and node context and any requested headers, such as MQ MQRFH2 header fields, HTTP headers, or Kafka properties.

  • beforeTransportOutputMessage

    This callback informs the user exit that an MQOutput, HTTPRequest, HTTPAsyncRequest, SOAPRequest, SOAPAsyncRequest, RESTRequest, RESTAsyncRequest, or KafkaProducer node is about to emit a message. This callback allows the user exit to set any required headers in the output message, such as MQ MQRFH2 header fields, HTTP headers, or Kafka properties.

  • afterTransportOutputMessage

    This callback informs the user exit that an MQOutput, HTTPRequest, HTTPAsyncRequest, SOAPRequest, SOAPAsyncRequest, RESTRequest, RESTAsyncRequest, or KafkaProducer node has emitted a message. This callback allows the user exit to receive details of the message emitted and its destination, such as MQ MQMD.msgid.

  • onTransportResponseMessage

    This callback informs the user exit that an HTTPRequest, SOAPRequest, or RESTRequest node has received a response message. The callback provides the message flow and node context and any requested headers.

  • beforeTransportGetMessage

    This callback informs the user exit that an MQGet or KafkaRead node is about to start a read operation to attempt to get a message.

  • onTransportGetMessage

    This callback informs the user exit that an MQGet or KafkaRead node has received a message. The callback allows the user exit to obtain required headers from the received message, such as MQ MQRFH2 header fields or Kafka properties.

  • onTransportTransactionComplete

    This callback informs the user exit that a message flow processing span, which started at an onTransportInputMessage, is complete. The callback allows the user exit to access the completion status and any last exception code.

For more information, see the functional description in the AceSpanTraceExample.hpp header file.
Note: The following class files, which are in the Base subfolder, must not be modified:
  • MqsiTransportUserExitBase
  • MqsiUserExitWrapper
If the transaction tracking exit is enabled for a message flow that includes any of the following message flow nodes, and the output terminal of these nodes has multiple direct connections to downstream message flow nodes (rather than using a FlowOrder node), the onTransportResponseMessage or onTransportGetMessage callback is invoked each time a message is propagated to the directly connected output connections:
  • HTTPRequest
  • RESTRequest
  • SOAPRequest
  • MQGet
  • KafkaRead
If you need to include multiple downstream connection branches in the message flow, use a FlowOrder node to control the order in which the connection branches are invoked.
The sample source code is also precompiled into the loadable exit library, AceSpanTraceExample.lel, which can be used by the integration server if it is specified in the UserExits section of the integration server's server.conf.yaml file:

UserExits:
  activeUserExitList: 'AceSpanTraceExample'
  userExitPath: '<path>'
The user exit path defines the directory or set of directories that the integration server's exit manager inspects to see whether there are any loadable exit library files.

When an integration server starts and finds the sample loadable exit library, it calls its onStartServer function and passes context information including the name of the integration server and the version at which the integration server is running, and also allows the sample to register the name of a span trace example. If that name matches an entry in the activeUserExitList, the transport-specific callback functions in this library are invoked by the integration server when messages are processed by the message flows.

The onStartServer callback in this example also provides a data structure of transports for which it should be called (either HTTP, Kafka, or MQ) and field names and locations that the functions in the user exit library will be passed (on an input message) or set (on an output message).

The exit callbacks in the sample are called from message flow nodes that interact with the MQ, Kafka, or HTTP transport. All these transports receive message flow context information including server name, application name, library name, message flow name, and message flow node name and type. Additionally, they are passed data from fields as defined in the example data structure table. For example, the following table defines the data structure for HTTP:

Table 1. Example data structure for HTTP
Transport Location Field name
HTTP HTTPHeader traceparent
HTTP HTTPHeader tracestate
HTTP HTTPHeader x-remote-addr
With the data structures defined in this table, the transport-specific callback functions implemented in the sample carry out the following processing:
  • The onTransportInputMessage receives any headers that are found in the input, such as traceparent, tracestate, and x-remote-addr. It sets a new value for traceparent and then saves the values in the environment, which can then be accessed from other message flow nodes that are involved in the trace.

    For example, you could have a message flow containing an HTTPInput node, and the onStartServer function has registered the data structure and specified that it should be called for HTTP transport messages. When the HTTPInput node receives a message, it checks to see whether it needs to invoke the user exit, based on the information in the transport section. If it does need to invoke the user exit, it performs a lookup in the message assembly to extract the values that are available, then calls the onTransportInputMessage function that is implemented in the user exit library. In addition to the data structure, it also passes some flow context information, such as the name of the integration server, the application, the message flow name, the message flow node name and type, and the transport type of the message flow node that has called this user exit.

    The sample implements this user exit interface. The sample receives the headers and sets a new value for traceparent (traceParent = TraceStartedinACE), which appears in the span context. The sample also sets a value for the AceTrace environment variable by using setEnvironmentValue(name), which provides an audit trail of the nodes that have called the user exit.

  • The beforeTransportOutputMessage function retrieves the value of traceparent from the environment by using getEnvironmentValue(name), and appends the name of the current message flow node to the AceTrace environment variable. The outbound HTTPOutputHeader is then set (traceparent=traceparent+"flow-context") and the returned values are updated into the output message header.

    For example, you could have an HTTPRequest node that is about to send an output message. The transport type has been checked, and it has been determined that the callback function in the user exit library needs to be invoked. The data that is relevant to the HTTP transport is passed over, and the message flow exit is invoked with the flow context information. The sample then appends the information about the current message flow (such as the message flow name, application name, and message flow node name) into the AceTrace environment variable, which is recording the audit trail of nodes that have caused the user exit to be invoked. The sample also retrieves the traceparent that was set by the HTTPInput node, and concatenates that with the flow context information. The HTTPRequest node has now been added onto the end of the traceparent in the environment, which was previously set by the HTTPInput node. The information in the data structure is updated into the message assembly and set into the outbound HTTP header.

  • The onTransportResponseMessage allows the user exit to record the arrival of a response message from an HTTP request.
  • The beforeTransportGetMessage signals that a mid-flow Get or Read message flow node is about to start a get or read operation on the transport.
  • The onTransportGetMessage signals that a mid-flow Get or Read message flow node has received a message from a transport and provides trace data from it.
  • The onTransportTransactionComplete message is used by the user exit to complete the span trace and issue a summary message to an MQ queue on the integration server's default queue manager (AceSpanTraceExample).
  • The afterTransportOutputMessage retrieves information from the transport relating to the message that has been transmitted. For HTTP, this would include the HTTP RequestURL.