This guide provides information about Rhino Service Assurance Server (SAS) API — a Rhino API that enables you to send data from Rhino applications to Metaswitch SAS for diagnosing issues or analyzing data. It explains relevant SAS and Rhino SAS API concepts, describes the Rhino SAS API application development process, and provides instructions on implementing SAS tracing with the API in your code.

This guide assumes that you are a Rhino application developer who:

  • Is familiar with Rhino Telephony Application Server (TAS).

  • Is familiar with the Java programming language.

  • Is familiar with Rhino application development.

  • Has basic knowledge about Metaswitch SAS.

Notices

Copyright © 2024 Microsoft. All rights reserved

This manual is issued on a controlled basis to a specific person on the understanding that no part of the Metaswitch Networks product code or documentation (including this manual) will be copied or distributed without prior agreement in writing from Metaswitch Networks.

Metaswitch Networks reserves the right to, without notice, modify or revise all or part of this document and/or change product features or specifications and shall not be responsible for any loss, cost, or damage, including consequential damage, caused by reliance on these materials.

Metaswitch and the Metaswitch logo are trademarks of Metaswitch Networks. Other brands and products referenced herein are the trademarks or registered trademarks of their respective holders.

Introduction

This section gives an overview of Rhino SAS API, describes the conventions used in the guide, and lists the documents from which you can get more information related to Rhino SAS API.

In addition, it provides the contact information that you can use if you encounter any technical issues while developing Rhino applications with SAS tracing support.

Rhino SAS API overview

Metaswitch Service Assurance Server (SAS) is a platform for analyzing call processing data and diagnosing call processing issues in both fixed line and mobile networks. It offers a web-based user interface (UI) in which the detailed call processing information it received from other network elements is displayed. Through the UI, you can examine the detailed history of how each call was processed.

Rhino SAS API is an API that Rhino TAS provides. With the API, you can send data to SAS servers from within your Rhino application, such as a service or a resource adaptor. An application with SAS tracing capability allows support personnel to use SAS to analyze the calls that it processed and investigate the cause of any issue.

In terms of the API architecture, Rhino TAS provides a SAS facility, with which applications can send diagnostic data to configured SAS servers. From the SAS facility, you can create a trail, which is the primary interface for reporting data to SAS. Multiple related trails form a trace, which reflects a distinct occurrence of subscriber activity. The most common trace type is a call. Trails are composed of two message types: events and markers. An event is a structure for capturing call processing data to be sent, while a marker is a tag for identifying or associating trails.

For example, in an application you can define an event that captures certain processing data of a call. In your processing logic, you can assign a marker such as CALLING_DN_MARKER to a trail or use a marker such as IMS_CHARGING_ID_MARKER to associate multiple trails. Markers that identify a call enable SAS users to search for the call. Markers that associate trails enable the users to view the full call flow.

Conventions

The following table lists the conventions that this guide uses:

Convention Description Example

Monospaced font

Sample code or inline code snippet.

Trail trail = null;

Italic font

Document title.

Rhino Extended APIs

Angle bracket with italic font

A value that you must specify appropriately.

For example, for version: <version-number>, you can specify version: 1.0.

version: <version-number>

Square bracket with italic font

A value that you can optionally specify.

For example, for [true_string], you can omit the parameter or specify it as yes.

[true_string]

Technical support

If you encounter technical issues when implementing SAS tracing in your Rhino applications, first check Rhino SAS API documentation listed in the Related information section and see whether that can help you resolve the issue.

If you cannot resolve an issue with the help of the documentation, check whether your organization has a support agreement with Metaswitch. If it has, contact Metaswitch support team through one of the channels listed on the Metaswitch website.

This section lists the documents related to Rhino SAS API development. It also provides information about major Rhino products that already have SAS tracing implemented.

The following table lists Rhino SAS API related documents:

Document Content related to SAS API

Rhino SAS API Development Guide (this document)

Rhino SAS API introduction and instructions on developing SAS tracing functionalities with the API.

Rhino Extensions API

Reference information about the interfaces, classes, and enums included in the com.opencloud.rhino.facilities.sas package.

Rhino Extended APIs

Introduction of the SAS facility, the Rhino interface for integrating applications with SAS.

Rhino Administration and Deployment Guide

Rhino management console commands for managing the SAS facility and resource bundles.

In addition to these documents, you may need to use other Rhino TAS documents or SAS documents during your application development.

Note The SAS documents on the Metaswitch manuals site require logging in. For more information, contact your Metaswitch representative.

This section provides information about SAS tracing support in Rhino products.

The following products already have SAS tracing implemented:

  • Sentinel VoLTE service

  • Sentinel Express service

  • Sentinel IP-SM-GW service

  • IM-SSF service (call flow only)

  • Sh Cache Microservice

  • HTTP resource adaptor

  • Diameter resource adaptor

  • SIS EasySIP resource adaptor

  • REST API framework

Note
  • To find out whether a feature in a service supports SAS tracing, check relevant product documentation.

  • Rhino services and resource adaptors that aren’t listed may also have SAS tracing implemented. Along with new product versions, the SAS tracing support in Rhino products may change.

For these products or product features, you don’t need to implement SAS tracing.

Application development

With Rhino SAS API, you can implement SAS tracing, which means capturing call processing data in your application and sending it to SAS.

