Files
gh-icehugh-web3-skills-plug…/skills/walrus-monitor.md
2025-11-29 18:47:53 +08:00

22 KiB
Raw Permalink Blame History

name, description, parameters
name description parameters
walrus-monitor Walrus 监控分析专家 - 存储使用监控、成本分析和性能指标跟踪
name type description required
monitor-type string 监控类型 (usage/cost/performance/errors/analytics) true
name type description default
time-range string 时间范围 (1h/24h/7d/30d/custom) 24h
name type description default
metrics array 需要监控的具体指标

Walrus 监控分析技能

技能概述

walrus-monitor 是专业的监控分析助手,提供全面的 Walrus 存储系统监控、成本分析和性能指标跟踪功能,帮助开发者优化资源使用和控制成本。

监控维度

1. 存储使用监控

实时存储指标

class StorageMonitor {
  private metrics: StorageMetrics[] = [];
  private alerts: Alert[] = [];

  async collectStorageMetrics(): Promise<StorageMetrics> {
    const client = await this.getWalrusClient();
    const accountAddress = this.getAccountAddress();

    // 获取账户存储信息
    const storageInfo = await client.getStorageInfo(accountAddress);
    const blobIds = storageInfo.storedBlobs || [];

    // 计算存储统计
    const metrics: StorageMetrics = {
      timestamp: Date.now(),
      totalFiles: blobIds.length,
      totalSize: 0,
      totalCost: 0,
      storageDistribution: {},
      fileAges: [],
      oldestFile: null,
      newestFile: null,
      expiringSoon: [],
      expired: []
    };

    // 分析每个文件
    for (const blobId of blobIds) {
      try {
        const [file] = await client.walrus.getFiles({ ids: [blobId] });
        const metadata = await this.getFileMetadata(file);
        const storedUntil = await file.storedUntil();

        metrics.totalSize += metadata.size;
        metrics.totalCost += metadata.estimatedCost;

        // 文件类型分布
        const fileType = this.getFileType(metadata.mimeType);
        metrics.storageDistribution[fileType] =
          (metrics.storageDistribution[fileType] || 0) + 1;

        // 文件年龄分析
        const age = Date.now() - metadata.uploadTime;
        metrics.fileAges.push(age);

        // 检查过期状态
        const daysToExpiry = Math.floor((storedUntil.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
        if (daysToExpiry < 7) {
          metrics.expiringSoon.push({
            blobId,
            fileName: metadata.identifier,
            daysToExpiry,
            size: metadata.size
          });
        }

        if (daysToExpiry < 0) {
          metrics.expired.push({
            blobId,
            fileName: metadata.identifier,
            daysExpired: Math.abs(daysToExpiry),
            size: metadata.size
          });
        }

        // 更新最新和最旧文件
        if (!metrics.oldestFile || age > metrics.oldestFile.age) {
          metrics.oldestFile = {
            blobId,
            fileName: metadata.identifier,
            age,
            uploadDate: new Date(metadata.uploadTime)
          };
        }

        if (!metrics.newestFile || age < metrics.newestFile.age) {
          metrics.newestFile = {
            blobId,
            fileName: metadata.identifier,
            age,
            uploadDate: new Date(metadata.uploadTime)
          };
        }

      } catch (error) {
        console.warn(`Failed to analyze blob ${blobId}:`, error.message);
      }
    }

    // 存储指标历史
    this.metrics.push(metrics);
    if (this.metrics.length > 1000) {
      this.metrics.shift(); // 保留最近1000条记录
    }

    // 检查告警条件
    this.checkStorageAlerts(metrics);

    return metrics;
  }

  private checkStorageAlerts(metrics: StorageMetrics): void {
    // 存储空间告警
    if (metrics.totalSize > 100 * 1024 * 1024 * 1024) { // 100GB
      this.createAlert({
        type: 'STORAGE_LIMIT',
        severity: 'WARNING',
        message: `存储使用量达到 ${this.formatBytes(metrics.totalSize)}`,
        recommendation: '考虑清理过期文件或升级存储计划'
      });
    }

    // 文件数量告警
    if (metrics.totalFiles > 10000) {
      this.createAlert({
        type: 'FILE_COUNT_LIMIT',
        severity: 'INFO',
        message: `文件数量达到 ${metrics.totalFiles}`,
        recommendation: '考虑实施文件归档策略'
      });
    }

    // 即将过期告警
    if (metrics.expiringSoon.length > 0) {
      this.createAlert({
        type: 'EXPIRING_FILES',
        severity: 'WARNING',
        message: `${metrics.expiringSoon.length} 个文件将在7天内过期`,
        recommendation: '续期或备份重要文件'
      });
    }
  }

  getStorageTrends(timeRange: number = 7 * 24 * 60 * 60 * 1000): StorageTrends {
    const cutoff = Date.now() - timeRange;
    const relevantMetrics = this.metrics.filter(m => m.timestamp >= cutoff);

    if (relevantMetrics.length < 2) {
      return {
        growthRate: 0,
        costGrowthRate: 0,
        fileGrowthRate: 0,
        projectedUsage: null,
        projectedCost: null
      };
    }

    const oldest = relevantMetrics[0];
    const newest = relevantMetrics[relevantMetrics.length - 1];
    const timeDiff = newest.timestamp - oldest.timestamp;

    return {
      growthRate: this.calculateGrowthRate(oldest.totalSize, newest.totalSize, timeDiff),
      costGrowthRate: this.calculateGrowthRate(oldest.totalCost, newest.totalCost, timeDiff),
      fileGrowthRate: this.calculateGrowthRate(oldest.totalFiles, newest.totalFiles, timeDiff),
      projectedUsage: this.projectUsage(newest.totalSize, this.calculateGrowthRate(oldest.totalSize, newest.totalSize, timeDiff)),
      projectedCost: this.projectCost(newest.totalCost, this.calculateGrowthRate(oldest.totalCost, newest.totalCost, timeDiff))
    };
  }
}

2. 成本分析

成本监控和优化

class CostAnalyzer {
  private costHistory: CostRecord[] = [];
  private pricingModel: PricingModel;

  constructor() {
    this.pricingModel = {
      storagePerGBPerEpoch: 0.001,
      writeFeePerMB: 0.0001,
      readFeePerMB: 0.00001,
      gasMultiplier: 1.0
    };
  }

  async analyzeCosts(timeRange: number = 30 * 24 * 60 * 60 * 1000): Promise<CostAnalysis> {
    const records = this.getRelevantCostRecords(timeRange);
    const storageMetrics = await this.getStorageMetrics(timeRange);

    return {
      totalCost: this.calculateTotalCost(records),
      storageCost: this.calculateStorageCost(storageMetrics),
      transactionCost: this.calculateTransactionCost(records),
      breakdown: this.getCostBreakdown(records),
      optimization: this.getOptimizationSuggestions(storageMetrics, records),
      forecast: this.forecastCosts(records, storageMetrics)
    };
  }

  private calculateStorageCost(metrics: StorageMetrics[]): number {
    return metrics.reduce((total, metric) => {
      const storageEpochs = 30; // 假设30天的轮数
      const sizeGB = metric.totalSize / (1024 * 1024 * 1024);
      return total + (sizeGB * this.pricingModel.storagePerGBPerEpoch * storageEpochs);
    }, 0);
  }

  private calculateTransactionCost(records: CostRecord[]): number {
    return records
      .filter(r => r.type === 'transaction')
      .reduce((total, record) => total + record.amount, 0);
  }

  private getOptimizationSuggestions(metrics: StorageMetrics[], records: CostRecord[]): OptimizationSuggestion[] {
    const suggestions: OptimizationSuggestion[] = [];

    // 分析文件大小分布
    const sizeDistribution = this.analyzeSizeDistribution(metrics);
    if (sizeDistribution.smallFiles > sizeDistribution.largeFiles * 2) {
      suggestions.push({
        type: 'BUNDLE_SMALL_FILES',
        description: '检测到大量小文件,建议使用 Quilt 打包存储',
        potentialSavings: '20-30%',
        difficulty: 'medium',
        implementation: this.getBundleImplementation()
      });
    }

    // 分析存储时长
    const avgStorageTime = this.calculateAverageStorageTime(metrics);
    if (avgStorageTime > 90) { // 超过90天
      suggestions.push({
        type: 'OPTIMIZE_STORAGE_DURATION',
        description: `平均存储时长 ${avgStorageTime} 天,建议实施生命周期管理`,
        potentialSavings: '40-60%',
        difficulty: 'low',
        implementation: this.getLifecycleImplementation()
      });
    }

    // 分析访问模式
    const accessPattern = this.analyzeAccessPattern(records);
    if (accessPattern.readHeavy) {
      suggestions.push({
        type: 'IMPLEMENT_CACHING',
        description: '读取频繁,建议实施缓存层',
        potentialSavings: '50-70%',
        difficulty: 'medium',
        implementation: this.getCacheImplementation()
      });
    }

    return suggestions;
  }

  private getBundleImplementation(): string {
    return `
// 使用 Quilt 打包小文件
async function bundleSmallFiles(files: WalrusFile[]): Promise<string> {
  const quilt = QuiltBuilder.create()
    .addFiles(files)
    .build();

  const result = await client.walrus.writeFiles({
    files: [quilt],
    epochs: 30,
    deletable: true,
    signer: keypair
  });

  return result[0].blobId;
}

// 原本多个小文件:
// File 1: 10KB, File 2: 15KB, File 3: 8KB = 33KB 总计
// 打包后:单个 Quilt 包含所有文件,减少存储开销
    `;
  }

  private getLifecycleImplementation(): string {
    return `
// 自动生命周期管理
class StorageLifecycleManager {
  async manageExpiredFiles() {
    const expiringFiles = await this.getFilesExpiringWithin(7);

    for (const file of expiringFiles) {
      const accessFrequency = await this.getAccessFrequency(file.blobId);

      if (accessFrequency === 'never') {
        // 删除未访问的文件
        await this.deleteFile(file.blobId);
      } else if (accessFrequency === 'rare') {
        // 降低存储时长
        await this.reduceStorageEpochs(file.blobId, 10);
      } else {
        // 续期重要文件
        await this.extendStorage(file.blobId, 30);
      }
    }
  }

  private async getAccessFrequency(blobId: string): Promise<'never' | 'rare' | 'frequent'> {
    // 分析访问日志
    const accessLogs = await this.getAccessLogs(blobId, 30); // 30天内
    const accessCount = accessLogs.length;

    if (accessCount === 0) return 'never';
    if (accessCount < 5) return 'rare';
    return 'frequent';
  }
}
    `;
  }
}

3. 性能监控

实时性能指标

class PerformanceMonitor {
  private performanceMetrics: PerformanceMetric[] = [];
  private slowQueries: SlowQuery[] = [];
  private alerts: PerformanceAlert[] = [];

  async recordPerformanceMetric(
    operation: string,
    duration: number,
    success: boolean,
    metadata: any = {}
  ): Promise<void> {
    const metric: PerformanceMetric = {
      timestamp: Date.now(),
      operation,
      duration,
      success,
      metadata
    };

    this.performanceMetrics.push(metric);

    // 限制历史数据
    if (this.performanceMetrics.length > 10000) {
      this.performanceMetrics.shift();
    }

    // 检查性能告警
    this.checkPerformanceAlerts(metric);

    // 记录慢操作
    if (duration > this.getSlowThreshold(operation)) {
      this.slowQueries.push({
        ...metric,
        threshold: this.getSlowThreshold(operation),
        severity: this.getSeverityLevel(duration, this.getSlowThreshold(operation))
      });

      // 限制慢查询记录
      if (this.slowQueries.length > 1000) {
        this.slowQueries.shift();
      }
    }
  }

  getPerformanceAnalysis(timeRange: number = 24 * 60 * 60 * 1000): PerformanceAnalysis {
    const metrics = this.getRelevantMetrics(timeRange);

    return {
      overview: this.getPerformanceOverview(metrics),
      operations: this.analyzeOperations(metrics),
      trends: this.analyzeTrends(metrics),
      bottlenecks: this.identifyBottlenecks(metrics),
      recommendations: this.getPerformanceRecommendations(metrics)
    };
  }

  private analyzeOperations(metrics: PerformanceMetric[]): OperationAnalysis[] {
    const operationGroups = this.groupBy(metrics, 'operation');
    const analyses: OperationAnalysis[] = [];

    for (const [operation, operationMetrics] of Object.entries(operationGroups)) {
      const durations = operationMetrics.map(m => m.duration);
      const successRate = operationMetrics.filter(m => m.success).length / operationMetrics.length;

      analyses.push({
        operation,
        totalCalls: operationMetrics.length,
        successRate,
        avgDuration: durations.reduce((a, b) => a + b, 0) / durations.length,
        minDuration: Math.min(...durations),
        maxDuration: Math.max(...durations),
        p95: this.percentile(durations, 0.95),
        p99: this.percentile(durations, 0.99),
        errorRate: 1 - successRate,
        slowQueries: operationMetrics.filter(m => m.duration > this.getSlowThreshold(operation)).length
      });
    }

    return analyses.sort((a, b) => b.avgDuration - a.avgDuration);
  }

  private identifyBottlenecks(metrics: PerformanceMetric[]): PerformanceBottleneck[] {
    const bottlenecks: PerformanceBottleneck[] = [];

    // 识别慢操作
    const slowOperations = this.analyzeOperations(metrics)
      .filter(op => op.avgDuration > this.getSlowThreshold(op.operation));

    for (const operation of slowOperations) {
      bottlenecks.push({
        type: 'SLOW_OPERATION',
        operation: operation.operation,
        severity: this.getBottleneckSeverity(operation.avgDuration, this.getSlowThreshold(operation.operation)),
        impact: 'medium',
        description: `\${operation.operation} 平均耗时 \${operation.avgDuration}ms`,
        suggestion: this.getOptimizationSuggestion(operation.operation)
      });
    }

    // 识别错误率高的操作
    const highErrorOps = this.analyzeOperations(metrics)
      .filter(op => op.errorRate > 0.05); // 5% 错误率

    for (const operation of highErrorOps) {
      bottlenecks.push({
        type: 'HIGH_ERROR_RATE',
        operation: operation.operation,
        severity: 'high',
        impact: 'high',
        description: `\${operation.operation} 错误率 \${(operation.errorRate * 100).toFixed(1)}%`,
        suggestion: this.getErrorOptimizationSuggestion(operation.operation)
      });
    }

    return bottlenecks;
  }

  private getPerformanceRecommendations(metrics: PerformanceMetric[]): PerformanceRecommendation[] {
    const recommendations: PerformanceRecommendation[] = [];

    // 分析上传性能
    const uploadMetrics = metrics.filter(m => m.operation.startsWith('upload'));
    if (uploadMetrics.length > 0) {
      const avgUploadSize = this.getAverageUploadSize(uploadMetrics);
      const avgUploadTime = uploadMetrics.reduce((sum, m) => sum + m.duration, 0) / uploadMetrics.length;

      if (avgUploadSize > 10 * 1024 * 1024 && avgUploadTime > 10000) { // 10MB, 10s
        recommendations.push({
          type: 'UPLOAD_OPTIMIZATION',
          priority: 'high',
          title: '大文件上传优化',
          description: '检测到大文件上传性能问题',
          implementation: `
// 实施分块上传和断点续传
class ChunkedUploader {
  async uploadLargeFile(file: File, chunkSize = 5 * 1024 * 1024): Promise<string> {
    const chunks = Math.ceil(file.size / chunkSize);
    const chunkIds: string[] = [];

    for (let i = 0; i < chunks; i++) {
      const start = i * chunkSize;
      const end = Math.min(start + chunkSize, file.size);
      const chunk = file.slice(start, end);

      const result = await this.uploadChunk(chunk, i, chunks);
      chunkIds.push(result.blobId);

      // 更新进度
      this.updateProgress((i + 1) / chunks);
    }

    // 创建文件清单
    return await this.createManifest(chunkIds, file);
  }
}

// 使用压缩减少传输量
const compressedFile = await this.compressFile(file);
const uploadResult = await client.walrus.writeFiles({
  files: [compressedFile],
  epochs: 10,
  signer: keypair
});
          `,
          expectedImprovement: '50-70%'
        });
      }
    }

    // 分析批量操作性能
    const batchMetrics = metrics.filter(m => m.operation.includes('batch'));
    if (batchMetrics.length > 0) {
      const concurrencyIssues = batchMetrics.filter(m => m.duration > 60000).length; // 超过1分钟

      if (concurrencyIssues > batchMetrics.length * 0.3) { // 30%超时
        recommendations.push({
          type: 'BATCH_OPTIMIZATION',
          priority: 'medium',
          title: '批量操作并发优化',
          description: '批量操作存在并发瓶颈',
          implementation: `
// 优化并发控制
class OptimizedBatchProcessor {
  private readonly MAX_CONCURRENT = 3;
  private readonly RETRY_DELAY = 1000;

  async processBatch<T>(items: T[], processor: (item: T) => Promise<any>): Promise<any[]> {
    const results: any[] = [];
    const chunks = this.chunkArray(items, this.MAX_CONCURRENT);

    for (const chunk of chunks) {
      const chunkPromises = chunk.map(async (item, index) => {
        try {
          return await this.withRetry(() => processor(item), 3);
        } catch (error) {
          console.error(\`Batch item failed: \${error.message}\`);
          return null;
        }
      });

      const chunkResults = await Promise.all(chunkPromises);
      results.push(...chunkResults);

      // 批次间延迟
      if (chunks.indexOf(chunk) < chunks.length - 1) {
        await this.delay(this.RETRY_DELAY);
      }
    }

    return results;
  }
}
          `,
          expectedImprovement: '30-50%'
        });
      }
    }

    return recommendations;
  }
}

4. 错误监控

错误统计和分析

class ErrorMonitor {
  private errorLog: ErrorRecord[] = [];
  private errorPatterns: ErrorPattern[] = [];

  recordError(error: Error, context: ErrorContext): void {
    const errorRecord: ErrorRecord = {
      timestamp: Date.now(),
      message: error.message,
      stack: error.stack,
      type: this.classifyError(error),
      severity: this.determineSeverity(error, context),
      context,
      resolved: false
    };

    this.errorLog.push(errorRecord);

    // 分析错误模式
    this.analyzeErrorPattern(errorRecord);

    // 错误告警
    this.checkErrorAlerts(errorRecord);
  }

  getErrorAnalysis(timeRange: number = 24 * 60 * 60 * 1000): ErrorAnalysis {
    const recentErrors = this.getRecentErrors(timeRange);

    return {
      summary: this.getErrorSummary(recentErrors),
      trends: this.getErrorTrends(recentErrors),
      patterns: this.getErrorPatterns(recentErrors),
      recommendations: this.getErrorRecommendations(recentErrors),
      resolutionRate: this.calculateResolutionRate(recentErrors)
    };
  }

  private analyzeErrorPattern(errorRecord: ErrorRecord): void {
    const patternKey = this.getPatternKey(errorRecord);
    let pattern = this.errorPatterns.find(p => p.key === patternKey);

    if (!pattern) {
      pattern = {
        key: patternKey,
        type: errorRecord.type,
        count: 0,
        firstOccurrence: errorRecord.timestamp,
        lastOccurrence: errorRecord.timestamp,
        contexts: [],
        resolutions: []
      };
      this.errorPatterns.push(pattern);
    }

    pattern.count++;
    pattern.lastOccurrence = errorRecord.timestamp;

    // 记录上下文
    const contextHash = this.hashContext(errorRecord.context);
    if (!pattern.contexts.some(c => c.hash === contextHash)) {
      pattern.contexts.push({
        hash: contextHash,
        context: errorRecord.context,
        occurrences: 1
      });
    } else {
      const existingContext = pattern.contexts.find(c => c.hash === contextHash);
      if (existingContext) {
        existingContext.occurrences++;
      }
    }
  }

  private getErrorRecommendations(errors: ErrorRecord[]): ErrorRecommendation[] {
    const recommendations: ErrorRecommendation[] = [];

    // 分析最常见的错误类型
    const errorTypes = this.groupBy(errors, 'type');
    const mostCommonType = Object.entries(errorTypes)
      .sort(([, a], [, b]) => b.length - a.length)[0];

    if (mostCommonType && mostCommonType[1].length > errors.length * 0.3) { // 30%以上
      recommendations.push({
        type: 'PREVENTION',
        priority: 'high',
        title: `高频率 \${mostCommonType[0]} 错误`,
        description: `\${mostCommonType[1].length} 次错误中 \${mostCommonType[1].length} 次是 \${mostCommonType[0]} 错误`,
        implementation: this.getPreventionImplementation(mostCommonType[0]),
        expectedReduction: '60-80%'
      });
    }

    // 分析网络相关错误
    const networkErrors = errors.filter(e => e.type === 'network' || e.type === 'timeout');
    if (networkErrors.length > errors.length * 0.2) { // 20%以上
      recommendations.push({
        type: 'NETWORK_OPTIMIZATION',
        priority: 'medium',
        title: '网络连接优化',
        description: '检测到大量网络相关错误',
        implementation: `
// 实施网络优化策略
class NetworkOptimizer {
  private readonly circuitBreaker = new CircuitBreaker({
    failureThreshold: 5,
    recoveryTimeout: 60000,
    monitoringPeriod: 30000
  });

  async robustRequest(url: string, options: RequestInit = {}): Promise<Response> {
    return this.circuitBreaker.execute(async () => {
      // 使用指数退避重试
      return this.withRetry(() => fetch(url, {
        ...options,
        timeout: 30000
      }), 3);
    });
  }

  private async withRetry<T>(
    operation: () => Promise<T>,
    maxRetries: number = 3
  ): Promise<T> {
    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        return await operation();
      } catch (error) {
        if (attempt === maxRetries || !this.isRetryableError(error)) {
          throw error;
        }

        const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
        await this.delay(delay);
      }
    }

    throw new Error('Max retries exceeded');
  }
}
        `,
        expectedReduction: '70-90%'
      });
    }

    return recommendations;
  }
}

使用方法

存储使用监控

skill: "walrus-monitor"
// monitor-type: usage
// time-range: 7d
// metrics: ["total-size", "file-count", "cost", "aging"]

成本分析

skill: "walrus-monitor"
// monitor-type: cost
// time-range: 30d
// metrics: ["storage-cost", "transaction-cost", "optimization-suggestions"]

性能监控

skill: "walrus-monitor"
// monitor-type: performance
// time-range: 24h
// metrics: ["upload-speed", "download-speed", "error-rate", "bottlenecks"]

错误分析

skill: "walrus-monitor"
// monitor-type: errors
// time-range: 7d
// metrics: ["error-patterns", "resolution-rate", "prevention-suggestions"]

更新时间2025-11-11