> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.itential.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.itential.com/_mcp/server.

# Logging best practices

> Developer guidelines for secure and effective logging

Follow these guidelines when implementing logging in Itential Platform adapters, applications, and integrations.

## Before you begin

* Understand log formats and severity levels
* Know your Platform version (affects logging signature requirements)
* Review your organization's data security policies

## Itential's logging security policy

Itential Platform automatically excludes Authentication, Authorization, and Accounting (AAA) sensitive data from logs, including Platform passwords, API keys, tokens, and credentials.

**However, Itential does not modify third-party system payloads.**

### Third-party payload data

Platform does not filter data from external systems:

* API responses from external systems (ServiceNow, Ansible, Splunk)
* Device configurations from network equipment (Cisco, Juniper, Arista)
* Authentication tokens from external auth providers (Okta, Active Directory)
* Data returned from monitoring tools, ticketing systems, and cloud providers

### Your responsibility

**You must filter sensitive data from third-party payloads before logging.**

Implement appropriate filtering in your workflows and custom code according to your organization's security policies.

Always create context objects containing only known-safe fields. Never log entire objects that may contain sensitive data.

## Use structured logging

**Requirements:** Platform 2023.2 or 6.2+

Use the structured logging signature with a single object containing `message`, `context`, and `error` fields.

**Do this:**

```javascript
log.info({
  message: 'User authentication successful',
  context: {
    userId: user.id,
    username: user.username,
    method: 'ldap'
  }
});
```

**Not this:**

```javascript
// Multi-argument format (legacy)
log.info('User authentication successful', user);
```

Structured logging enables automated parsing and prevents accidental exposure of sensitive data.

### Migrate to structured logging

**Requirements:** Platform 2023.2 or 6.2+

Update existing multi-argument log calls to structured logging format.

**Before migration:**

```javascript
log.warn('Service connection timed out', 'service-A', 5000);
```

**After migration:**

```javascript
log.warn({
  message: 'Service connection timed out',
  context: {
    serviceName: 'service-A',
    timeoutMs: 5000
  },
  error: err
});
```

**Migration steps:**

### Set logger version

Set `loggerVersion` to `2` in your application's **pronghorn.json** file.

### Identify primary message

Find the main message (typically first argument).

### Create context object

Create `context` object with named fields for additional data.

### Verify no sensitive data

Ensure context contains only known-safe fields.

### Add error object

Include `error` object for error and warn levels.

## Choose the correct log level

Select appropriate severity level based on event type and required response.

| Level    | When to use                                         | Example                                                      |
| -------- | --------------------------------------------------- | ------------------------------------------------------------ |
| `system` | Critical platform lifecycle events only             | Platform initialization complete, shutting down              |
| `error`  | Unrecoverable errors requiring immediate attention  | Database connection lost, authentication service unavailable |
| `warn`   | Recoverable issues requiring monitoring             | API retry in progress, deprecated feature used               |
| `info`   | Normal operational messages                         | User logged in, workflow completed, service started          |
| `debug`  | Detailed diagnostic information for troubleshooting | Configuration loaded, API response received                  |
| `trace`  | Step-by-step execution flow                         | Function entry/exit, variable values                         |
| `spam`   | Extremely frequent events                           | Loop iterations, polling events                              |

### Best practices for log levels

* Use `info` for events you'd want in production dashboards
* Use `debug` only during active troubleshooting
* Reserve `error` for situations requiring human intervention
* Always include `error` objects with `error` and `warn` levels
* Never use `trace` or `spam` in production

## Write clear messages

Write log messages that explain what happened and provide troubleshooting context.

**Do this:**

```javascript
log.error({
  message: 'Failed to update user profile in database',
  context: {
    userId: 'user123',
    operation: 'updateEmail'
  },
  error: err
});
```

**Not this:**

```javascript
log.error({ message: 'Update failed', error: err });
```

### Message guidelines

* State what operation was attempted
* Include relevant identifiers (user ID, resource ID)
* Explain the outcome (success, failure, retry)
* Provide context for troubleshooting
* Avoid generic messages like "Error occurred" or "Processing complete"

## Filter sensitive data

Always create context objects containing only known-safe fields.

### Create safe context objects

Extract only the fields you need and explicitly define them.

**Do this:**

```javascript
const logContext = {
  userId: user.id,
  username: user.username,
  operation: 'updateProfile'
};

log.info({
  message: 'User profile updated successfully',
  context: logContext
});
```

**Not this:**

