Skip to content

Conversation

@Mossaka
Copy link
Collaborator

@Mossaka Mossaka commented Dec 19, 2025

Summary

  • Add awf logs stats command to show aggregated statistics from firewall logs
  • Add awf logs summary command to generate summary reports (optimized for GitHub Actions)
  • Both commands support --format json|markdown|pretty for flexible output

Motivation

This PR addresses the complexity of parsing firewall logs in GitHub Actions workflows. Previously, users had to write 150+ lines of custom JavaScript to parse Squid log files and generate summaries. Now this can be done with a single command:

- name: Generate firewall summary
  if: always()
  run: awf logs summary >> $GITHUB_STEP_SUMMARY

New Commands

awf logs stats

Shows aggregated statistics in the terminal (default: pretty format):

awf logs stats                    # Pretty terminal output
awf logs stats --format json      # JSON for scripting
awf logs stats --format markdown  # Markdown format

awf logs summary

Generates summary report (default: markdown format for GitHub Actions):

awf logs summary                  # Default: markdown format
awf logs summary >> $GITHUB_STEP_SUMMARY  # GitHub Actions

Output Examples

Pretty Format (terminal)

Firewall Statistics
────────────────────────────────────────
Total Requests:  150
Allowed:         145 (96.7%)
Denied:          5 (3.3%)
Unique Domains:  12

Domains:
  api.github.com       50 allowed, 0 denied
  registry.npmjs.org   95 allowed, 0 denied
  evil.com             0 allowed, 5 denied

Markdown Format (GitHub Actions)

### Firewall Activity

<details>
<summary>150 requests | 145 allowed | 5 blocked | 12 unique domains</summary>

| Domain | Allowed | Denied |
|--------|---------|--------|
| api.github.com | 50 | 0 |
| registry.npmjs.org | 95 | 0 |
| evil.com | 0 | 5 |

</details>

Test plan

  • All existing tests pass (389 tests)
  • New tests added for log-aggregator, stats-formatter, logs-stats command, logs-summary command
  • Build succeeds
  • Commands work correctly (awf logs stats --help, awf logs summary --help)

🤖 Generated with Claude Code

Add built-in log aggregation and summary generation to reduce complexity
of parsing firewall logs in GitHub Actions workflows.

New commands:
- `awf logs stats` - Show aggregated statistics (default: pretty format)
- `awf logs summary` - Generate summary report (default: markdown format)

Both commands support --format json|markdown|pretty for flexible output.

Example GitHub Actions usage:
  awf logs summary >> $GITHUB_STEP_SUMMARY

This replaces 150+ lines of custom JavaScript log parsing with a single command.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@github-actions
Copy link

github-actions bot commented Dec 19, 2025

Test Coverage Report

Metric Coverage Covered/Total
Lines 69.91% 918/1313
Statements 70.05% 943/1346
Functions 69.62% 110/158
Branches 64.05% 303/473
Coverage Thresholds

The project has the following coverage thresholds configured:

  • Lines: 38%
  • Statements: 38%
  • Functions: 35%
  • Branches: 30%

Coverage report generated by `npm run test:coverage`

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds two new CLI commands (awf logs stats and awf logs summary) that aggregate and display firewall log statistics in multiple formats. The commands are specifically designed to simplify log analysis in GitHub Actions workflows, replacing the need for custom parsing scripts.

Key changes:

  • New log aggregation module that computes statistics from parsed Squid log entries
  • Flexible output formatters supporting JSON, Markdown, and pretty-printed terminal formats
  • Two command interfaces: stats (terminal-focused, defaults to pretty) and summary (GitHub Actions-focused, defaults to markdown)

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
src/logs/log-aggregator.ts Implements log loading and aggregation logic for computing statistics across all log entries
src/logs/log-aggregator.test.ts Comprehensive test suite for aggregation logic, including edge cases for empty logs and missing domains
src/logs/stats-formatter.ts Formatters for JSON, Markdown, and pretty-printed terminal output with color support
src/logs/stats-formatter.test.ts Tests for all three output formatters, including singular/plural handling and domain filtering
src/logs/index.ts Exports new aggregation and formatting functions for external use
src/commands/logs-stats.ts Command handler for awf logs stats with pretty format as default
src/commands/logs-stats.test.ts Tests for stats command including source discovery and error handling
src/commands/logs-summary.ts Command handler for awf logs summary with markdown format as default for GitHub Actions
src/commands/logs-summary.test.ts Tests for summary command mirroring stats command test coverage
src/cli.ts CLI integration adding stats and summary as subcommands of the logs command
README.md User-facing documentation with usage examples for the new commands
CLAUDE.md Developer documentation explaining the architecture and data structures

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 16 to 20