Tip For a development environment, you can manage all the development, deployment, and configuration tasks yourself. However, for a production environment, you may need the help of an administrator to handle tasks such as application deployment or Rhino SAS facility configuration.

To implement SAS tracing for a component in your application, take the following steps:

  1. Create a mini-bundle file — a yaml file that defines events.

  2. Add events to the mini-bundle.

    In each event, you can define parameters for the data you want to send.

  3. Generate a Java enums file from the mini-bundle.

    The enums file contains the enums that represent the events in the mini-bundle and the data the events use.

  4. In your code, add logic for capturing and sending data.

  5. Create a bundle mapping file — a yaml file that defines a unique ID for each mini-bundle in your application.

    In the data sent to SAS, these IDs are added to the event IDs as prefixes so that SAS can differentiate events from different components within the deployed application.

  6. Build your application and deploy it onto Rhino TAS.

  7. Configure the SAS facility to connect to relevant SAS servers and then enable SAS tracing.

  8. Export a resource bundle file from Rhino TAS.

    The resource bundle file is a yaml file that contains event and enum definitions from the mini-bundles, with the event IDs prefixed with relevant IDs from the bundle mapping file. SAS uses information in the file to display events.

  9. Import the resource bundle file in SAS and test the data sent from your application.

Note Information about exporting the resource bundle file and configuring the SAS facility is in the Rhino Administration and Deployment Guide.

The use of the resource bundle file enables Rhino TAS to send only event IDs and relevant parameter values to SAS. SAS reads the event summary information locally from the imported resource bundle file. This way, Rhino SAS API makes efficient use of the bandwidth between Rhino TAS and SAS.

Subsections provide detailed instructions on each step, with necessary code examples. For a complete code example that involves all the steps, check the example service source code of the HTTP Resource Adaptor. The HTTP Resource Adaptor Guide provides more information about the code.

Create a mini-bundle

A mini-bundle is a yaml file in which you define events that you use in your processing logic and the enumerated values that the events use. For each component with SAS tracing support in your application, you usually create a separate mini-bundle.

From each mini-bundle file, you can generate a Java enums file that contains enums representing the events and the data that the events use. All the mini-bundle files in your application, together with the bundle mapping file, are used to generate a resource bundle file, which you will need to import in SAS.

By convention, the mini-bundle file is named sas-bundle.yaml and located in a nested subdirectory under the resources/sas-bundles directory of the corresponding module. The path to the mini-bundle file must match the Java package for the module. For example, if the package is com.abc.feature.xyz, the path of the mini-bundle file should be resources/sas-bundles/com/abc/feature/xyz/. You will need the path and name of the mini-bundle file when you generate the Java enum file from it.

The fields in a mini-bundle are as follows:

version: <file-version> 1
events: 2
    <EVENT_NAME_1>:
        ...
    <EVENT_NAME_N>:
        ...

enums: 3
    <ENUM_NAME_1>:
        ...
    <ENUM_NAME_N>:
        ...
1 The version field specifies the version of the mini-bundle.
2 The events section contains event definitions. For more information, see Define events in a mini-bundle.
3 The enums section contains definitions of the enums that the events use.
Tip

Use enums for known sets of values in an event. For example, you can use enums to represent the reasons for a call diversion or the types of a timer. This can reduce the amount of data sent to SAS and therefore save network bandwidth and storage space, because for enums, you are sending integers rather than objects.

For example, the following mini-bundle file contains the definition of the SETTING_TIMER event and the TIMER_TYPES enum that the event uses:

version: 1.0
events:
    SETTING_TIMER:
        level: 20
        summary: 'Set {{ static_data[0] | enum: "TIMER_TYPES" }} timer for {{ static_data[1] }} seconds'

enums:
    TIMER_TYPES:
        1: 'No reply'
        2: 'Forward to voicemail'
Note In this example, enum is a Liquid filter that SAS provides for getting the relevant value from the enumeration variable. For more information, see Predefined Liquid filters.

To make sure that SAS uses the correct version of an event or enum, follow these guidelines when you update the mini-bundle for a new version of the application:

  • Increment the version number appropriately.

  • Don’t remove old events that you no longer need.

  • Don’t change the meaning or order of events or enum values.

Define events in a mini-bundle

In Rhino SAS API, an event is a structure for capturing call processing data to be sent to SAS. For example, if you want to send data to SAS to indicate that a call is being diverted, you can define an event called DIVERTING_CALL, which has parameters to capture data such as the phone number to which the call is diverted.

By convention, events are defined in the mini-bundle file in the same module where they are used.

Event structure

The structure of an event is as follows:


          
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<EVENT_NAME>:
    level: <event-level>
    summary: <event-summary>
    user_exp: <user-experience>
    details: <call-details>
    call_flow:
        caption: <call-flow-caption>
        data: <call-flow-data>
        protocol: <call-flow-protocol>
        call_leg: <call-leg>
        direction: <call-flow-direction>
        local_address: <call-flow-local-address>
        remote_address: <call-flow-remote-address>
        message: <protocol_message>
        message_id: <call-flow-message-id>
        call_info_id: <call-flow-call-info-id>

