The Report action instructs an OSB Proxy Service to send tracing information to a JMS queue. By default, this queue’s content is stored in a database and made accessible trough the OSB console. In a Report action, you can specify an arbitrary XML “Report Body” and multiple “Report Indices” (key-value pairs used for searching). So Reporting is basically “Logging on Vitamins”. You can learn more about OSB Reporting from J. v. Zoggel. In this article, I will describe how to populate the Report Body with meaningful information using a generic XQuery transformation.

reporting-pipeline-600

Each proxy service starts with a pipeline pair called ReportingPipelinePair. It has an Assign action in its request pipeline and a Report action in its response pipeline. The whole pipeline pair can be copied as is (we don’t have inheritance or templates in OSB). In the Assign action, we populate the variable reportInfo with the following XQuery transformation, using the OSB variables $inbound and $body as parameters:

declare function xf:reportInfo_V1_0
  ($inbound as element(*), $requestBody as element(*)) 
  as element(ns0:reportInfo) {
  <ns0:reportInfo>
    <ns0:proxyName>{ 
      fn:tokenize($inbound/@name,'\$')[last()] }
    </ns0:proxyName>
    <ns0:operation>{ 
      $inbound/ctx:service/ctx:operation/text() }
    </ns0:operation>
    <ns0:correlationId>
      {
      if (exists($inbound/ctx:transport/ctx:request/tp:headers/
        tp:user-header[@name='CORRELATION_ID']/@value))
      then data($inbound/ctx:transport/ctx:request/tp:headers/
        tp:user-header[@name='CORRELATION_ID']/@value)
      else if (exists($requestBody//*:correlationId)) 
        then $requestBody//*:correlationId/text()
      else 
        fn-bea:uuid()
      }
    </ns0:correlationId>
    <ns0:timestamp>{ fn:current-dateTime() }</ns0:timestamp>
    <ns0:inbound>
      <ns0:request>{ $requestBody }</ns0:request>
      <ns0:response/>
    </ns0:inbound>
    <ns0:outbound>
      <ns0:request/>
      <ns0:response/>
    </ns0:outbound>
  </ns0:reportInfo>
};

declare variable $inbound as element(*) external;
declare variable $requestBody as element(*) external;

xf:reportInfo_V1_0($inbound, $requestBody)

What we do here is to store the proxy service’s name, the operation, and the current time. Note this is just an example (every report will contain this information anyway), you might want to store different things in your reportInfo. We also try to find a correlation identifier in the request headers and the payload. If we succeed, we use it, otherwise we generate a new UUID. And we save the whole payload ($requestBody) – we might want to log it later on, for example if an error occurs. But when we make it to the response pipeline of our ReportingPipelinePair, we know that no error has occurred. We will thus create a success report based on reportInfo’s content using the following XQuery transformation.

declare function xf:createSuccessReport
  ($reportInfo as element(ns0:reportInfo))
  as element(ns0:successReport) {
  <ns0:successReport> {
    for $proxyName in $reportInfo/ns0:proxyName
    return
      <ns0:proxyName>{ data($proxyName) }</ns0:proxyName>
    } {
    for $operation in $reportInfo/ns0:operation
    return
      <ns0:operation>{ data($operation) }</ns0:operation>
    } {
    for $correlationId in $reportInfo/ns0:correlationId
    return
      <ns0:correlationId>{ data($correlationId) }</ns0:correlationId>
    } {
    for $timestamp in $reportInfo/ns0:timestamp
    return 
      <ns0:startTime>{ data($timestamp) }</ns0:startTime>
    }
    <ns0:endTime>{ fn:current-dateTime() }</ns0:endTime>
    <ns0:duration>{ 
      fn:current-dateTime() - xs:dateTime($reportInfo/ns0:timestamp) }
    </ns0:duration>
  </ns0:successReport>
};

declare variable $reportInfo as element(ns0:reportInfo) external;

xf:createSuccessReport($reportInfo)

So we just copy the proxyName, operation and correlationId from reportInfo, use the current time as value for endTime, and calculate the time our service took. (Isn’t it wonderful that you can just subtract to dateTime values and get a duration?). Again, you might want to extract different information. The resulting successReport is our Report Body, and $reportInfo/correlationId will be used as value for the Report Index CORRELATION_ID.

You probably noticed in the first transformation that there are request and response elements under both the inbound and the outbound element. Whenever we call a service, we store the respective request payload in outbound/request and the response payload in outbound/response. inbound/response is supposed to hold the response our service returns. As we have seen, we do not log and of this information in success cases. But in the case of an error, we store the SOAP fault we are about to return in $reportInfo/inbound/response and use reportInfo‘s content Report Body content. Having all the incoming and outgoing payloads appear in the report is extremely helpful when analyzing problems.

How to construct SOAP faults and service stacktraces using reusable XQuery transformations will be a topic of one or more future articles.