atlet99/dtorrent_tracker_v2

[FEATURE] - Add tracker statistics and metrics;

开放

#9 创建于 2025年11月3日

 (0 条评论) (0 个反应) (0 位负责人)Dart (0 个派生)auto 404
enhancementhelp wanted

仓库指标

星标
 (0 个星标)
PR 合并指标
 (30 天内没有已合并 PR)

描述

Description

There is currently no way to track statistics about tracker operations, such as:

  • Number of successful/unsuccessful requests
  • Response times
  • Error rates
  • Request counts per tracker

This would be valuable for monitoring, debugging, and optimizing tracker usage.

Location

File: lib/src/tracker/tracker.dart (new functionality)

Expected Behavior

Add optional statistics tracking to the Tracker class that includes:

  1. Request Statistics:

    • Total requests sent
    • Successful responses
    • Failed responses
    • Timeout errors
    • Network errors
  2. Performance Metrics:

    • Average response time
    • Min/Max response times
    • Last response time
  3. Error Tracking:

    • Error counts by type
    • Last error message/time
  4. Availability:

    • Track success rate
    • Track consecutive failures
    • Last successful request time

Proposed Implementation

/// Statistics for tracker operations
class TrackerStatistics {
  int totalRequests = 0;
  int successfulRequests = 0;
  int failedRequests = 0;
  int timeoutErrors = 0;
  int networkErrors = 0;
  
  final List<Duration> responseTimes = [];
  
  DateTime? lastRequestTime;
  DateTime? lastSuccessTime;
  DateTime? lastFailureTime;
  
  String? lastErrorMessage;
  dynamic lastError;
  
  /// Average response time in milliseconds
  double get averageResponseTime {
    if (responseTimes.isEmpty) return 0.0;
    var total = responseTimes.fold<int>(
      0, 
      (sum, duration) => sum + duration.inMilliseconds,
    );
    return total / responseTimes.length;
  }
  
  /// Minimum response time
  Duration? get minResponseTime {
    if (responseTimes.isEmpty) return null;
    return responseTimes.reduce((a, b) => a < b ? a : b);
  }
  
  /// Maximum response time
  Duration? get maxResponseTime {
    if (responseTimes.isEmpty) return null;
    return responseTimes.reduce((a, b) => a > b ? a : b);
  }
  
  /// Success rate (0.0 to 1.0)
  double get successRate {
    if (totalRequests == 0) return 0.0;
    return successfulRequests / totalRequests;
  }
  
  /// Reset all statistics
  void reset() {
    totalRequests = 0;
    successfulRequests = 0;
    failedRequests = 0;
    timeoutErrors = 0;
    networkErrors = 0;
    responseTimes.clear();
    lastRequestTime = null;
    lastSuccessTime = null;
    lastFailureTime = null;
    lastErrorMessage = null;
    lastError = null;
  }
}

Add to Tracker class:

abstract class Tracker with EventsEmittable<TrackerEvent> {
  // ... existing code ...
  
  /// Statistics tracking (optional, disabled by default)
  bool enableStatistics = false;
  
  TrackerStatistics? _statistics;
  
  TrackerStatistics? get statistics => 
      enableStatistics ? _statistics : null;
  
  Tracker(this.id, this.announceUrl, this.infoHashBuffer, {this.provider}) {
    _statistics = TrackerStatistics();
  }
  
  Future<bool> _intervalAnnounce(String event) async {
    var startTime = DateTime.now();
    
    if (enableStatistics) {
      _statistics?.totalRequests++;
      _statistics?.lastRequestTime = startTime;
    }
    
    // ... existing announce code ...
    
    try {
      result = await announce(event, await _announceOptions);
      
      if (enableStatistics && result != null) {
        var duration = DateTime.now().difference(startTime);
        _statistics?.successfulRequests++;
        _statistics?.responseTimes.add(duration);
        _statistics?.lastSuccessTime = DateTime.now();
      }
      
      // ... rest of processing ...
    } catch (e) {
      if (enableStatistics) {
        _statistics?.failedRequests++;
        _statistics?.lastFailureTime = DateTime.now();
        _statistics?.lastError = e;
        _statistics?.lastErrorMessage = e.toString();
        
        if (e.toString().contains('timeout')) {
          _statistics?.timeoutErrors++;
        } else {
          _statistics?.networkErrors++;
        }
      }
      
      events.emit(TrackerAnnounceErrorEvent(this, e));
      return false;
    }
  }
}

Usage Example

var tracker = HttpTracker(uri, infoHash);
tracker.enableStatistics = true;

await tracker.start();

// Later, access statistics
var stats = tracker.statistics;
if (stats != null) {
  print('Total requests: ${stats.totalRequests}');
  print('Success rate: ${(stats.successRate * 100).toStringAsFixed(1)}%');
  print('Average response time: ${stats.averageResponseTime.toStringAsFixed(0)}ms');
  print('Last error: ${stats.lastErrorMessage}');
}

Impact

  • Severity: Low (Feature Enhancement)
  • Affected: All tracker instances (optional feature)
  • Benefits:
    • Monitoring and observability
    • Debugging performance issues
    • Optimizing tracker selection
    • Production diagnostics
    • User metrics and analytics

Additional Context

Statistics should be:

  • Optional (disabled by default for performance)
  • Low overhead when enabled
  • Thread-safe if used in multi-threaded environments
  • Resettable for testing/debugging
  • Accessible without breaking existing API

Related Standards

  • General best practices for metrics collection
  • Observability patterns

贡献者指南