IMetricsCollector

Interface for collecting metrics during migration execution.

Table of Contents

  1. Overview
  2. Interface Definition
  3. Methods
    1. recordMigrationStart
    2. recordMigrationComplete
    3. recordScriptStart
    4. recordScriptComplete
    5. recordScriptError
    6. recordRollback
    7. recordValidationErrors
    8. recordBackup
    9. recordError
    10. close
  4. Usage
    1. Basic Implementation
    2. With Dependency Injection
    3. Multiple Collectors
  5. Built-in Implementations
    1. ConsoleMetricsCollector
    2. LoggerMetricsCollector
    3. JsonMetricsCollector
    4. CsvMetricsCollector
  6. Custom Implementation
    1. Example: Datadog Collector
  7. Best Practices
    1. 1. Implement Only What You Need
    2. 2. Handle Errors Gracefully
    3. 3. Use Async When Needed
    4. 4. Clean Up Resources

Overview

IMetricsCollector defines the contract for metrics collection implementations. Collectors receive lifecycle events during migration execution and can send metrics to various destinations (console, files, monitoring services, etc.).

Added in: v0.6.0

Use cases:

  • Track migration performance
  • Monitor execution times in production
  • Debug slow migrations
  • Send metrics to APM tools (Datadog, CloudWatch, Prometheus)
  • Create audit trails

Interface Definition

interface IMetricsCollector {
  recordMigrationStart?(context: IMigrationContext): void | Promise<void>;
  recordMigrationComplete?(result: IMigrationResult, duration: number): void | Promise<void>;
  recordScriptStart?(script: MigrationScript): void | Promise<void>;
  recordScriptComplete?(script: MigrationScript, duration: number): void | Promise<void>;
  recordScriptError?(script: MigrationScript, error: Error): void | Promise<void>;
  recordRollback?(strategy: RollbackStrategy, success: boolean, duration?: number): void | Promise<void>;
  recordValidationErrors?(errors: ValidationError[]): void | Promise<void>;
  recordBackup?(backupPath: string, duration: number): void | Promise<void>;
  recordError?(error: Error): void | Promise<void>;
  close?(): void | Promise<void>;
}

All methods are optional - implement only what you need.


Methods

recordMigrationStart

Called when migration process begins.

recordMigrationStart?(context: IMigrationContext): void | Promise<void>

Parameters:

  • context: IMigrationContext - Migration context with pending and executed counts

Example:

recordMigrationStart(context: IMigrationContext): void {
  console.log(`[METRICS] Migration started - ${context.pending} pending scripts`);
}

recordMigrationComplete

Called when migration process completes (success or failure).

recordMigrationComplete?(result: IMigrationResult, duration: number): void | Promise<void>

Parameters:

  • result: IMigrationResult - Migration result with success status and executed scripts
  • duration: number - Total execution time in milliseconds

Example:

recordMigrationComplete(result: IMigrationResult, duration: number): void {
  if (result.success) {
    console.log(`[METRICS] Migration completed - ${result.executed.length} scripts in ${duration}ms`);
  } else {
    console.error(`[METRICS] Migration failed after ${duration}ms`);
  }
}

recordScriptStart

Called when individual migration script starts executing.

recordScriptStart?(script: MigrationScript): void | Promise<void>

Parameters:

  • script: MigrationScript - Migration script being executed

Example:

recordScriptStart(script: MigrationScript): void {
  console.log(`[METRICS] ${script.name} started`);
}

recordScriptComplete

Called when individual migration script completes successfully.

recordScriptComplete?(script: MigrationScript, duration: number): void | Promise<void>

Parameters:

  • script: MigrationScript - Migration script that completed
  • duration: number - Execution time in milliseconds

Example:

recordScriptComplete(script: MigrationScript, duration: number): void {
  console.log(`[METRICS] ${script.name} completed in ${duration}ms`);
}

recordScriptError

Called when individual migration script fails.

recordScriptError?(script: MigrationScript, error: Error): void | Promise<void>

Parameters:

  • script: MigrationScript - Migration script that failed
  • error: Error - Error that caused the failure

Example:

recordScriptError(script: MigrationScript, error: Error): void {
  console.error(`[METRICS] ${script.name} failed: ${error.message}`);
}

recordRollback

Called when rollback is attempted.

recordRollback?(strategy: RollbackStrategy, success: boolean, duration?: number): void | Promise<void>

Parameters:

  • strategy: RollbackStrategy - Rollback strategy used (BACKUP, DOWN, BOTH, NONE)
  • success: boolean - Whether rollback succeeded
  • duration?: number - Optional rollback duration in milliseconds

Example:

recordRollback(strategy: RollbackStrategy, success: boolean, duration?: number): void {
  const status = success ? 'succeeded' : 'failed';
  console.log(`[METRICS] Rollback (${strategy}) ${status}${duration ? ` in ${duration}ms` : ''}`);
}

recordValidationErrors

Called when validation errors are found before migration execution.

recordValidationErrors?(errors: ValidationError[]): void | Promise<void>

Parameters:

  • errors: ValidationError[] - Array of validation errors

Example:

recordValidationErrors(errors: ValidationError[]): void {
  console.warn(`[METRICS] Validation errors: ${errors.length} issues found`);
  errors.forEach(err => console.warn(`[METRICS]   - ${err.message}`));
}

recordBackup

Called when database backup is created.

recordBackup?(backupPath: string, duration: number): void | Promise<void>

Parameters:

  • backupPath: string - Path or identifier of created backup
  • duration: number - Backup creation time in milliseconds

Example:

recordBackup(backupPath: string, duration: number): void {
  console.log(`[METRICS] Backup created in ${duration}ms: ${backupPath}`);
}

recordError

Called when general error occurs (not migration script specific).

recordError?(error: Error): void | Promise<void>

Parameters:

  • error: Error - Error that occurred

Example:

recordError(error: Error): void {
  console.error(`[METRICS] Error: ${error.message}`);
}

close

Called when metrics collector should clean up resources (e.g., close file handles, flush buffers).

close?(): void | Promise<void>

Example:

async close(): Promise<void> {
  await this.fileHandle.close();
  console.log('[METRICS] Collector closed');
}

Usage

Basic Implementation

import { IMetricsCollector, MigrationScript, IMigrationResult } from '@migration-script-runner/core';

class SimpleMetricsCollector implements IMetricsCollector {
  recordScriptComplete(script: MigrationScript, duration: number): void {
    console.log(`✓ ${script.name} completed in ${duration}ms`);
  }

  recordScriptError(script: MigrationScript, error: Error): void {
    console.error(`✗ ${script.name} failed: ${error.message}`);
  }
}

With Dependency Injection

import { MigrationScriptExecutor } from '@migration-script-runner/core';

const executor = new MigrationScriptExecutor({
  handler,
  metricsCollectors: [
    new SimpleMetricsCollector()
  ]
}, config);

await executor.up();

Multiple Collectors

const executor = new MigrationScriptExecutor({
  handler,
  metricsCollectors: [
    new ConsoleMetricsCollector(),      // Real-time feedback
    new JsonMetricsCollector({          // Detailed analysis
      filePath: './metrics/migration.json'
    }),
    new DatadogCollector({              // Production monitoring
      apiKey: process.env.DD_API_KEY
    })
  ]
}, config);

Built-in Implementations

MSR includes four production-ready collectors:

ConsoleMetricsCollector

Zero-config console output for development.

import { ConsoleMetricsCollector } from '@migration-script-runner/core';

new ConsoleMetricsCollector()

View ConsoleMetricsCollector docs →


LoggerMetricsCollector

Send metrics through any ILogger implementation.

import { LoggerMetricsCollector, FileLogger } from '@migration-script-runner/core';

new LoggerMetricsCollector({
  logger: new FileLogger('./logs/metrics.log')
})

View LoggerMetricsCollector docs →


JsonMetricsCollector

Write detailed JSON metrics for analysis.

import { JsonMetricsCollector } from '@migration-script-runner/core';

new JsonMetricsCollector({
  filePath: './metrics/migration.json',
  pretty: true
})

View JsonMetricsCollector docs →


CsvMetricsCollector

CSV format for Excel and data analysis.

import { CsvMetricsCollector } from '@migration-script-runner/core';

new CsvMetricsCollector({
  filePath: './metrics/migrations.csv'
})

View CsvMetricsCollector docs →


Custom Implementation

Example: Datadog Collector

import { IMetricsCollector, MigrationScript, IMigrationResult } from '@migration-script-runner/core';
import StatsD from 'hot-shots';

export class DatadogCollector implements IMetricsCollector {
  private client: StatsD;

  constructor(config: { apiKey: string; host?: string; prefix?: string }) {
    this.client = new StatsD({
      host: config.host || 'localhost',
      port: 8125,
      prefix: config.prefix || 'msr.',
      globalTags: {
        env: process.env.NODE_ENV || 'production'
      }
    });
  }

  recordScriptComplete(script: MigrationScript, duration: number): void {
    this.client.increment('migrations.success', 1, { script: script.name });
    this.client.timing('migrations.duration', duration, { script: script.name });
  }

  recordScriptError(script: MigrationScript, error: Error): void {
    this.client.increment('migrations.failed', 1, {
      script: script.name,
      error: error.constructor.name
    });
  }

  async close(): Promise<void> {
    this.client.close();
  }
}

View more custom collector examples →


Best Practices

1. Implement Only What You Need

All methods are optional - implement only the metrics relevant to your use case:

// Minimal implementation - only track failures
class ErrorOnlyCollector implements IMetricsCollector {
  recordScriptError(script: MigrationScript, error: Error): void {
    // Send alert
  }
}

2. Handle Errors Gracefully

Metrics collection failures should never stop migrations:

recordScriptComplete(script: MigrationScript, duration: number): void {
  try {
    this.sendMetric(script, duration);
  } catch (error) {
    console.error('Metrics error:', error);
    // Don't throw - continue with migration
  }
}

3. Use Async When Needed

Return Promise<void> for async operations:

async recordScriptComplete(script: MigrationScript, duration: number): Promise<void> {
  await this.apiClient.send({
    metric: 'migration.duration',
    value: duration
  });
}

4. Clean Up Resources

Implement close() to flush buffers and close connections:

async close(): Promise<void> {
  await this.flush();
  await this.fileHandle.close();
}

← Back to Interfaces