22 KiB
22 KiB
name, description, parameters
| name | description | parameters | |||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| walrus-monitor | Walrus 监控分析专家 - 存储使用监控、成本分析和性能指标跟踪 |
|
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