The Zurich release has arrived! Interested in new features and functionalities? Click here for more

sabell2012
Mega Sage
Mega Sage

NOTE: MY POSTINGS REFLECT MY OWN VIEWS AND DO NOT NECESSARILY REPRESENT THE VIEWS OF MY EMPLOYER, ACCENTURE.

 

DIFFICULTY LEVEL:    INTERMEDIATE
Assumes having taken the class SSNF and has good intermediate level of knowledge and/or familiarity with Scripting in ServiceNow.


Have you ever wanted to be able to send errors to the System Log from a Client Script? a UI Action? a UI Policy? Well, in this mini-lab article I will show you a method for doing so.

 

I had been wanting gs.log/gs.info on the Client (i.e. Browser) for some time when one day it occurred to me that I could write a simple reusable UI Script / Ajax Script Include combo that would give me that capability.

 

I will be using techniques that I described in my previous two articles:

 

Pragmatic Patterns: Ajax - Architecting Your Code

Mini-Lab: Using Ajax and JSON to Send Objects to the Server

 

Requirements 

  1. Ability to write to the system log from a Client Script, UI Action, or UI Policy script.
  2. Must be re-usable from any of those mechanisms (i.e. emulate gs.log/gs.info on the server side)

 

Design 

  1. Create an Ajax Script Include with a function that will take a "message" string and write it to the System Log using gs.log, gs.info, gs.debug, gs.error, or gs.warn.
    • The gs.log part will be straight forward, we simply do something like log(string, location) and translate that on the server side to gs.log(string, location).
    • The gs.info et. al. part is a bit trickier.   Here we will have to be a bit innovative.   Something like logextended(string, [array of values], type) may need to be done.
  2. Create a UI Script with a function that will take a string and communicate it via Ajax to an Ajax Script Include.
    • Make this a Function Library that will be callable similar to gs.
  3. Write an example onLoad Client Script that will call the UI Script function.   This will be used in testing.

 

NOTE: In examining the design I determined that a Script Include Function Library would be overkill as the number of code lines needed to write to the System Log would be too few to warrant extraction.

 

find_real_file.png

 

 

Lab 1.1:   Writing Log Entries Into The System Log From the Browser

 

Development 

We will start with a bottom up development approach.

 

