UNPKG

@nanocollective/nanocoder

Version:

A local-first CLI coding agent that brings the power of agentic coding tools like Claude Code and Gemini CLI to local models or controlled APIs like OpenRouter

119 lines 4.51 kB
/** * Log entry aggregation engine */ /** * Get group key for an entry based on groupBy field */ export function getGroupKey(entry, groupBy) { switch (groupBy) { case 'hour': return new Date(entry.timestamp).toISOString().substring(0, 13); // YYYY-MM-DDTHH case 'day': return new Date(entry.timestamp).toISOString().substring(0, 10); // YYYY-MM-DD case 'level': return entry.level; case 'source': return entry.source || 'unknown'; case 'correlationId': return entry.correlationId || 'no-correlation'; case 'errorType': return entry.error?.type || 'no-error'; default: return 'unknown'; } } /** * Aggregate log entries based on options */ export function aggregateLogEntries(entries, options) { const startTime = performance.now(); // Filter by time range if specified let filteredEntries = entries; if (options.timeRange?.startTime && options.timeRange?.endTime) { filteredEntries = entries.filter(entry => { const entryTime = new Date(entry.timestamp); return ( // biome-ignore lint/style/noNonNullAssertion: timeRange existence checked in if condition above entryTime >= options.timeRange.startTime && // biome-ignore lint/style/noNonNullAssertion: timeRange existence checked in if condition above entryTime <= options.timeRange.endTime); }); } // biome-ignore lint/suspicious/noExplicitAny: Dynamic log bindings const groups = {}; // Group entries for (const entry of filteredEntries) { const groupKey = getGroupKey(entry, options.groupBy); if (!groups[groupKey]) { groups[groupKey] = { count: 0, durations: [], memoryUsages: [], errorCount: 0, samples: [], }; } const group = groups[groupKey]; group.count++; if (entry.performance?.duration) { group.durations.push(entry.performance.duration); } if (entry.performance?.memory) { group.memoryUsages.push(entry.performance.memory.heapUsed || 0); } if (entry.error) { group.errorCount++; } // Keep sample entries for analysis if (group.samples.length < 10) { group.samples.push(entry); } } // Calculate aggregations // biome-ignore lint/suspicious/noExplicitAny: Dynamic log bindings const result = {}; for (const [groupKey, group] of Object.entries(groups)) { // biome-ignore lint/suspicious/noExplicitAny: Dynamic value type const groupResult = { count: group.count }; if (options.aggregations.includes('avgDuration') && group.durations.length > 0) { groupResult.avgDuration = group.durations.reduce((sum, d) => sum + d, 0) / group.durations.length; } if (options.aggregations.includes('maxDuration') && group.durations.length > 0) { groupResult.maxDuration = Math.max(...group.durations); } if (options.aggregations.includes('minDuration') && group.durations.length > 0) { groupResult.minDuration = Math.min(...group.durations); } if (options.aggregations.includes('sumDuration')) { groupResult.sumDuration = group.durations.reduce((sum, d) => sum + d, 0); } if (options.aggregations.includes('errorRate')) { groupResult.errorRate = group.errorCount / group.count; } if (options.aggregations.includes('memoryUsage') && group.memoryUsages.length > 0) { groupResult.memoryUsage = { avgHeapUsed: group.memoryUsages.reduce((sum, m) => sum + m, 0) / group.memoryUsages.length, maxHeapUsed: Math.max(...group.memoryUsages), minHeapUsed: Math.min(...group.memoryUsages), }; } if (options.aggregations.includes('count')) { groupResult.samples = group.samples; } result[groupKey] = groupResult; } const queryTime = performance.now() - startTime; return { groups: result, totalGroups: Object.keys(result).length, queryTime, }; } //# sourceMappingURL=aggregator.js.map