Debugging Server-Side Code with GSLog

Apr 16, 2026 4 min read
ServiceNow Debugging Logging JavaScript

When server-side debugging starts with a few gs.info() calls, it usually ends with noisy logs and no clean way to turn the detail back off.

If you are looking for a built-in “GSLogger” in ServiceNow, the API you usually want is GSLog.

GSLog gives you two things that plain gs.info() does not:

  • log levels
  • control through a system property per caller

That makes it much easier to investigate an issue for one script without flooding the system log with everything else.

1. Create a logger

The basic pattern is simple:

var log = new GSLog("x_acme.order_sync.log", "OrderSync");

The two parameters matter:

  • the first parameter is the system property that controls the active logging level
  • the second parameter is the caller name that appears as the source in the logs

Pick a property name that is specific to the script or feature you are debugging. That way you can enable detailed logging for one area without affecting unrelated code.

2. Write messages at the right level

Once you have the logger, write messages with an appropriate severity.

var log = new GSLog("x_acme.order_sync.log", "OrderSync");
log.setLevel("debug");

log.logDebug("Starting sync for order " + current.getValue("number"));
log.logInfo("Validated outbound payload");
log.logNotice("Order sync completed");
log.logWarning("External system returned a partial response");
log.logErr("Order sync failed");

The useful levels to keep in mind are:

  • debug for temporary deep troubleshooting
  • info for extra operational detail
  • notice for normal high-signal events
  • warning for unusual but non-fatal conditions
  • err and crit for real failures

One detail that catches people: the default level is notice.

So if you only call logDebug() and never lower the active level, you may think the logger is broken when it is actually filtering exactly as designed.

3. Use a system property instead of hard-coding debug

setLevel("debug") is useful for a quick test, but the more practical pattern is letting the property drive the level.

For example, create a system property like this:

  • name: x_acme.order_sync.log
  • value during normal operation: notice
  • value during troubleshooting: debug

Then keep the script itself simple:

var log = new GSLog("x_acme.order_sync.log", "OrderSync");

log.logDebug("Raw response body: " + responseBody);
log.logNotice("Sync finished for " + current.getValue("number"));

With this approach, you do not have to edit the script every time you need more detail. You temporarily change the property, reproduce the issue, inspect the logs, and then move the property back to a quieter level.

That is the main reason GSLog scales better than random gs.info() statements left around the codebase.

4. Guard expensive debug statements with debugOn()

Sometimes the expensive part is not writing the log entry. It is building the message.

If the debug message needs a large JSON.stringify(), a long loop, or a big object inspection, check debugOn() first.

var log = new GSLog("x_acme.order_sync.log", "OrderSync");

if (log.debugOn()) {
  log.logDebug("Outbound payload: " + JSON.stringify(payload));
}

That pattern avoids doing unnecessary work when debug logging is disabled.

5. A practical Script Include pattern

This is a reasonable way to use GSLog inside reusable server-side code:

var UserSync = Class.create();
UserSync.prototype = {
  initialize: function () {
    this.log = new GSLog("x_acme.user_sync.log", "UserSync");
  },

  syncUser: function (userId) {
    this.log.logInfo("Starting sync for " + userId);

    var user = new GlideRecord("sys_user");
    if (!user.get(userId)) {
      this.log.logWarning("User not found: " + userId);
      return;
    }

    if (this.log.debugOn()) {
      this.log.logDebug("User email: " + user.getValue("email"));
    }

    // Integration logic here.

    this.log.logNotice("Finished sync for " + user.getValue("user_name"));
  },

  type: "UserSync"
};

This keeps the logging style consistent and makes the script easier to trace in the system log.

6. Where to view the logs

GSLog writes to the system logs, so that is where you should look first.

In the UI, navigate to:

  • System Logs > System Log

When you are trying to find one script quickly, these filters help:

  • filter Source by the caller name you passed, such as OrderSync
  • limit the time window to today or the last few minutes
  • filter by message text if you know part of the output

If you are generating script log statements during a test run, the script-related log views under System Logs can also help narrow the noise.

7. Common mistakes

These are the mistakes I see most often:

  • using gs.info() everywhere and losing control over log volume
  • using one generic property for many unrelated scripts
  • leaving a property at debug long after the investigation is over
  • building large debug strings without checking debugOn()
  • assuming logDebug() will appear even when the active level is still notice

The fix is usually simple: one logger per feature, one clear property name, and a deliberate logging level.

Final thought

Good debugging is not only about printing more information. It is about printing the right information and being able to turn it on and off safely.

That is where GSLog helps.

Use it when you want server-side logs that are easier to control, easier to search, and less likely to turn into permanent noise.