endHistogramTimer method

Future<void> endHistogramTimer(
  1. String key,
  2. String? fbl,
  3. String? operation,
  4. String? view,
  5. String? state, {
  6. int? timestamp,
})

End a histogram timer and generate metric event

Implementation

Future<void> endHistogramTimer(
  String key,
  String? fbl,
  String? operation,
  String? view,
  String? state, {
  int? timestamp,
}) async {
  final hub = _hub;
  if (!_isInitialized || hub == null) {
    ObslyLogger.warn('MetricsController not initialized');
    return;
  }

  // Check if metrics is enabled
  final config = ConfigController.instance.config;
  if (!(config?.enableMetrics ?? true)) {
    ObslyLogger.debug('Metrics disabled, ignoring endHistogramTimer call');
    return;
  }

  if (!_isTimerSessionValid()) {
    ObslyLogger.warn('Invalid session for histogram timer');
    return;
  }

  try {
    final timerKey = _createTimerKey(key, fbl, operation, view);

    // Check if timer exists - silently return if not found (normal for deeplinks, etc.)
    if (!_histogramTimers.containsKey(timerKey)) {
      return;
    }

    // Calculate duration
    final startTime = _histogramTimers.remove(timerKey)!;
    final endTime = timestamp ?? DateTime.now().millisecondsSinceEpoch;
    final durationMs = endTime - startTime;

    // Calculate le (bucket) using UX-appropriate millisecond-based thresholds
    final buckets = [100.0, 300.0, 500.0, 1000.0, 3000.0, 5000.0, 30000.0]; // UX-appropriate buckets + 30s catch-all
    double? le;
    for (final bucket in buckets) {
      if (durationMs <= bucket) {
        le = bucket;
        break;
      }
    }

    final currentView =
        NavigationIntegrationV2.isNavigationAvailable ? NavigationIntegrationV2.getCurrentViewName() : view;

    final dimensions = Dimension(
      fbl: (fbl?.isNotEmpty ?? false) ? fbl! : '', // Pass empty if not provided, Hub will use session fallback
      operation: (operation?.isNotEmpty ?? false)
          ? operation!
          : '', // Pass empty if not provided, Hub will use session fallback
      view: (currentView?.isNotEmpty ?? false)
          ? currentView!
          : '', // Pass empty if not provided, Hub will use session fallback
      state: state ?? '',
      app: '', // Will be filled by Hub with app info
      platform: '', // Will be filled by Hub with app info
      version: '', // Will be filled by Hub with app info
      le: le,
    );

    final event = MetricEventBase(
      key: key,
      value: 1, // Count = 1 (similar to JavaScript implementation)
      dimensions: dimensions,
      metricType: MetricType.histogram.value,
      duration: durationMs, // Send calculated duration
    );

    final reservation = hub.reserveEventMetadata();
    hub.captureEvent(event, reservation);

    ObslyLogger.verbose('📊 Histogram timer ended: $timerKey (${durationMs}s, le: $le)');
  } catch (e) {
    ObslyLogger.error('Error ending histogram timer: $e');
  }
}