NexJ Logo

Debugging

The following topics provide information about using log files to diagnose problems.

Debugging SQL queries

To evaluate certain SQL queries, it is beneficial to have a log file with bind variables appearing inline within the query.

You can use the debug:extract-sql-to-file function to obtain this alternative format by passing in a log file with SQL adapter logging as a parameter. This function separates the SQL queries in the input log file from other log entries and extracts them into an output log file in a simpler format that allows for easy debugging.

For the log file to be generated, the log level for the SQLAdapter class in the log4j.properties file must be set to DEBUG. The following line of code specifies this log level:
Dlog4j.logger.nexj.core.persistence.sql.SQLAdapter=DEBUG

Info

Extracting SQL dumps from large log files can impact your system performance. If you have a large log file, it is recommended that you split the log file into smaller files of 50 MB or less before extracting the SQL dump.

Example of the debug:extract-sql-to-file function

The following example shows the debug:extract-sql-to-file function run on an input log file named SystemOut.log to generate an output log file named SystemOut_sqllog.sql.

(debug:extract-sql-to-file
   "C:\\work\\logs\\SystemOut.log"
   "C:\\work\\logs\\SystemOut_sqllog.sql"
   'mssql
   #t
   #t
)

While scanning the input log file for SQL dumps, the function looks for lines such as the following:

; 12:34:58,368 DEBUG [SQLAdapter] (http-7080-exec-2) select top 1 A.id, A.binaryData,
 A.classCode, A.locking,
A.name from NJExternalReport A where A.deletedFlag = ? and A.viewPrincipalId in
 (?, ?) and A.name = ?
; 12:34:58,368 DEBUG [SQLAdapter] (http-7080-exec-2) Bind[0] = 0
; 12:34:58,368 DEBUG [SQLAdapter] (http-7080-exec-2) Bind[1] =
 236D3BB475BC4B38BDBA4CECDEB62237
; 12:34:58,368 DEBUG [SQLAdapter] (http-7080-exec-2) Bind[2] =
 00000000000010008000BEEF0000000A
; 12:34:58,368 DEBUG [SQLAdapter] (http-7080-exec-2) Bind[3] = 'BBList_excel'

In the output log file, these lines appear as follows:

select top 1 externalReport_A.id, externalReport_A.binaryData,
 externalReport_A.classCode, externalReport_A.locking,
externalReport_A.name from NJExternalReport externalReport_A where
 externalReport_A.deletedFlag = 0
and externalReport_A.viewPrincipalId in
 (hextoraw('236D3BB475BC4B38BDBA4CECDEB62237'),
hextoraw('00000000000010008000BEEF0000000A')) and externalReport_A.name =
 N'BBList_excel'

You can run a third-party formatter to expand the SQL queries in the output file and evaluate them further.

Configuring integration logs

Integration logging provides a summary of requests and responses for NexJ CRM channels that interact with a third-party application or process.

Integration logs are used by system administrators to validate processing times, identify performance issues, and troubleshoot reported errors for the external application or process. A system administrator also enables integration logging for the system or for a specific channel in NexJ System Admin Console.

You can modify exception and description information for integration logs, which are stored in the NJIntegrationLog table. You can also filter and display the logs on the Integration Log page in NexJ System Admin Console. By default, the description attribute is empty and only send and receive exceptions are automatically logged for outbound services. Exceptions are not logged if they occur after an outbound response is successfully received. Logging of these exceptions must be manually specified.

You configure logging of exceptions for an outbound service by passing exceptions either as a string or as an exception object. You configure logging of descriptions by modifying the description attribute in the service that is bound to the target channel.

You can also configure logging of a custom attribute by specifying the attribute in a subclass of the SysIntegrationLog.

Configuring integration logging of exception and description attributes

You can configure integration logging of the exception and description attributes for an outbound service by modifying the outbound service in NexJ Studio.

To configure integration logging of exception and description attributes:

  1. In NexJ Studio, navigate to the Integration layer and select the Services tab.
  2. Create a new service or select and open an existing outbound service.
  3. To configure logging of the exception attribute, in the Source tab, pass the exception as either as a string or as an exception object, as shown in the examples below.

    Info

    You must add the exception before the service terminates.

    The following example shows how to pass an exception as a string.

    ((SysIntegrationLog'senderEntry)'exception
       "Exception: collection of IDs returned when only one was expected"
    )

    The following example shows how to pass an exception as an exception object.
    ((SysIntegrationLog'senderEntry)'exception exceptionObject)

    Info

    You do not need to enable exception logging for inbound services. Errors encountered by the inbound service are assigned the ERROR log level. Errors are written to the Exception column in the NJIntegrationLog table.

  4. To configure logging of the description attribute, in the Source tab, modify the description attribute in the service that is bound to the target channel, as shown in the examples below.

    Info

    For an outbound service, add the description before the service terminates.

    The following example shows how to enable logging of the description for an inbound service.
    ((SysIntegrationLog'receiverEntry)'description "<sample description string>")
    The following example shows how to enable logging of the description for an outbound service.
    ((SysIntegrationLog'senderEntry)'description "<sample description string>")
    Similarly, you can enable exception logging by modifying the exception attribute, as shown in the following inbound service example.

    (SysIntegrationLog'receiverEntry
       (message
          (: exception "Exception: collection of IDs returned when only one was
     expected"
    )
    )
  5. Save and close the outbound service.

You have configured integration logging for exception and description attributes.

Configuring integration logging of a custom attribute

You can configure integration logging of a custom attribute by specifying the attribute in a derived class of the SysIntegrationLog class. A derived class is also known as a subclass.

Also, you must specify that the System.IntegrationLogger component uses the new derived class instead of the SysIntegrationLog class.

To configure logging of a custom attribute:

  1. In NexJ Studio, navigate to the Business Model layer and select the Classes tab.
  2. Create a new class and specify the SysIntegrationLog class as the base class of the new class.
  3. Add the custom attribute or attributes that you want to configure logging for.

    Info

    If you want to persist logging of the custom attribute, ensure that you add the attribute to an extension table rather than the NJIntegrationLog table.

    If you want to persist logging of the custom attribute, ensure that you add the attribute to an extension table rather than the NJIntegrationLog table.

  4. In the Resources layer, select the Components tab, and open the System.IntegrationLogger component.

  5. In the Source tab, change the integrationLogClass property value to the name of the derived class that you created earlier. For example, change:
    <Property name="integrationLogClass"><SysIntegrationLog></Property>
    to:
    <Property name="integrationLogClass"><SysIntegrationLogDerivedClass></Property>

  6. If you want to persist logging of the custom attribute, then you must specify the column name of the custom attribute before the service terminates. The following example shows the syntax for persisting logging of a custom attribute as a string:
    ((SysIntegrationLog'senderEntry)'<customAttribute> "<AttributeStringValue>")
    The following example shows how to persist logging of an outbound call to the messageId custom column:
    ((SysIntegrationLog'senderEntry)'<messageId> "<1567651>")

You have added logging of a custom attribute to the integration logs.

Analyzing dataload extract output

You can use the debug:normalize-objects-to-file function to identify differences between objects in a large collections of messages. When you extract data using the invoke-dataload-extract function, several messages in the collection may have the same attribute values as other messages. The debug:normalize-objects-to-file function separates unique attributes of messages from those that are common to multiple messages. This minimizes the amount of data for you to review when you debug issues.

Info

The debug:normalize-objects-to-file function can only be used to analyze persisted messages that specify an object ID (OID) and class.

The function uses the following arguments:

  • objects collection<msg>, which represents the collection of messages that need to be parsed
  • funPrefix string, which is a string to prefix all of the defined variables
  • outFile, which is the location of the output file with the generated scheme code

Example of the debug: normalize-objects-to-file function

The following example shows the debug:normalize-objects-to-file function run on data related to a group of contacts.

(debug:normalize-objects-to-file
   (collection
      (message
         (: :class "Person")
         (: :oid (oid #z2931241FFA5944A483A249EF3482988C))
         (: firstName "Tagan")
         (: lastName "Kenton")
         (: tier "A")
      )
      (message
         (: :class "Person")
         (: :oid (oid #z2931241FFA5944A48FA249EF3482988C))
         (: firstName "Giovanni")
         (: lastName "Kenton")
         (: tier "A")
      )
    )
    "contactImport"
    (string-collection-affix (java.io.File'separator) (debug:get-project_dir) "etc"
  "scratchpads" "seed_contact_data.scm")
)

After the function is run, the output file (seed_contact_data.scm) is saved to a scratchpad in your project directory.

The output file lists attributes common to all messages at the top of the file, for example, the class, last name, and tier in this case. The file then lists individual messages in the same sequence as in the input file, but only displays attributes that are unique to each message, for example, the first name.

(define contactImport:getMsgCollection
   (lambda ()
      (define msgCollection (collection))
       (msgCollection'add (contactImport:Person:getMessage
 "Person_102931241FFA5944A483A249EF3482988C"))
       (msgCollection'add (contactImport:Person:getMessage
 "Person_102931241FFA5944A48FA249EF3482988C"))
       ; return
       msgCollection
   )
)
(define contactImport:Person:getMessage
   (lambda (imgId)
     ; Common values are defined once
     (define msg
         (message
           (: :class "Person")
           (: :oid (string->oid ((string-match imgId "_([A-Z\\d]+)$") 1)))
           (: lastName "Kenton")
           (: tier "A")
         )
     )
     (cond
        ((= imgId "Person_102931241FFA5944A483A249EF3482988C") (msg 'firstName
 "Tagan"))
        ((= imgId "Person_102931241FFA5944A48FA249EF3482988C") (msg 'firstName
 "Giovanni"))
        (else (set! msg ()))
      )
      ; return
      msg
    )
)