This structure contains these fields:

  • <EVENT_NAME>: The name of the event. By convention, event names are capitalized.

  • level: The level of the event. For more information, see Event level.

  • summary: The summary of the event. SAS displays this information as the Event Summary of the event.

  • user_exp: The user experience information of the event. SAS displays this information in the User Experience view.

  • details: Detailed information about the event. SAS displays this information as the detailed text of the event in the Detailed Timeline view.

  • call_flow: The call flow information of the event. SAS displays this information in the Call Flow view.

    This field contains the following subfields (lines 7-16):

    • caption: The text that SAS displays above the arrows in the ladder diagram for the event.

    • data: The protocol message data to add to the call flow diagram.

    • protocol: The protocol to which the message belongs.

    • call_leg: A string that identifies the call leg to which the message belongs.

    • direction: The direction of the call flow.

    • local_address: The local IP address and port that the message is sent from or received at.

    • remote_address: The remote IP address and port that the message is sent from or received at.

    • message: The protocol message data for display in the Message tab of the details panel of the call flow diagram.

    • message_id: The ID of the protocol message.

    • call_info_id: The ID of the "call info" marker that can be used to modify the event or supply additional information for it after it is logged.

Note If you send the same call flow events from multiple nodes, send them using the same fields to ensure correct association within SAS. Otherwise, SAS will display the events separately.

For information about the SAS user interface, check the SAS documentation listed in Related information.

For each event, the level and summary fields are mandatory. If the call_flow field is specified, the data, protocol, and direction subfields are mandatory.

All text fields can contain parameterized text using the Liquid template language. For example, New target: {{ var_data[0] }}.

Tip
  • Don’t use HTML tags. SAS ignores them when it displays an event.

  • Use the <sas:fixed-width-font> and </sas:fixed-width-font> tags to display text in a fixed-width font. The Event example section has an example.

SAS uses two types of predefined event variables: static_data and var_data. These variables are arrays.

  • The static_data variable contains an array of the static data associated with the event, with each element of the array being a 32-bit integer.

  • The var_data variable contains an array of variable data associated with the event, with each element of the array being variable length data (typically a string).

In your event definition, you can use these variables to format the event data that SAS displays. For example, Attempts in the last minute: {{ static_data[0] }} or User agent string: {{ var_data[0] }}.

SAS assigns relevant values to these variables when it needs to display the event data that you send with the staticParam() method or the varParam() method. For more information about using these methods, see Create and send an API message.

In addition, SAS provides predefined Liquid filters that you can use in your event definition. For example, with the if_blank filter, you can get the specified string if the input string is blank. For details about these filters, see Predefined Liquid filters.

Event level

The level of an event determines how SAS displays the event in its GUI.

You can assign one of the following levels to an event:

  • 100: A high-level event that SAS displays in the User Experience view.

  • 80: A user-friendly event that SAS displays in the Detailed Timeline view.

  • 60: A protocol event where the protocol is of interest to the user. SAS displays events of this level in the protocol flows group.

  • 40: An event that SAS displays in the Detailed events group.

  • 20: A diagnostic event that SAS displays in the Engineering level events group. Events of this level are for application engineers.

Event example

The following event named INCOMING_MESSAGE for reporting an HTTP message received contains the summary, details, level, and call_flow fields, in which the call_flow field contains several subfields:

INCOMING_MESSAGE:
  summary: 'Received HTTP {{ var_data[3] }} from {{ var_data[2] }}'
  details: |
            Received HTTP message from {{ var_data[2] }}:{{ static_data[1] }}
            On {{ var_data[1] }}:{{ static_data[0] }}<sas:fixed-width-font>{{ var_data[0] }}</sas:fixed-width-font>
  level: 60
  call_flow:
    caption: '{{ var_data[3] }}'
    data: '{{ var_data[0] | binary }}'
    protocol: HTTP
    direction: in
    local_address: '{{ var_data[1] }}:{{ static_data[0] }}'
    remote_address: '{{ var_data[2] }}:{{ static_data[1] }}'
    message_id: '{{ var_data[4] }}'

In this example, the binary filter is used on the call flow data. For more information, see Predefined Liquid filters.

Generate an enums file

To use an event that you have defined in the mini-bundle in your data processing code, you must generate a Java enums file from the mini-bundle and then add the event to a trail with the relevant enum.

The Ant task for generating the enums file is generate-bundle-enums, and the relevant Java class is provided in the client/lib/rhino-extensions-ant.jar Rhino TAS library.

In Rhino TAS, if you check client/etc/rhino-extensions.xml, you will find the task defined as follows:

<taskdef name="generate-bundle-enums" classname="com.opencloud.ant.sas.GenerateBundleEnumsTask">
    <classpath refid="rhino-extensions-ant.classpath"/>
</taskdef>

You can add this task to your build script so that when the application is built, the enums files are automatically generated from the latest mini-bundle files.

To use the task in your build script, import rhino-extensions.xml first. For example, you can import the file like this:

<import file="${client.home}/etc/rhino-extensions.xml"/>

After that, you can define a build target that calls the task, as illustrated below:

<target name="generate-sas-enums" depends="initiation">
    <generate-bundle-enums destDir="${src}" dir="${basedir}/resources/sas-bundles" includes="*.yaml"/>
</target>

The attributes that you can set for the task are:

  • dir: The base directory to search for the mini-bundle files. The default value is resources/sas-bundles.

  • destDir: The output base directory where the task will save the generated enums files. The default value is src.

  • eventsClassName: The class name for the generated enum. The default value is SasEvent. This attribute is optional.

Note The Java class for the task extends the Ant class MatchingTask, which makes attributes such as includes available.

