Skip to content

Debugging

ReActive Record provides a comprehensive logging system that helps you debug your application by tracking database operations, model changes, and relationship updates.

Logger Configuration

The logger can be configured in two ways during database initialization:

  1. Level-specific logging through initial.loggers
  2. Severity-based subscriptions through initial.subscriptions
typescript
import { ReactiveDatabase } from '@nhtio/web-re-active-record'

const db = new ReactiveDatabase({
  // ... other config options
  initial: {
    // Level-specific loggers
    loggers: {
      error: [
        (...args) => console.error(...args)
      ],
      info: [
        (...args) => console.info(...args)
      ]
    },
    // Severity-based subscriptions
    // Will receive all logs at or above the specified level
    subscriptions: [
      // Subscribe to error and all more severe levels (emerg, alert, crit)
      ['error', (msg) => console.log(msg)],
      
      // Subscribe to warning and all more severe levels
      ['warning', (msg) => errorTrackingService.capture(msg)]
    ]
  }
})

Level-Specific Logging

Use loggers when you want handlers for specific log levels:

typescript
{
  loggers: {
      error: [(...args) => handleError(...args)],
      info: [(...args) => logInfo(...args)],
      debug: [(...args) => console.debug(...args)]
  }
}

Severity-Based Subscriptions

Use subscriptions when you want handlers for all logs at or above a certain severity level:

typescript
{
  subscriptions: [
    // Will receive emerg, alert, crit, and error logs
    ['error', (...args) => handleSevereIssues(...args)],
    
    // Will receive all logs from warning up to emerg
    ['warning', (...args) => trackIssues(...args)]
  ]
}

You can then use the logger accessor on the database instance to make changes to the logger's configuration during the runtime of your application.

typescript
db.logger.off('info', someCallback)
db.logger.once('debug', someDebugCallback)
db.logger.subscribe('error', someCallbackForAllLogLevelsOverError)

For more information about the logger, see the logger api documentation.

Log Events

ReActive Record provides eight independent log events that you can subscribe to:

EventNumeric ValueDescriptionExample Use Case
emerg0System is unusableCritical database corruption
alert1Action must be taken immediatelySevere data integrity issues
crit2Critical conditionsFailed relationship synchronization
error3Error conditionsQuery failures, invalid operations
warning4Warning conditionsDeprecated usage, performance issues
notice5Normal but significant conditionSchema changes, version updates
info6Informational messagesSuccessful operations, sync events
debug7Debug-level messagesDetailed operation tracking

Each log event operates independently. You can subscribe to any combination of events based on your needs. There is no hierarchical relationship between events - subscribing to one event does not automatically subscribe you to others.

Understanding the Levels

The log levels are inspired by syslog severity levels and is meant to represent the importance of the message in relation to your applications usage of the database. The general rule is that the lower the numeric value, the more severe the message.

Accessing the Logger

You can access the logger instance through the database object:

typescript
const db = new ReactiveDatabase(config)
const logger = db.logger

Subscribing to Log Events

Subscribe to specific log events after initialization:

typescript
// Subscribe to multiple events independently
db.logger
  .on('error', (...args) => {
    // Handle error events
    errorTrackingService.capture(...args)
  })
  .on('info', (...args) => {
    // Handle info events
    customLogger.info(...args)
  })
  .on('debug', (...args) => {
    // Handle debug events
    console.debug('[ReActive Record]:', ...args)
  })

// Events are independent - you can subscribe to any combination
db.logger
  .on('emerg', emergencyHandler)
  .on('crit', criticalHandler)
  // You don't need to subscribe to all events
  // .on('alert', alertHandler)
  // .on('error', errorHandler)
  .on('warning', warningHandler)

Unsubscribing from Log Events

Remove specific handlers or all handlers for an event:

typescript
// Remove a specific handler from an event
db.logger.off('error', specificHandler)

// Remove all handlers for an event
db.logger.off('debug')

// You can unsubscribe from events independently
db.logger
  .off('emerg')
  .off('error', errorHandler)
  .off('warning')

One-Time Event Handlers

Register handlers that will be called only once for a specific event:

typescript
// Listen for the next occurrence of specific events
db.logger
  .once('error', (...args) => {
    console.error('First error encountered:', ...args)
  })
  .once('info', (...args) => {
    console.info('First info received:', ...args)
  })

// Each event's one-time handler operates independently

Error Handling

ReActive Record includes a separate error handler for managing asynchronous errors:

typescript
// Register an error handler
db.errorHandler.on((error: Error) => {
  // Handle async errors
  console.error('Async Error:', error)
})

// Remove error handler
db.errorHandler.off(handler)

If no error handlers are registered, errors will be thrown normally. When handlers are registered, errors are "swallowed" and passed to the handlers instead.

Debug Level Logging

The debug log level is the most verbose level of logging and is typically used for detailed operation tracking during development.

Configure debug level logging by adding handlers to the debug log level:

typescript
const db = new ReactiveDatabase({
  // ... other config
  initial: {
    loggers: {
      debug: [
        (...args) => {
          console.group('[ReActive Record Debug]')
          console.debug(...args)
          console.groupEnd()
        }
      ]
    }
  }
})

What Gets Logged

Here's what each log level typically includes:

Debug Level

  • Model instantiation
  • Query execution
  • Relationship loading
  • Cache operations
  • State changes
  • Sync events
typescript
db.logger.on('debug', console.debug)
const user = await db.model('users').find(1)
// Debug: Finding user with id 1
// Debug: Loading user relationships
// Debug: User loaded successfully

Info Level

  • Successful operations
  • Model saves
  • Relationship updates
  • Cross-tab synchronization
typescript
db.logger.on('info', console.info)
await user.save()
// Info: User 1 saved successfully
// Info: Changes synchronized across tabs

Warning Level

  • Deprecated feature usage
  • Performance concerns
  • Non-critical issues
typescript
db.logger.on('warning', console.warn)
// Warning: Large result set detected (>1000 records)

Error Level

  • Query failures
  • Validation errors
  • Relationship errors
typescript
db.logger.on('error', console.error)
// Error: Failed to save user - validation error

Production Logging

For production environments, it's recommended to:

  1. Disable debug logging
  2. Only log errors and critical issues
  3. Use structured logging for better analysis
typescript
const db = new ReactiveDatabase({
  // ... other config
  initial: {
    loggers: {
      error: [(msg) => {
        // Log to service
        errorLoggingService.log(msg)
      }],
      crit: [(msg) => {
        // Alert on critical issues
        alertingService.notify(msg)
      }]
    }
  }
})