mirror of
https://github.com/jlengrand/Maestro.git
synced 2026-03-10 08:31:19 +00:00
feat(stats-db): add Sentry error reporting for database failures
Stats DB initialization failures were only logged locally, providing no visibility into production issues. This change adds Sentry reporting for: - Database corruption detection (integrity check failures) - Native module loading failures (architecture mismatches) - Database creation failures - General initialization failures Created reusable Sentry utilities in src/main/utils/sentry.ts that lazily load @sentry/electron to avoid module initialization issues.
This commit is contained in:
@@ -39,6 +39,7 @@ import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { app } from 'electron';
|
||||
import { logger } from './utils/logger';
|
||||
import { captureException, captureMessage } from './utils/sentry';
|
||||
import {
|
||||
QueryEvent,
|
||||
AutoRunSession,
|
||||
@@ -414,6 +415,13 @@ export class StatsDB {
|
||||
// This can happen if the native module fails to load
|
||||
const errorMessage = createError instanceof Error ? createError.message : String(createError);
|
||||
logger.error(`Failed to create database: ${errorMessage}`, LOG_CONTEXT);
|
||||
|
||||
// Report to Sentry
|
||||
void captureException(createError, {
|
||||
context: 'initialize:createNewDatabase',
|
||||
dbPath: this.dbPath,
|
||||
});
|
||||
|
||||
return {
|
||||
success: false,
|
||||
wasReset: false,
|
||||
@@ -451,6 +459,14 @@ export class StatsDB {
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
logger.error(`Failed to initialize stats database: ${errorMessage}`, LOG_CONTEXT);
|
||||
|
||||
// Report to Sentry
|
||||
void captureException(error, {
|
||||
context: 'initialize:outerCatch',
|
||||
dbPath: this.dbPath,
|
||||
wasReset,
|
||||
});
|
||||
|
||||
return {
|
||||
success: false,
|
||||
wasReset,
|
||||
@@ -1105,6 +1121,12 @@ export class StatsDB {
|
||||
const errors = result.map((row) => row.integrity_check);
|
||||
logger.error(`Database integrity check failed: ${errors.join(', ')}`, LOG_CONTEXT);
|
||||
|
||||
// Report corruption to Sentry for monitoring
|
||||
void captureMessage('Stats database corruption detected', 'error', {
|
||||
integrityErrors: errors,
|
||||
dbPath: this.dbPath,
|
||||
});
|
||||
|
||||
// Close before recovery
|
||||
db.close();
|
||||
} catch (error) {
|
||||
@@ -1112,6 +1134,14 @@ export class StatsDB {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
logger.error(`Failed to open database: ${errorMessage}`, LOG_CONTEXT);
|
||||
|
||||
// Report failure to Sentry
|
||||
void captureException(error, {
|
||||
context: 'openWithCorruptionHandling',
|
||||
dbPath: this.dbPath,
|
||||
isNativeModuleError:
|
||||
errorMessage.includes('dlopen') || errorMessage.includes('better_sqlite3.node'),
|
||||
});
|
||||
|
||||
// Check if this is a native module loading issue (not recoverable by reset)
|
||||
if (errorMessage.includes('dlopen') || errorMessage.includes('better_sqlite3.node')) {
|
||||
logger.error('Native SQLite module failed to load - cannot recover', LOG_CONTEXT);
|
||||
|
||||
74
src/main/utils/sentry.ts
Normal file
74
src/main/utils/sentry.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Sentry utilities for error reporting in the main process.
|
||||
*
|
||||
* These utilities lazily load Sentry to avoid module initialization issues
|
||||
* that can occur when importing @sentry/electron/main before app.whenReady().
|
||||
*/
|
||||
|
||||
import { logger } from './logger';
|
||||
|
||||
/** Sentry severity levels */
|
||||
export type SentrySeverityLevel = 'fatal' | 'error' | 'warning' | 'log' | 'info' | 'debug';
|
||||
|
||||
/** Sentry module type for crash reporting */
|
||||
interface SentryModule {
|
||||
captureMessage: (
|
||||
message: string,
|
||||
captureContext?: { level?: SentrySeverityLevel; extra?: Record<string, unknown> }
|
||||
) => string;
|
||||
captureException: (
|
||||
exception: Error | unknown,
|
||||
captureContext?: { level?: SentrySeverityLevel; extra?: Record<string, unknown> }
|
||||
) => string;
|
||||
}
|
||||
|
||||
/** Cached Sentry module reference */
|
||||
let sentryModule: SentryModule | null = null;
|
||||
|
||||
/**
|
||||
* Reports an exception to Sentry from the main process.
|
||||
* Lazily loads Sentry to avoid module initialization issues.
|
||||
*
|
||||
* @param error - The error to report
|
||||
* @param extra - Additional context data
|
||||
*/
|
||||
export async function captureException(
|
||||
error: Error | unknown,
|
||||
extra?: Record<string, unknown>
|
||||
): Promise<void> {
|
||||
try {
|
||||
if (!sentryModule) {
|
||||
const sentry = await import('@sentry/electron/main');
|
||||
sentryModule = sentry;
|
||||
}
|
||||
sentryModule.captureException(error, { extra });
|
||||
} catch {
|
||||
// Sentry not available (development mode or initialization failed)
|
||||
logger.debug('Sentry not available for exception reporting', '[Sentry]');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports a message to Sentry from the main process.
|
||||
* Lazily loads Sentry to avoid module initialization issues.
|
||||
*
|
||||
* @param message - The message to report
|
||||
* @param level - Severity level
|
||||
* @param extra - Additional context data
|
||||
*/
|
||||
export async function captureMessage(
|
||||
message: string,
|
||||
level: SentrySeverityLevel = 'error',
|
||||
extra?: Record<string, unknown>
|
||||
): Promise<void> {
|
||||
try {
|
||||
if (!sentryModule) {
|
||||
const sentry = await import('@sentry/electron/main');
|
||||
sentryModule = sentry;
|
||||
}
|
||||
sentryModule.captureMessage(message, { level, extra });
|
||||
} catch {
|
||||
// Sentry not available (development mode or initialization failed)
|
||||
logger.debug('Sentry not available for message reporting', '[Sentry]');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user