By convention, the script reads the mini-bundle files from the resources/sas-bundles base directory and saves the generated enums files to the src base directory. The name of the Java package determines the actual subdirectory paths. For example, for a package named com.abc.feature.xyz, the path of the mini-bundle should be resources/sas-bundles/com/abc/feature/xyz/sas-bundle.yaml, and the path of the generated enums file should be src/com/abc/feature/xyz/SasEvent.java.

The following example shows a generated enum class:

public enum SasEvent implements SasEventEnum {

    ANNOUNCE ( 0 ),
    DIVERT ( 1 ),
    ERROR ( 2 ),
    ;

    SasEvent(int id) {
        this.id = id;
    }

    @Override
    public int id() {
        return id;
    }

    private int id;
}

Once this file is generated, you can add relevant events to a trail in your code. For example, you can use the following code to add the ANNOUNCE event:

EventMessage event = trail.event(SasEvent.ANNOUNCE);

For more information, see Create and send an API message.

Warning Don’t edit the generated enums file. If you need an updated version of the file after changing the relevant mini-bundle, use the Ant task to generate it again.

Create and send an API message

After adding events to the mini-bundle and generating the relevant Java enums file from it, you can start to add data sending logic in your code.

To send data, you need to create or get a trail first. The SAS facility creates trails either explicitly, when a resource adaptor calls startTrail() or implicitly, when a resource adaptor or service calls getOrCreateTrail() with an activity reference. Trails typically last the lifetime of an activity. Multiple activities may share the same trail. For example, a database lookup in a call setup uses the trail of the SIP dialog or transaction.

You can use the methods provided by the SasFacility interface to create or get a trail. For example, the getTrail() method enables you to get the trail attached to an activity.

When a SLEE event is delivered to a service, the invoking trail is available through an invoking trail accessor, which you can get by calling the getInvokingTrail() method of the InvokingTrailAccessor class. The method returns the SAS trail attached to the activity context interface (ACI) on which the event was fired. On subsequent down calls that result in a new SLEE activity being created, the invoking trail is automatically attached to the new activity. This can save you from having to add code that explicitly gets a trail from the invoking ACI and attaches it to a new ACI. If necessary, you can call the setInvokingTrail(Trail) or remove() method to manually set or clear the invoking trail.

The following example code shows how to get the invoking trail from an invoking trail accessor:

Trail trail = InvokingTrailAccessor.getInvokingTrail();

Once you have a trail, you can use it to create API messages and send data to SAS.

With a trail, you can send two types of data:

  • Send event data by adding events that you defined in the mini-bundle to the trail.

  • Send marker data by adding predefined markers with appropriate values to the trail.

Note The objects and methods for creating and sending API messages are defined in the Java library rhino-extensions.jar. Make sure that you include this library in your class path.

Send event data

Once you have a trail, you can use it to create an API message and send data with the message.

The following example code for call diversion illustrates the steps for sending data with events:


         
1
2
3
4
5
Trail trail = InvokingTrailAccessor.getInvokingTrail();
EventMessage event = trail.event(SasEvent.DIVERT_CALL);
event.staticParam(diversionTypeToSasType(diversionType));
event.varParam(newTarget);
event.report();

These steps are as follows:

  1. Add an event to the trail with an event message object.

    As shown in line 2, the event() method is used to add the DIVERT_CALL event to the trail.

  2. Assign values to event parameters with an appropriate method.

    As shown in lines 3 and 4, the staticParam() method and the varParam() method are respectively used to add the values.

  3. Send the data out with the report() method.

    As shown in line 5, the report() method of the event message object is used to send the data.

Once you are familiar with the process, you can combine the methods in the same statement. The example code above can also be written as follows:

InvokingTrailAccessor.getInvokingTrail()
    .event(SasEvent.DIVERT_CALL)
    .staticParam(diversionTypeToSasType(diversionType))
    .varParam(newTarget)
    .report();

Add an event to the trail

Once you get a trail, you can add an event that you defined in the mini-bundle to the trail.

Events can be added with the event() method. In this method, you must use the Java enum generated from the mini-bundle to add the relevant event. For example, the following code uses the enum for the SETTING_TIMER event to add the event to a trail:

EventMessage event = trail.event(SasEvent.SETTING_TIMER);

EventMessage is an object type for sending event data to SAS. Each object can have parameters holding information with details about event specifics.

Assign values to event parameters

After adding the event, you can use appropriate methods of the event message object to assign values to event parameters. For example, the following code assigns values to two parameters with the staticParam() method, which assigns a value to a 32-bit fixed-length parameter:

event.staticParam(timerTypes.No_Reply);
event.staticParam(timerValue);
Important The number and type of the parameters that you assign to the event must match the number and type of the parameters that you define for the event in the mini-bundle.

For information about the methods that you can use, see the Rhino Extension API document listed in the Related information section.

Send the data out

Once you have an event message object ready, you can use the report() method to send the relevant data out.

Important The report() method is asynchronous, which means once it is called, the Rhino SAS facility enqueues the message for sending. Make sure the values of the parameters don’t change between their assignments and the actual sending of the data.

Send marker data

To make the events and trails for a single call appear in a single trace and searchable, you can attach markers to trails.

Markers can associate trails. If you use the same value for an association marker, such as GENERIC_CORRELATOR_MARKER, on multiple trails, SAS associates those trails to give its users a comprehensive view of the trace. Markers can also make trails searchable. If you add a value to a searchable marker, such as CALLING_DN_MARKER, which is attached to a trail, SAS users can use that value to search for the trail once it is sent through.

