Dynamic Logic Traceability

Traceability is essential for diagnosing issues in dynamic logic. There are cases when dynamic logic gives unexpected results. Since there are many branching constructs (loops, conditions, etc.), it becomes difficult to trace the path the logic takes. Traceability helps trace a path that a code takes.

Traceability is not an in-built functionality. You need to code it into dynamic logic. The log.debug() or log.trace() statements help with this. These statements re-run the dynamic logic and help you follow the path that the code takes. See "Logging" in Groovy Examples or Testing Dynamic Logic Using Logs for more information.

Guidelines

Following is a list of guidelines to follow when using log statements:

  • Each branching construct must have a logging statement:

    • a log.debug() statement must have essential context information for a use case.

    • a log.trace() statement must have more-detailed context information for complete traceability.

  • You must provide context to each log line. If multiple processes are running simultaneously, it becomes difficult to separate log lines.

A debug logging statement offers essential information, and trace logging statement gets all details, including logs of debug level.

Example of Non-Traceable Dynamic Logic

Here is an example of a code that does not have logging statements. This makes it difficult to trace errors. This example searches a claim for a message in the Message Group DO_NOT_DERIVE_SERVICE_TYPE. If there is such a message, then the code does not assign any value to the serviceType object.

def messageList = []
messageList.addAll(claim.claimLineList?.claimLineMessageList)
messageList.addAll(claim.claimMessageList)

if (messageList.any { it.inMessageGroup('DO_NOT_DERIVE_SERVICE_TYPE') }) {
    // don't assign service type
} else {
    // some logic here, simplified
    claim.serviceType = '03'
}

It is difficult to figure out what the logic does and identify when the error occurs from the code. It is unclear whether the logic even executes.

Example of Traceable Dynamic Logic

Here is the same dynamic logic, but with log statements:

log.debug('Start for claim {0}', claim.code)

def messageList = []
messageList.addAll(claim.claimLineList?.claimLineMessageList)
messageList.addAll(claim.claimMessageList)

log.debug('claim {0}: {1} distinct messages', claim.code, messageList.size())

def message = messageList.find { it.inMessageGroup('DO_NOT_DERIVE_SERVICE_TYPE') }

if (message) {
    // don't assign service type
    log.debug('claim {0}: found message {1} in messageGroup DO_NOT_DERIVE_SERVICE_TYPE', claim.code, message.code)
} else {
    log.debug('claim {0}: serviceType is derived', claim.code)
    // some logic here, simplified
    claim.serviceType = '03'
}
Here, the use of list.find { ... } replaces list.any { ... }. This searches through every item in the list and returns the first one that matches the message group or returns null. This way, we can figure out when the message appears in the logs.
Application Logs

After using the debug logging statements for the dynamic logic, we find the following information in the application logs. In this example, the dynamic logic code is CLAIMTYPE:

<date, time> | DEBUG | ohi.dynamiclogic.claimtype.CLAIMTYPE | Start for claim CLA_TEST_01
<date, time> | DEBUG | ohi.dynamiclogic.claimtype.CLAIMTYPE | CLA_TEST_01: 2 distinct messages on the claim
<date, time> | DEBUG | ohi.dynamiclogic.claimtype.CLAIMTYPE | CLA_TEST_01: serviceType is derived
...
<date, time> | DEBUG | ohi.dynamiclogic.claimtype.CLAIMTYPE | Start for claim CLA_TEST_02
<date, time> | DEBUG | ohi.dynamiclogic.claimtype.CLAIMTYPE | CLA_TEST_02: 3 distinct messages on the claim
<date, time> | DEBUG | ohi.dynamiclogic.claimtype.CLAIMTYPE | CLA_TEST_02: found message TYPED_MSG_01 in messageGroup DO_NOT_DERIVE_SERVICE_TYPE

Here, we see that claim CLA_TEST_01 has two messages, but none for the message group: DO_NOT_DERIVE_SERVICE_TYPE. So, the service type sets to O3. We see that the claim CLA_TEST_02 has three messages. One of them is TYPED_MSG_01, which is in the message group: DO_NOT_DERIVE_SERVICE_TYPE. So, the claim does not have a service type.

Logging slows the system and creates large log files on a live application. We recommend using debug logging process for the required duration.
Context in logging is important. If multiple users use the same dynamic logic, it is difficult to distinguish between the log lines.