-
Notifications
You must be signed in to change notification settings - Fork 2
CloudWatch Logs filter (v0.9.1) #122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
* Add filter functionality to CloudWatch Logs tail view Implements hybrid filter system with AWS and client modes: - AWS mode: Uses CloudWatch filterPattern API (cost efficient) - Client mode: Local substring match (instant UX) Key features: - / key activates filter input (consistent with ResourceBrowser) - Client mode: real-time filtering as user types - AWS mode: applies filter on Enter (triggers API re-fetch) - Ctrl+F toggles between modes - c key clears filter (or buffer if no filter) - Visual indicators: 🔍 filter display, mode label [AWS]/[Client] - Filtered count display: (45/1,234 lines) Implementation: - Single file change: internal/view/log_view.go - Pure additive feature, no breaking changes - Follows existing patterns (textinput, ViewportState) Closes #120 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Fix Backspace handling in filter input mode Add HasActiveInput() method to LogView so app.go correctly handles input mode and doesn't treat Backspace as back navigation. Fixes issue where editing filter text with Backspace caused screen to go back instead of deleting characters. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Simplify filter to client-side only Remove AWS mode complexity. Client-side substring match is sufficient for tail use case where logs are already fetched. Removed: - filterPattern (AWS server-side filtering) - filterMode toggle - Ctrl+F mode switching - Mode display [AWS]/[Client] - API FilterPattern parameter Simplified: - Single filterText field (client-side substring) - / to activate filter - Real-time filtering as user types - Enter to done (no-op, already filtered) - c to clear - Cleaner UI without mode indicators Rationale: Tail views logs already fetched. AWS filtering only useful for high-volume streams (rare case). Hybrid approach added unnecessary complexity and confusion (which filter is active?). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Refactor: extract filter logic to matchesFilter method Eliminate duplication by extracting filter matching logic into dedicated matchesFilter() method, reused in updateViewportContent() and getDisplayedCount(). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * Fix filter issues: SetSize recalc, tests, min width, truncate Addresses PR review feedback: - Track width/height to recalc viewport when filter cleared - Add min width check (10 chars) for narrow terminals - Truncate long filter text in status line (>20 chars) - Add comprehensive test coverage (7 new tests) - Keep real-time filtering (1000 logs = negligible perf impact) Tests added: - TestLogViewFilterActivation - TestLogViewFilterMatching - TestLogViewFilterClear - TestLogViewFilteredCount - TestLogViewHasActiveInput - TestLogViewFilterSetSizeRecalculation - TestLogViewFilterStatusLine 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This comment was marked as resolved.
This comment was marked as resolved.
Addresses PR #122 review feedback: 1. Extract magic numbers to constants: - filterInputPadding = 4 - minFilterWidth = 10 - maxFilterDisplayLength = 20 2. Fix Unicode slicing in status line: - Use rune-based slicing instead of byte slicing - Prevents breaking multi-byte UTF-8 characters (emoji, CJK) 3. Add Unicode filter test coverage: - Emoji filtering (🔥, ✅) - Japanese character filtering (日本語) - Long Unicode filter truncation - Verify no broken encoding (�) All tests pass, including new TestLogViewFilterUnicode. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
PR Review: CloudWatch Logs Filter (v0.9.1)SummaryThis PR adds client-side substring filtering to the CloudWatch Logs tail view. Overall, this is a well-implemented feature with excellent test coverage. The code follows the repository's patterns and integrates cleanly with the existing TUI framework. ✅ Strengths1. Excellent Test Coverage
2. Consistent Architecture
3. User Experience
4. Code Quality
🔍 Observations & Suggestions1. Performance Consideration (Minor)Location: The filter is applied twice per render cycle:
Suggestion: Consider caching the filtered count when updating viewport content: func (v *LogView) updateViewportContent() {
var sb strings.Builder
v.displayedCount = 0 // Add field to struct
for _, entry := range v.logs {
if !v.matchesFilter(entry) {
continue
}
v.displayedCount++
ts := v.styles.timestamp.Render(entry.timestamp.Format("15:04:05.000"))
msg := v.styles.message.Render(entry.message)
sb.WriteString(fmt.Sprintf("%s %s\n", ts, msg))
}
v.vp.Model.SetContent(sb.String())
}
func (v *LogView) getDisplayedCount() int {
if v.filterText == "" {
return len(v.logs)
}
return v.displayedCount
}Impact: Low priority - only matters with >10k logs, but worth considering for consistency with the "no lag" performance claim. 2. Case-Insensitive Filter Allocation (Minor)Location: The filter creates a new lowercase string for each log entry: filter := strings.ToLower(v.filterText)
msg := strings.ToLower(entry.message)Suggestion: Pre-compute the lowercase filter once and store it: // Add to struct
filterTextLower string
// In handleFilterInput and filter clear
v.filterText = v.filterInput.Value()
v.filterTextLower = strings.ToLower(v.filterText)
// In matchesFilter
func (v *LogView) matchesFilter(entry logEntry) bool {
if v.filterText == "" {
return true
}
msg := strings.ToLower(entry.message)
return strings.Contains(msg, v.filterTextLower)
}Impact: Micro-optimization, reduces allocations from 3. Test Coverage Gap (Informational)Location: Missing test cases (not critical, but would be nice to have):
4. Viewport Height Recalculation (Minor Logic Question)Location: When clearing the filter with v.SetSize(v.width, v.height) // Recalculate viewport heightQuestion: Is this necessary? The viewport height should auto-adjust on the next render cycle when Suggestion: Consider removing unless there's a specific visual glitch this fixes. If kept, add a comment explaining why immediate recalculation is needed. 5. Status Line Filter Display Truncation (Potential UX Issue)Location: The truncation logic uses rune slicing but doesn't account for zero-width characters or combining marks: filterDisplay := v.filterText
runes := []rune(filterDisplay)
if len(runes) > maxFilterDisplayLength {
filterDisplay = string(runes[:maxFilterDisplayLength-3]) + "..."
}Suggestion: While the Unicode test passes, consider using a library like Impact: Very low - current implementation is fine for 99% of use cases. 🔒 Security✅ No security concerns identified:
🎯 Best Practices AlignmentFollows Repository Conventions:
Code Style:
📊 Test ResultsBased on the PR description:
🎉 ConclusionOverall Assessment: LGTM with minor suggestions This is a solid implementation that adds real value to the CloudWatch Logs experience. The code quality is high, test coverage is excellent, and it follows all the repository's established patterns. Recommendation: ✅ Approve The suggestions above are minor optimizations that can be addressed in future PRs if needed. None of them are blockers for merging. Priority of Suggestions:
Great work! 🚀 Review generated by Claude Code |
Summary
Client-side substring filter for CloudWatch Logs tail view
Changes
/key, real-time filtering, Esc/Entercclears filter → bufferImplementation Details
Testing
Review
Closes #120
🤖 Generated with Claude Code