Note A marker can serve multiple purposes. For example, you can use a marker to both search for a trail and associate multiple trails.

The following code example illustrates the steps for adding an association marker and sending the data out:


         
1
2
3
4
MarkerMessage associationMarker = trail.marker(Markers.IMS_CHARGING_ID_MARKER);
associationMarker.traceScope();
associationMarker.varParam(icid);
associationMarker.report();

The following code example illustrates the steps for adding a searchable marker and sending the data out:


         
1
2
3
4
MarkerMessage searchableMarker = trail.marker(Markers.CALLING_DN_MARKER);
searchableMarker.noneScope();
searchableMarker.varParam(fromAddress);
searchableMarker.report();

As shown in these examples, sending marker data takes these steps:

  1. Add a marker to the trail with a MarkerMessage object.

    As shown in line 1 of the examples, the marker() method is used to respectively add the IMS_CHARGING_ID_MARKER marker and the CALLING_DN_MARKER to the trails.

  2. Define the scope of the marker.

    For association markers, you must define an appropriate scope. SAS only associates markers that have the same scope. A scope can be a branch of a call leg or a trace. In line 2 of the examples, the traceScope() method specifies that the scope of the marker is the trace, while the noneScope() method specifies that the marker isn’t an association marker.

  3. Add values to the marker.

    To add the values, use the staticParam() method for static data and the varParam() method for variable data. As shown in line 3 of the examples, the values of the icid and the fromAddress variables are respectively added to the IMS_CHARGING_ID_MARKER and the CALLING_DN_MARKER markers.

  4. Send the marker out.

    Once the marker message object is ready with appropriate marker, scope, and values, you can use the report() method of the object to send the marker data out.

    You can also use the report() method of the Trail object to send the data out. For example, in the first example, you can change the last line to trail.report(associationMarker).

For the best practices in using markers, see the Best practices sections.

Define bundle mappings

To let SAS distinguish the events reported from different modules that use separate mini-bundles, Rhino TAS allows you to use a bundle mapping file to define a unique prefix for the events in each mini-bundle. In the resource bundle file exported for SAS, Rhino TAS adds the prefixes to the IDs of the events from relevant mini-bundle files.

A bundle mapping file is a yaml file, with all bundle mappings defined at the top level. The prefixes are 16 bits, that is, from 0 to 65535 (0xFFFF), and must be unique. You can define the prefixes in the integer or hexadecimal format. By convention, the hexadecimal format is used. For the packages, use fully qualified bundle names, such as com.abc.feature.xyz.

For example, the following lines define the mappings for com.abc.feature.m and com.abc.feature.n:

com.abc.feature.m: 0x0001
com.abc.feature.n: 0x0002

After creating the bundle mapping file, use the setsasbundlemapping Ant task to instruct Rhino to use the file.

As to the mappings in the bundle mapping file, you can add or remove them either by manually editing the file, or by using the Rhino management console commands. For example, you can use the setsasbundlemapping command to add a mapping or use the removesasbundlemapping command to remove a mapping.

To ensure consistency between deployments and upgrades, you generally need to create the bundle mapping file and assign bundle mappings manually. If you want Rhino TAS to automatically assign prefixes to unmapped bundles, set the assignbundlemappings attribute in the install Ant task to true when you install the relevant deployable unit.

Note If no prefix is assigned for a mini-bundle, Rhino TAS doesn’t send the events in it. To check for any mini-bundles with missing prefix mappings, use the listunmappedsasbundles Rhino management console command.

Create a deployment package

Upon finishing your application development, you will need to package the application and deploy it onto Rhino TAS, so that the application can run and send data to SAS.

When you create a SLEE component JAR file, make sure that you include the following files:

  • The mini-bundle files in your application.

    Rhino TAS needs to read the event definitions from these mini-bundle files. In the JAR file, put the mini-bundle files in subdirectories under META-INF/sas-bundles, with the subdirectory structure matching the names of relevant Java packages. For example, if the mini-bundle is for a package named com.abc.feature.xyz, its path in the JAR file should be META-INF/sas-bundles/com/abc/feature/xyz/sas-bundle.yaml.

  • The compiled Java enums files generated from relevant mini-bundle files.

    Compile and package the enums files like other Java class files. For example, in the JAR file, the path of one of the enums file might be com/abc/feature/xyz/SasEvent.class.

Don’t include the bundle mapping file in the deployment package. Once the application is deployed, you can use the setsasbundlemapping Ant task to install the bundle mapping file onto Rhino TAS.

After deploying the application and installing the relevant bundle mapping file, you can export a resource bundle file from Rhino TAS with the exportsasbundle command. SAS uses information in the resource bundle file to format the data it receives from your application.

Test a Rhino SAS API application

To ensure your application correctly processes data, test it before putting it into production. For applications with SAS tracing support, in addition to local tests, you must check the data received by SAS and make sure relevant events are displayed as expected.