Ajax Script Include (Design Element #1)

 

1. Navigate to System Definition -> Script Includes.   This will display the Script Includes list view.

2. Click on the New button.   This will display a blank Script Include form.

3. Fill in the form with the following:

    • Name: SystemLoggerAjax
    • Client Callable: Checked (this will cause the Ajax template to be added to the Script field).
    • Accessible from: All application scopes
    • Active: checked
    • Description:   System Logging functions
    • Script:

 

var SystemLoggerAjax = Class.create();
SystemLoggerAjax.prototype = Object.extendsObject(AbstractAjaxProcessor, {

	// simple logging function
	systemLog: function() {
		var message = this.getParameter('sysparm_message');
		var loggingSource = this.getParameter('sysparm_logging_source');

		gs.info(message, loggingSource);
	},

	// complex logging function
	systemLogExtended: function() {
		var message = this.getParameter('sysparm_message');

		// convert the JSON object back to our array
		var messageArgs = JSON.parse(this.getParameter('sysparm_args'));

		// type of log message to be executed
		var messageType = this.getParameter('sysparm_type').toLowerCase();

		switch (true) {
			case messageType == 'info':
				gs.info(message, messageArgs);
				break;
			case messageType == 'debug':
				gs.debug(message, messageArgs);
				break;
			case messageType == 'warn':
				gs.warn(message, messageArgs);
				break;
			case messageType == 'error':
				gs.error(message, messageArgs);
				break;
		}
	},

	type: 'SystemLoggerAjax'

});

 

Your AJAX Script Include should look like this:

find_real_file.png

 

UI Script (Design Element #2)

 

1. Navigate to System UI -> UI Scripts.   This will display the UI Scripts list view.

2. Click on the New button.   This will display a blank UI Script form.

3. Fill in the form with the following:

    • Name: LoggingUtils
    • Global: Checked
    • Active: Checked
    • Description: System Logging Functions
    • Script:

 

var log = Class.create();

// Simple logging function
log.log = function(message, loggingSource) {
loggingSource = '[BROWSER] ' + loggingSource; // spiff up the source a bit

var systemLogger = new GlideAjax('SystemLoggerAjax');
	systemLogger.addParam('sysparm_name','systemLog');
	systemLogger.addParam('sysparm_message', message);
	systemLogger.addParam('sysparm_logging_source', loggingSource);
	systemLogger.getXML(function(result){ }); // no callback action
};

// Complex logging functions - can probably be refactored
log.info = function(message) {
	_syslogExt(message, _toArray(arguments), 'info');
};

log.warn = function(message) {
	_syslogExt(message, _toArray(arguments), 'warn');
};

log.debug = function(message) {
	_syslogExt(message, _toArray(arguments), 'debug');
};

log.error = function(message) {
	_syslogExt(message, _toArray(arguments), 'error');
};

// Transmit complex logging task to server side
function _syslogExt(message, messageArgs, messageType) {

	message = '[BROWSER] ' + message; // spiff up the message
	var systemLogger = new GlideAjax('SystemLoggerAjax');
	systemLogger.addParam('sysparm_name','systemLogExtended');
	systemLogger.addParam('sysparm_message', message);

	// method for transmitting complex objects to the server
	systemLogger.addParam('sysparm_args', JSON.stringify(messageArgs));
	systemLogger.addParam('sysparm_type', messageType);
	systemLogger.getXML(function(result){ }); // no callback action

}

// Simple function to convert "argument" arrays into a true array for manipulation
function _toArray(source) {
	var target = [];

	// shave off the first argument - this is our message line
	for (var i=1; i < source.length; i++) {
		target.push(source[i] + '');
	}

	return target;
}

 

Your UI Script should look like this:

find_real_file.png

 

Client Script (Design Element #3)

 

1. Navigate to System Definition -> Client Scripts.   This will display the Client Script List View.

2. Click on the New button.   This will display a blank Client Script form.

3. Fill in the form with the following:

    • Name: syslog Example
    • Table: Incident
    • UI Type: Desktop
    • Type: onLoad
    • Active: checked
    • Inherited: unchecked
    • Global: checked
    • Description:   System Logger Example
    • Script:

 

function onLoad() {
	var incidentNumber = g_form.getValue('number');
	var userName = g_user.userName;

	// Test the simple logging function
	log.log('---> The form has finished loading and is ready for user input. Incident: ' + incidentNumber, 
		'CS:syslog example');

	// Test the complex logging function
	log.info('---> Test {0}: form has finished. userID = {1}, incident = {2}', '1', userName, incidentNumber);

	log.error('---> Test {0}: form has finished. userID = {1}, incident = {2}', '2', userName, incidentNumber);
}

 

Your Client Script should look like this:

find_real_file.png

 

Unit Testing

1. Navigate to Incident -> Open. The Open Incident list view will be displayed.

2. Choose an open Incident (click on the incident number to open the form).

3. Navigate to System Logs -> System Log -> All. The System Log will be displayed.

4. Order by Created Date descending.

5. Filter the Message column with: *--->

6. Note the three new entries. Your results should look something like this:

 

sabell2012_0-1697924399423.png

 

And there you have it!   A way to write entries to the System Log from the Client (browser) side!

 

Best practices demonstrated:

  1. Maintainability.  
  2. Extensibility.
  3. Coding Best Practice: Another method of logging unit test results.

NOTE: If you are working with this example in your PDI and run into issues with the system not working right: There is a problem with the Client Scripts being editable, or your client script runs into an issue in Global. I found that flipping the glide.script.block.client.globals to false and then doing a cache.do seems to clear it all up...after a couple of tries. This seem to fix the problem with the OOB client scripts "Validate Client Script w/o Server Obj" and "Validate Client Script Func Declaration" as well. I had to initially deactivate both just to work on my Client Script. Really weird/odd behavior that I wasn't able to replicate on a real DEV instance. Check out this post if you want to know more as it was illuminating: Isolate Script in London (thanks to  for digging this stuff out).

 

Enjoy!

Steven Bell.

 

If you find this article helps you, don't forget to log in and mark it as "Helpful"!

 

sabell2012_0-1697638059364.png


Originally published on: 01-28-2016 09:57 AM

I updated the code and brought the article into alignment with my new formatting standard.