```javascript
// Entire user object may contain email, address, or other PII
log.info({
  message: 'User profile updated successfully',
  context: user  // NEVER DO THIS
});
```

### Filter third-party payloads

Extract only metadata from third-party responses. Never log full response bodies.

**Do this:**

```javascript
log.debug({
  message: 'ServiceNow API response received',
  context: {
    endpoint: '/api/now/table/incident',
    statusCode: response.status,
    recordCount: response.data.result.length
  }
});
```

**Not this:**

```javascript
// response.data may contain PII or sensitive content
log.debug({
  message: 'ServiceNow API response received',
  context: {
    endpoint: '/api/now/table/incident',
    responseData: response.data  // NEVER DO THIS
  }
});
```

### Sensitive data categories

**Platform data (handled by Itential):**

* Platform passwords, API keys, tokens, secrets
* Platform authorization headers, cookies, sessions
* Platform JWT tokens, OAuth credentials

**Third-party data (your responsibility):**

* API response payloads from external systems
* Device configurations from network equipment
* Credentials or tokens in third-party responses
* Customer data from external integrations

**Personally Identifiable Information:**

* Email addresses, phone numbers, full names
* Social security numbers, addresses
* IP addresses (context-dependent)

**Business & customer data:**

* Customer-specific business data
* Proprietary configurations
* Sensitive content from third-party systems

## Log useful context

Include relevant context that helps diagnose issues without exposing sensitive data.

### API calls

Log metadata about API requests and responses:

```javascript
log.info({
  message: 'Outbound API request',
  context: {
    service: 'ServiceNow',
    endpoint: '/api/now/table/incident',
    method: 'POST',
    correlationId: req.headers['x-correlation-id']
  }
});
```

**Include:**

* HTTP method, endpoint (without sensitive query parameters)
* Status codes, response times
* Correlation IDs, operation names
* Record counts

**Exclude:**

* Request/response bodies
* Authorization headers
* API keys in URLs
* Full third-party payloads

### Database operations

Log query metadata and execution details:

```javascript
log.debug({
  message: 'Database query executed',
  context: {
    collection: 'users',
    operation: 'findOne',
    query: { _id: userId },
    resultFound: !!result,
    duration: queryDuration
  }
});
```

**Include:**

* Collection/table name
* Operation type
* Query parameters (if not sensitive)
* Success/failure status
* Execution time

**Exclude:**

* Full result sets
* Connection strings with credentials
* Entire documents with PII

### Workflows

Log workflow execution details:

```javascript
log.info({
  message: 'Workflow execution started',
  context: {
    workflowName: 'deploy_configuration',
    workflowId: workflow.id,
    triggeredBy: user.username,
    targetDevices: deviceCount
  }
});
```

**Include:**

* Workflow name and ID
* Initiating user (username only)
* Count of affected resources
* Execution stage

**Exclude:**

* Complete workflow payloads
* Device configurations
* Credential data
* Full third-party responses

### Authentication

Log authentication events for security auditing:

```javascript
log.info({
  message: 'User authentication successful',
  context: {
    userId: user.id,
    username: user.username,
    authMethod: 'ldap',
    sourceIp: req.ip
  }
});
```

**Include:**

* User identifier
* Authentication method
* Success/failure status
* Source IP (if relevant)

**Exclude:**

* Passwords, credentials
* Full authentication payloads
* Session tokens

### Third-party integrations

Log integration operation metadata:

```javascript
log.debug({
  message: 'Device configuration retrieved',
  context: {
    deviceId: device.id,
    deviceType: device.type,
    configSize: config.length,
    retrievalTime: duration
  }
});
```

**Include:**

* Metadata about the operation
* Device ID, data size, timing
* Success/failure

**Exclude:**

* Full configurations
* Credentials
* Proprietary settings
* Customer data

## Handle errors

Always include error objects with `error` and `warn` level logs.

```javascript
try {
  await database.updateUser(userId, updates);
} catch (err) {
  log.error({
    message: 'Failed to update user in database',
    context: {
      userId: userId,
      operation: 'updateUser',
      attemptedUpdates: Object.keys(updates)
    },
    error: err
  });
  throw err;
}
```

### Error handling best practices

* Explain what the code was trying to do when it failed
* Include relevant identifiers (user ID, resource ID)
* Include the error object for stack traces
* Don't log the same error multiple times as it propagates
* Use appropriate log level (error vs warn)

## Use appropriate log frequency

Log summaries of batch operations rather than individual iterations.

**Do this:**