For the SAS tracing functionalities in your application, make sure that you test these items:

  • The mini-bundle file.

    Review the event definitions and see whether there are any issues. If an event uses enums, make sure the relevant enums are defined in the enums section.

  • The Java enums file.

    Make sure a Java enums file is generated for each mini-bundle. If there are any errors in the mini-bundle file, you will see errors when you generate the enums file.

  • The bundle mapping file.

    Make sure a bundle mapping file is in place for your application. Check the bundle mapping file and make sure a prefix has been defined for each Java package that has a mini-bundle. Check whether there are any prefix conflicts.

    Tip For deployed applications, use the listunmappedsasbundles command to identify missing prefixes.

To check the data that SAS receives from your application, take the following steps.

  • Create a deployment package and then deploy the application onto Rhino TAS.

  • Install the bundle mapping file onto Rhino TAS with the setsasbundlemapping Ant task.

  • Configure the Rhino SAS facility to connect to relevant SAS servers.

  • Export a resource bundle file from Rhino TAS with the exportsasbundle command.

  • Enable SAS tracing in Rhino TAS.

  • Import the resource bundle file in SAS.

    The resource bundle file contains information about the events defined in your application. Once you import it in SAS, SAS can format the data it receives with relevant event information and then display complete event information through its user interface.

  • Check the events that SAS receives and make sure everything is as expected.

    For example, you may want to check whether the set of events for a feature represents the data flow and the processing logic, because that can help SAS users better understand the situation and diagnose issues easily. For more information about the best practices like this, see Best practices.

For instructions on importing resource bundle files and checking events in SAS, see the SAS documentation listed in Rhino SAS API documentation.

Best practices

Once you are familiar with the Rhino SAS API application development process, you may want to search for best practices for improving your efficiency and productivity in application development.

This section and its subsections describe the best practices in using events, markers, associating trails, and handling object parameters. With these best practices, you can define your application logic efficiently and avoid making mistakes.

Tip To use SAS for issue diagnostics, you can implement SAS tracing in resource adaptors and/or services. If SAS tracing isn’t provided in the resource adaptors that you use, add call flow events and markers in your service. Rhino SAS facility handles trails from both services and resource adaptors.

Events

Well-defined events can make it easy for support personnel to investigate call processing issues, and well-used events can minimize the amount of data that your application sends, therefore saving the network bandwidth and the storage space that your application uses.

Follow these guidelines when you define or use events:

  • Assign appropriate event levels.

    For details, see Event level.

  • Minimize event data for protocol events.

    If a resource adaptor already sends a SAS event containing a protocol stack message, don’t send the same stack message in SAS events at the application layer. Doing that wastes the bandwidth and storage space and may also clutter the SAS trace with redundant information that reduces the clarity of the trace.

  • Be aware of event ordering.

    Send SAS events inside a service before calling down to a resource adaptor to send a protocol message. This way the event you create will precede the resource adaptor’s SAS events and provide context and explanation for them.

  • Consider the entire narrative of the SAS trace.

    The goal in sending SAS events is to record a concise, linear narrative of what happened and why, both in normal and abnormal flows. By ensuring a coherent SAS narrative as a whole on the SAS server, we can afford the SAS user a greater understanding of what went wrong in instances where the call flow is unexpected.

Markers

Well-used markers can make it easy for users to find the information they need and help to produce a coherent view of the call flow.

Follow these guidelines when you use markers:

  • Use an appropriate marker to record each party’s identity in at least one event in each trail.

    To SAS users, a trace is worthless if they cannot find it.

  • Assign at least one start marker and one end marker to a trace.

    The start marker allows the trace to be found as part of a time-constrained search. The end marker ensures that SAS writes the trace to the disk in a timely manner.

  • Make a trace searchable even if there is no appropriate marker.

    If there is no Call ID or SIP URL or anything else that is searchable associated with a trail, make something up and employ one of the existing searchable markers. If an end user identity looks like a directory number (DN), pretend it is a DN. and if it looks like a URL, pretend it is a URL. If there is a token, which would be known or discoverable, passed around, pretend it is a Call ID. Test and make sure that the pretense works, and then document that as a way of searching for the trace.

  • Associate trails in the same trace.

    If there is a risk that two trails in a trace might not be associated, use at least one association marker with the same value and appropriate scope in both trails.

  • Don’t use DN or URI markers as association markers.

    These markers aren’t unique to a particular trace and will result in incorrectly associating unrelated trails in a single trace. For example, if a subscriber makes two calls in quick succession and the DN marker is used for trail association, SAS will display the trails for these two calls in the same search result, which is incorrect.

    For DN or URI markers, always use the noneScope() method to specify that they aren’t association markers.

Trail association and colocation

You can associate SAS trails that form part of the same call with the associate(Trail) method. If the Rhino SAS facility is configured with multiple SAS servers, different trails may not use the same server.

Associating trails that use the same SAS server is more efficient, as you only need to send a trail association message to the one server informing it that the trails form part of the same trail group. If the trails being associated use different servers, a generic association marker gets sent to each SAS server with the UUID of the one trail. In that case, SAS needs to perform additional operations to associate the trails.

To colocate related trails on the same SAS server, use the startColocatedTrail(Trail) method or the startAndAssociateTrail(Trail, Scope) method. These methods create a new SAS trail using the same server as the given trail and, in the second case, automatically send a trail association message to the server.

Object parameter handling

You can use the following methods to pass data to a SAS EventMessage object:

  • staticParam()

  • varParam() or varParams()

  • threadSafeParam()

Static parameters

The staticParam() method allows a 32-bit integer value in the EventMessage object to be sent to the SAS server. In the SAS mini-bundle, you can get the corresponding enum value and format SAS EventMessage details with meaningful information.

