initialize static method

Future<void> initialize(
  1. BloomObservabilityConfig config
)

Initializes the observability pipeline and attaches automated error hooks.

Implementation

static Future<void> initialize(BloomObservabilityConfig config) async {

  if (_isInitialized) {
    await reset();
  }

  _config = config;
  _ringBuffer = BloomBreadcrumbRingBuffer(maxCapacity: config.maxBreadcrumbs);
  _isInitialized = true;

  if (!config.enabled) {
    logger.info('BloomObservability: Disabled by configuration.');
    return;
  }

  // 1. Hook Flutter widget error pipeline
  if (config.autoCaptureFlutterErrors) {
    _originalFlutterOnError = FlutterError.onError;
    _flutterHookInstalled = true;
    FlutterError.onError = (FlutterErrorDetails details) {
      captureException(
        details.exception,
        stackTrace: details.stack,
        context: {
          'library': details.library ?? 'flutter_framework',
          'context': details.context?.toDescription() ?? 'rendering',
          ..._config.tags,
        },
        level: BloomErrorLevel.error,
      );

      if (_originalFlutterOnError != null) {
        _originalFlutterOnError!(details);
      }
    };
  }

  // 2. Hook PlatformDispatcher asynchronous unhandled errors
  if (config.autoCaptureZoneErrors) {
    _originalPlatformOnError = PlatformDispatcher.instance.onError;
    _platformHookInstalled = true;
    PlatformDispatcher.instance.onError = (Object error, StackTrace stack) {
      captureException(
        error,
        stackTrace: stack,
        context: {
          'origin': 'platform_dispatcher_async',
          ..._config.tags,
        },
        level: BloomErrorLevel.fatal,
      );

      if (_originalPlatformOnError != null) {
        return _originalPlatformOnError!(error, stack);
      }
      return true;
    };
  }

  // 3. Native platform crash handler hook (Android/iOS)
  if (config.autoCaptureNativeCrashes && !kIsWeb) {
    try {
      await _nativeChannel.invokeMethod<void>('enableNativeCrashReporting');
      final pendingCrashes =
          await _nativeChannel.invokeListMethod<dynamic>('getPendingNativeCrashes');
      if (pendingCrashes != null && pendingCrashes.isNotEmpty) {
        for (final crash in pendingCrashes) {
          if (crash is Map) {
            await captureException(
              crash['message'] ?? 'Native Crash',
              exceptionType: crash['exceptionType']?.toString() ?? 'NativeCrash',
              stackTrace: crash['stackTrace']?.toString(),
              context: {
                'native': true,
                if (crash['signal'] != null) 'signal': crash['signal'],
                if (crash['context'] is Map)
                  ...Map<String, dynamic>.from(crash['context'] as Map),
                ..._config.tags,
              },
              level: BloomErrorLevel.fatal,
            );
          }
        }
        await _nativeChannel.invokeMethod<void>('clearPendingNativeCrashes');
      }
    } catch (e) {
      logger.debug('BloomObservability: Native crash bridge unhandled or stubbed: $e');
    }
  }

  logger.info(
      'BloomObservability: Initialized (Sampling: ${(config.sampleRate * 100).toInt()}%, Ring Buffer: ${config.maxBreadcrumbs})');
}