atlet99/dtorrent_tracker_v2

[FEATURE] - Add tracker statistics and metrics;

オープン

#9 opened on 2025/11/03

 (0 件のコメント) (0 件のリアクション) (0 人の担当者)Dart (0 件のフォーク)auto 404
enhancementhelp wanted

Repository metrics

Stars
 (0 個のスター)
PR merge metrics
 (30d に merged 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

コントリビューターガイド