```javascript
log.info({
  message: 'Processed device configurations',
  context: {
    deviceCount: devices.length,
    successCount: results.filter(r => r.success).length,
    failureCount: results.filter(r => !r.success).length,
    duration: processingTime
  }
});
```

**Not this:**

```javascript
devices.forEach(device => {
  log.debug({ 
    message: 'Processing device', 
    context: { deviceId: device.id }
  });
  processDevice(device);
});
```

### Frequency best practices

* Log summaries of batch operations, not individual iterations
* Use `trace` or `spam` for high-frequency events during development
* Remove or reduce verbosity in production code
* Consider performance impact of excessive logging

## Common logging patterns

### Successful operation

```javascript
log.info({
  message: 'Configuration deployed successfully',
  context: {
    workflowId: workflow.id,
    deviceCount: 5,
    duration: executionTime
  }
});
```

### Recoverable warning

```javascript
log.warn({
  message: 'API request retry scheduled after timeout',
  context: {
    service: 'ServiceNow',
    endpoint: '/api/now/table/incident',
    retryAttempt: 2,
    maxRetries: 3,
    retryDelay: 5000
  },
  error: timeoutError
});
```

### Unrecoverable error

```javascript
log.error({
  message: 'Database connection pool exhausted',
  context: {
    poolSize: config.db.poolSize,
    activeConnections: pool.activeCount,
    waitingRequests: pool.waitingCount
  },
  error: poolError
});
```

### Debug information

```javascript
log.debug({
  message: 'External API response received',
  context: {
    service: 'ServiceNow',
    endpoint: '/api/now/table/incident',
    statusCode: response.status,
    responseTime: responseTime,
    recordCount: response.data.result.length
  }
});
```

### Third-party payload filtering

**Do this - Log metadata only:**

```javascript
log.debug({
  message: 'Network device configuration backup completed',
  context: {
    deviceId: device.id,
    deviceType: 'cisco_ios',
    configLines: configData.split('\n').length,
    backupSize: Buffer.byteLength(configData),
    duration: backupTime
  }
});
```

**Not this - Logging full configuration:**

```javascript
log.debug({
  message: 'Network device configuration backup completed',
  context: {
    deviceId: device.id,
    configData: configData  // MAY CONTAIN PASSWORDS AND SENSITIVE SETTINGS
  }
});
```

## Automated validation

**Requirements:** Platform 2023.2 or 6.2+

Platform automatically validates logging code during development.

**Validation checks:**

* All log calls pass a single object as argument
* Object contains a `message` property
* `context` property (if present) is an object
* `log.error` and `log.warn` calls include an `error` property

Code violating these rules triggers ESLint warnings.

**Valid:**

```javascript
log.info({ message: 'Operation completed' });

log.error({
  message: 'Operation failed',
  context: { userId: '123' },
  error: err
});
```

**Invalid:**

```javascript
log.info('Operation completed');  // Not an object

log.error({ message: 'Failed' });  // Missing error property

log.info({ context: { userId: '123' }});  // Missing message
```

## Multi-argument logging (Platform 2023.1 and earlier)

For Platform versions before 2023.2, use multi-argument logging signature.

Pass only safe, specific values as arguments:

```javascript
log.info('User authentication successful', userId, authMethod);
```

**Never pass entire objects:**

```javascript
// Don't pass entire objects - may contain sensitive data
log.info('User authenticated', user);  // WRONG
log.debug('Request received', req);  // WRONG
log.debug('API response', apiResponse);  // WRONG
```

### Legacy logging best practices

* Only pass specific, known-safe values as arguments
* Avoid passing request, response, user, or configuration objects
* Manually extract safe fields before logging
* Filter third-party payloads before logging any data

## Performance considerations

### Logging overhead

* Console logging has higher performance impact than file logging
* Structured JSON logging is more efficient than standard format
* Excessive logging in hot paths impacts performance
* Log level affects volume and performance

### Production recommendations

* Use `info` or `warn` log level
* Disable `debug` logging
* Never use `trace` or `spam`
* Log summaries of batch operations
* Monitor log volume and performance impact

## Next steps

#### [Logging overview](/itential-platform/monitor/log/overview)

Understand log formats and levels

#### [Configure logging](/itential-platform/monitor/log/configure)

Set log levels and rotation settings

#### [View and search logs](/itential-platform/monitor/log/view-and-search)

Access and search log files

#### [Troubleshoot logging](/itential-platform/monitor/log/troubleshoot)

Resolve common logging issues