Whenever possible, use staticParam() instead of varParam(), as the size of the resulting EventMessage object is much smaller and therefore requires less memory, CPU usage, and network bandwidth. This is important because unlike most logging APIs that offer a log level hierarchy to modulate the log verbosity, SAS tracing sends every SAS event to the server regardless of the level specified in the SAS mini-bundle.

The cumulative effect of using varParam() calls in place of staticParam() calls can have a sizeable impact on system performance. Therefore, pack as much fixed data into the mini-bundle as is practical to minimize the amount of data to be sent to the server. Encoding data in an enum value and decoding it to text in the bundle helps ensure this and allows the application to scale efficiently.

Consider refactoring code to use staticParam() if:

  • The varParam() method is used to send a string from a finite set of strings (typically for debugging).

  • The varParam() method is used to format fixed strings with variable data.

    Either define these strings in full inside the mini-bundle or separate the constant text into the mini-bundle and divide the variable data into static parameters and variable-length parameters appropriately. If this becomes too complex, for example if you are passing differently formatted data at different locations in your code to a single event, consider using separate events.

The staticParam() method allows parameters to be passed in as int, Integer, or EnumParameter.

Variable-length parameters

If you cannot define the data being passed to SAS as an enum or the length of the data varies, use the varParam() or the varParams() method. These methods accept object parameters, allowing for flexibility in the data passed to the EventMessage. The varParams() method behaves similarly to varParam() but allows two or three objects to be passed in as parameters. You can achieve the same result by chaining multiple varParam() calls together.

The varParam() method handles parameters passed to it differently depending on their types:

  • null: Encodes as a zero-length byte array.

  • byte[]: Copies directly into the message.

  • java.nio.ByteBuffer: Unsupported. Coerces to a zero-length byte array. In this case, use threadSafeParam(byte[]) instead.

  • java.lang.String: Encodes as UTF-8 and copies into the message.

  • Any other type: Calls Object#toString() and then proceeds as for java.lang.String.

For the implementation of EncodeableParameter, the method calls EncodeableParameter#encode(ByteBuffer) and copies bytes written to stream into the message, while for the implementation of MarshalableParameter, the method calls MarshalableParameter#marshal() and copies the returned byte[] into the message.

Don’t use the varParam() method for parameters that implement EnumParameter. It will automatically add the parameter as a static parameter, with a debug message logged. Use the staticParam(EnumParameter) method for enum parameters instead.

Thread safety

Data passed as parameters is only copied or marshalled into the relevant EventMessage object when the report() method is invoked. Therefore, don’t modify the parameters passed to the EventMessage object before invoking the report() method.

Additionally, parameters passed using the threadSafeParam(byte[]) method will not be copied even after the report() method is invoked. This means that if a value you want to pass as a parameter may be subsequently modified, and you already have defensively copied it, you should use the threadSafeParam(byte[]) method to put the defensive copy in the event. This allows the SAS API to be more efficient, by not making its own defensive copy when the report() method is invoked.

Different from the above, the staticParam(), varParam(), and varParams() methods convert the following types of parameters when they are added:

  • null: Convert to an empty byte array.

  • ByteBuffer: Convert to an empty byte array.

  • EnumParameter: Convert to its integer value.

Check isEnabled() on the trail object before marshalling. The isEnabled() method returns a boolean value, which is true if SAS tracing is enabled on the SAS facility that the trail object was created from. By wrapping the code responsible for marshalling the object conditioned on the isEnabled() method, you can achieve further reduction in memory and CPU usage.

Predefined Liquid filters

SAS provides predefined Liquid filters that you can use to further process the event data it receives. It runs the filters used in an event when it displays the event.

Important To display the data from your application, SAS assigns relevant values to the static_data or var_data variables, and then renders the Liquid template for the relevant event. For that reason, this section uses the Liquid terminology, instead of the Java terminology, for the description of the filters.

This section lists the filters that you may need when you develop applications with Rhino SAS API.

To use the predefined filters, separate the input value and the filter with a | symbol. Some filters can have additional parameters. To specify them, use a colon (:) followed by a comma-separated list of parameters.

For example, the following template calls the foo filter with three parameters, static_data[0], static_data[1], and static_data[2]:

{{ static_data[0] | foo: static_data[1], static_data[2] }}

You can combine multiple filters. In that case, they are applied from left to right.

Filters for formatting static_data

Data that your application attached to an event using the staticParam() method is available to the Liquid templates in the mini-bundle in the static_data array variable. Without any filters, SAS renders this variable as an unsigned decimal string, or as an empty string if the value is absent.

To format static_data in other forms, use the following filters:

Filter name Description Parameters

boolean

Treats an integer as a Boolean value and then converts the value to a string.

If the integer is 0, it returns "False". Otherwise, it returns "True".

For example, the expression {{ 0 | boolean: "yes", "no" }} returns "no".

  • <input>: The integer to process.

  • [true_string]: A string to specify the value of true. The default value is "True".

  • [false_string]: A string to specify the value of false. The default value is "False".

enum

Gets a value stored in an enumeration variable defined for an event in the mini-bundle.

  • <input>: The key to look up.

  • <enum_name>: The name of the enumeration variable.

float

Formats the input integer as a 32-bit float value.

<input>: The integer to format.

http_status_code_text

Returns the text description for an integer HTTP status code.