/**
* Output format type for summary command
*/
export type SummaryFormat = 'json' | 'markdown' | 'pretty';
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The StatsFormat and SummaryFormat types are defined separately in their respective command files but are identical. This creates potential for inconsistency if one is changed without updating the other.

Consider defining a shared type in a common location (e.g., src/types.ts) or simply reusing the same type:

export type LogStatsFormat = 'json' | 'markdown' | 'pretty';

Then both commands can import and use this shared type.

Suggested change
/**
* Output format type for summary command
*/
export type SummaryFormat = 'json' | 'markdown' | 'pretty';
import type { StatsFormat } from './logs-stats';
/**
* Output format type for summary command
*
* Alias of StatsFormat to ensure a single source of truth
* for supported log output formats.
*/
export type SummaryFormat = StatsFormat;

Copilot uses AI. Check for mistakes.
.sort((a, b) => b.total - a.total);

// Calculate max domain length for alignment
const maxDomainLen = Math.max(...sortedDomains.map(d => d.domain.length), 0);
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When all domains in the byDomain map are "-" (no valid domains), sortedDomains will be empty after filtering, which could cause Math.max(...sortedDomains.map(...)) on line 146 to return -Infinity. This would make maxDomainLen equal to -Infinity, causing unexpected padding behavior.

Consider adding a guard: const maxDomainLen = sortedDomains.length > 0 ? Math.max(...sortedDomains.map(d => d.domain.length)) : 0;

Copilot uses AI. Check for mistakes.
@Mossaka
Copy link
Collaborator Author

Mossaka commented Dec 19, 2025

@copilot open a new pull request to apply changes based on the comments in this thread

Copy link
Contributor

Copilot AI commented Dec 19, 2025

@Mossaka I've opened a new pull request, #126, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI added a commit that referenced this pull request Dec 19, 2025
- Move duplicate StatsFormat/SummaryFormat types to shared LogStatsFormat in types.ts
- Simplify colorize fallback using Proxy pattern instead of object literal
- Add null checks for source.path and source.containerName before usage
- Extract duplicated format validation into validateFormat helper function
- Refactor duplicated command handler logic into shared logs-command-helpers module
- Document intentional logging suppression difference between stats and summary commands
- Fix uniqueDomains count discrepancy by showing valid domain count when "-" present
- Add guard for empty sortedDomains to prevent -Infinity in maxDomainLen calculation

Co-authored-by: Mossaka <5447827+Mossaka@users.noreply.github.com>
@Mossaka Mossaka added the smoke label Dec 19, 2025
Copilot AI and others added 2 commits December 19, 2025 12:48
…#126)

* Initial plan

* refactor: address code review comments from PR #118

- Move duplicate StatsFormat/SummaryFormat types to shared LogStatsFormat in types.ts
- Simplify colorize fallback using Proxy pattern instead of object literal
- Add null checks for source.path and source.containerName before usage
- Extract duplicated format validation into validateFormat helper function
- Refactor duplicated command handler logic into shared logs-command-helpers module
- Document intentional logging suppression difference between stats and summary commands
- Fix uniqueDomains count discrepancy by showing valid domain count when "-" present
- Add guard for empty sortedDomains to prevent -Infinity in maxDomainLen calculation

Co-authored-by: Mossaka <5447827+Mossaka@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Mossaka <5447827+Mossaka@users.noreply.github.com>
Resolved README.md conflict - keeping simplified README that points to docs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@Mossaka Mossaka merged commit 960609f into main Dec 19, 2025
10 checks passed
@Mossaka Mossaka deleted the feat/logs-stats-summary branch December 19, 2025 21:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants