Improvements in JScript logging

Introduction

Since there is no way to debug a JScript in LSO developers have to use other means to find errors. This can include writing to the debug console in the Script Tool, writing to the Smart Office log file or the classic, showing dialog boxes.

In many cases you want to write both to the debug console for development purposes and to the log file for troubleshooting deployed scripts. One way to solve this is to create your own wrapper functions that does both these things and some of you have probably done this already. To make logging a bit easier from JScript we have added some new methods and properties to the ScriptDebugConsole class. First I will describe how a log wrapper function might be implemented and then how the new API methods can be used to remove the need for a wrapper function.

The new log methods are available in Smart Office version 10.0.3 or later.

Log wrapper function

The most basic log wrapper function would simply write to both the ScriptDebugConsole and to a Log4Net logger instance. To do this you need to store the debug parameter in a member variable so that it can be used from other functions than the Init function. You also need declare a logger instance for the script.

A simple log wrapper function can be seen in the script below. The function can be improved by checking that the log level is enabled before logging. Other improvements could be to add additional methods for the different log levels (debug, info, warning, error and fatal) or add a parameter that indicates the log level.

import System;
import System.Windows;
import System.Windows.Controls;
import MForms;
import Mango.Core;

package MForms.JScript {
   class LogWrapperTest {
      static var logger : log4net.ILog = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
      var debug;

      function log(message : String) {
         logger.Debug(message);
         debug.WriteLine(message);
      }

      public function Init(element : Object, args : Object, controller : Object, debug : Object) {
         this.debug = debug;

         log("A log message");
      }
   }
}

New log methods

From Smart Office version 10.0.3 the ScriptDebugConsole class exposes parts of the Log4Net logger methods and properties directly to make it easier to log from scripts. Some other benefit are that if the script is running in the Script Tool the log messages are automatically added to the debug console and that the script name is automatically appended to the log message in the log file.

The following script example shows how to use the different log functions. The script first calls all the log functions with just a string message. After that the script provokes different kinds of exceptions and logs a message and the exception using all log methods. Note that even if the fatal log level is supported you probably should not use this from a script. The fatal level should only be used if something is preventing the Smart Office client from running as expected.

import System;
import System.Windows;
import System.Windows.Controls;
import MForms;

package MForms.JScript {
   class ScriptLogTest {
      public function Init(element : Object, args : Object, controller : Object, debug : Object) {

         debug.Debug("A debug message");
         debug.Info("An information message");
         debug.Warn("A warning message");
         debug.Error("An error message");
         debug.Fatal("A fatal message");

         try {
            Int32.Parse("NotANumber");
         } catch (ex) {
            debug.Debug("Failed to parse", ex);
         }

         try {
            controller.MethodDoesNotExist();
         } catch (ex) {
            debug.Info("Failed to call method", ex);
         }

         try {
            var s : String = null;
            s.ToString();
         } catch (ex) {
            debug.Warn("Failed to convert to string", ex);
         }

         try {
            var uri = new Uri(null);
         } catch (ex) {
            debug.Error("Failed to create Uri", ex);
         }

         try {
            "1,2".Split[","][2];
         } catch (ex) {
            debug.Fatal("Failed to index array", ex);
         }
      }
   }
}

The output in the Script Tool looks like this.

To check the output in the log file start the Log Viewer from the About dialog or by using the URI internal://log. The value in the Origin column for all log entries will be the ScriptDebugConsole but the first part of the Message column will be the name of the script, including the namespace. To find logs from a particular script just filter on the script name in the Message column as can be seen in the screenshot below.

Checking log levels

Before logging lots of data and before formatting data to be logged you should check that the target log level is actually enabled. This is especially important for logs on the lower log leves (debug and info) and for frequent logs in loops etc.

The log level can be checked using the properties IsDebugEnabled, IsInfoEnabled, IsWarnEnabled, IsErrorEnabled and IsFatalEnabled. These properties are available on the Log4Net logger and on the ScriptDebugConsole class from version 10.0.3.

An example of how to use these properties and when it is definitely appropriate can be seen below.

if(debug.IsDebugEnabled){
   var xml = document.OuterXml;
   var message = "XML response received, length=" + xml.Length + " content=" + xml;
   debug.Debug(message);
}

How to check if the new log methods are available

In case a script needs to support different LSO version but you still want to use the new log methods when available an API check must be included in the script. The easiest way to do an API check in JScript is to just add an if-statement for one of the new methods or properties. If the statement evaluates to True the API is available. The example script below has the same log wrapper function as described previously but with an added API check.

import System;
import System.Windows;
import System.Windows.Controls;
import MForms;
import Mango.Core;

package MForms.JScript {

   class LogWrapperTest {
      static var logger : log4net.ILog;
      var debug;

      function log(message : String) {
         if (debug.Debug) {
            debug.Debug(message);
         } else {
            if(logger == null) {
               logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
            }
            logger.Debug(message);
            debug.WriteLine(message);
         }
      }

      public function Init(element : Object, args : Object, controller : Object, debug : Object) {
         this.debug = debug;

         log("A log message");
      }
   }
}

One thought on “Improvements in JScript logging

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s