For example, the expression {{ 404 | http_status_code_text }} returns "Not Found".

<input>: The integer HTTP status code.

int32

Formats the input integer as a signed integer with optional radix (for example, 10 for decimal) parameter.

  • <input>: The integer to format.

  • [radix]: The parameter that specifies the radix to display the number in. The default value is 10 (decimal).

uint32

Formats the input integer as an unsigned one with optional radix (for example, 10 for decimal) parameter.

  • <input>: The integer to be formatted.

  • [radix]: The parameter that specifies the radix to display the number in. The default value is 10 (decimal).

Filters for formatting var_data

Data that your application attaches to an event using the varParam() method is available to the Liquid templates in the mini-bundle in the var_data array variable. Without any filters, SAS casts the value as UTF-8 encoded text and displays it as such. If the variable value is not valid UTF-8 encoded text, SAS renders it as Invalid UTF-8 String. If the value is absent, SAS renders it as an empty string.

Tip When a Java String is passed to the varParam() method, the corresponding var_data is UTF-8 encoded.

Filters for formatting UTF-8 var_data

To format var_data that is UTF-8 encoded in other forms, use the following filters:

Filter name Description Parameters

append_if_present

Appends an object to another and returns a concatenated string if the first object is present. Otherwise returns an empty string.

  • <input>: The object to check.

  • <append_obj>: The object to append.

append_port

Appends the specified port number to the input IPv4 address/hostname or IPv6 address. For an IPv6 address, the filter adds brackets.

Consider the following example:

{{ var_data[2] | append_port: var_data[3] }}

If the value of var_data[2] is 0000:0000:0000:0000:0000:0000:0000:0001, and the value of var_data[3] is 3000, the result is "[0000:0000:0000:0000:0000:0000:0000:0001]:3000".

  • <input>: The IP address or hostname in any format.

  • <port>: The port number to append to the address.

format_json

Converts the given JSON object to a well-formatted string.

You can use this filter in combination with the read_json filter. For example, with the following code, you read the value of the var_data[3] variable as a JSON object and then convert the object to a well-formatted string:

{{ var_data[3] | read_json | format_json }}

<input>: The JSON object to convert.

if_absent

Returns the specified string if the input value is absent. Otherwise returns the input value.

  • <input>: The input that might be absent.

  • <ret_string>: The string to return if the input is absent.

if_blank

Returns the specified string if the input value is blank. Otherwise returns the input value.

Consider the following example:

body: {{ var_data[2] | if_blank: "No body" }}

In this example, if the value of the var_data[2] variable is abcd, the result is "body: abcd". If the variable is blank, the result is "body: No body".

  • <input>: The input that might be blank.

  • <ret_string>: The string to return if the input is blank.

prepend_if_present

Prepends an object to another and returns a concatenated string if the first object is present. Otherwise returns an empty string.

Consider the following example:

The call was ended{{ var_data[1] | prepend_if_present: " by subscriber " }}.

If the variable var_data[1] is present and its value is 012-345-6789, the result is "The call was ended by subscriber 012-345-6789." If the variable isn’t present, the result is "The call was ended."

  • <input>: The object to check.

  • <prepend_obj>: The object to prepend.

read_json

Converts a string into a JSON object.

For example, after getting the p object with the following code:

{% assign p = var_data[3] | read_json %}

You can print the entire JSON structure with this code:

{{ p }}

Or print a field with this code:

{{ p.field_name }}

<input>: The string to convert.

Filters for formatting other var_data

To format var_data that is not UTF-8 encoded, use the following filters. For example, you may need one of these filters when you pass the contents of a binary encoded message in a byte array to the varParam() method.

Note These filters need to access the raw data form of the input. Don’t use them after other filters because all filters output UTF-8 strings that don’t have a raw data form.
Filter name Description Parameters

as_hex

Converts the input, which may include binary data, into a hexadecimal string.

<input>: The data to convert. It must not be nil.

binary

Interprets the input non UTF-8 data as valid UTF-8 data so that the data can be further processed.

Use this filter when SAS needs to process the results of a template render as binary.

For example, the variable for the data subfield of the call_flow field needs to be passed through this filter, unless the whole message is always valid UTF-8 text. Without the filter, the protocol message is invalid because SAS will cast its value and render it as Invalid UTF-8 String.

Tip Some nominally text-based protocols like SIP and HTTP may have body elements that are not valid UTF-8.

<input>: The raw data to process.

decode_diameter_details

Formats a string as Diameter details.

<input>: The string to format.

decode_diameter_summary

Formats a string as a Diameter summary

<input>: The string to format.

decode_sip_details

Formats a string as SIP details.

<input>: The string to format.

decode_sip_summary

Formats a string as a SIP summary.

  • <input>: The string to format.

  • [protocol_variant]: The protocol variant.

dn

Formats the input string as a directory number.

<input>: The string to format.

dump_as_hex

Dump a byte string as a hexadecimal string. Returns "" if the data is nil.

<input>: The byte string to process.

hex_and_ascii

Formats the input data by displaying it as a hex dump, with corresponding printable ASCII characters alongside.

Use this filter to display blobs of binary data for which a decoder isn’t available.

<input>: The data to format.

ip_addr

Formats the input string’s byte sequence as an IP address.

The filter interprets four bytes as an IPv4 address and 16 bytes as an IPv6 address.

<input>: The string to format.