← Script library

Business Rules

Audit Trail and Change Logging

Create detailed audit trail records when sensitive fields are modified, maintaining compliance and tracking changes.

JavaScript
(function executeRule(current, previous /*null when async*/) {

  // Configuration: Define fields to audit
  var auditedFields = [
    'priority',
    'state',
    'assignment_group',
    'assigned_to',
    'close_code',
    'resolved_by'
  ];

  // Configuration: Define critical tables that require full auditing
  var criticalTables = ['incident', 'change_request', 'problem'];

  // Check if any audited fields changed
  var changedFields = [];
  auditedFields.forEach(function(fieldName) {
    if (current[fieldName].changes()) {
      changedFields.push(fieldName);
    }
  });

  // Only proceed if there are changes to audit
  if (changedFields.length === 0) {
    return;
  }

  // Create audit records for each changed field
  changedFields.forEach(function(fieldName) {
    var grAudit = new GlideRecord('u_audit_trail');  // Create this table for audit logging
    grAudit.initialize();

    // Record metadata
    grAudit.u_table_name = current.getTableName();
    grAudit.u_record_id = current.sys_id.toString();
    grAudit.u_record_number = current.number.toString();
    grAudit.u_field_name = fieldName;

    // Get field label
    var fieldLabel = current.getElement(fieldName).getLabel();
    grAudit.u_field_label = fieldLabel;

    // Record old and new values
    var oldValue = previous ? previous.getValue(fieldName) : '';
    var newValue = current.getValue(fieldName);

    grAudit.u_old_value = oldValue;
    grAudit.u_new_value = newValue;

    // Get display values for reference fields
    if (current.getElement(fieldName).getED().isReference()) {
      grAudit.u_old_display_value = previous ? previous[fieldName].getDisplayValue() : '';
      grAudit.u_new_display_value = current[fieldName].getDisplayValue();
    }

    // Record who made the change
    grAudit.u_changed_by = gs.getUserID();
    grAudit.u_changed_at = new GlideDateTime();

    // Record IP address and session info
    grAudit.u_ip_address = gs.getSession().getClientIP();
    grAudit.u_session_id = gs.getSession().getSessionToken();

    // Categorize the change type
    if (fieldName === 'state') {
      grAudit.u_change_type = 'Status Change';
    } else if (fieldName === 'assignment_group' || fieldName === 'assigned_to') {
      grAudit.u_change_type = 'Assignment Change';
    } else if (fieldName === 'priority') {
      grAudit.u_change_type = 'Priority Change';
    } else {
      grAudit.u_change_type = 'Field Update';
    }

    // Add business context
    grAudit.u_reason = current.work_notes.toString() || 'No reason provided';

    // Insert audit record
    var auditId = grAudit.insert();

    if (auditId) {
      gs.info('Audit trail created for ' + current.number + ', field: ' + fieldLabel +
              ', changed by: ' + gs.getUserName());
    }
  });

  // Optional: Send alert for critical changes
  if (changedFields.indexOf('priority') !== -1) {
    var oldPriority = previous ? previous.priority.toString() : '';
    var newPriority = current.priority.toString();

    // Alert if priority increased to Critical
    if (newPriority === '1' && oldPriority !== '1') {
      gs.eventQueue('audit.critical_priority_change', current, gs.getUserID(), current.sys_id);
      gs.warn('AUDIT: Priority changed to Critical for ' + current.number +
              ' by ' + gs.getUserName());
    }
  }

  // Optional: Add audit summary to work notes
  var auditSummary = 'AUDIT: Fields changed - ' + changedFields.join(', ') +
                     ' | Changed by: ' + gs.getUserName() +
                     ' | Time: ' + new GlideDateTime().getDisplayValue();
  current.comments = auditSummary;

})(current, previous);

How to use it

1. Create a custom table u_audit_trail with fields: u_table_name, u_record_id, u_record_number, u_field_name, u_field_label, u_old_value, u_new_value, u_old_display_value, u_new_display_value, u_changed_by, u_changed_at, u_ip_address, u_session_id, u_change_type, u_reason 2. Create an after Business Rule on tables you want to audit 3. Check "Update" checkbox only 4. Customize auditedFields array for your requirements 5. Consider data retention policies for audit records 6. Test thoroughly to ensure no performance impact

Adapt the table names, fields, and conditions to your instance. Test the behavior in a development environment before using it in production.