Skip to content

Commit b305355

Browse files
Merge pull request #34 from stackroost/dev
feat(cli): add analyze-log-traffic command for domain access log insi…
2 parents fdb78db + 21c9880 commit b305355

File tree

2 files changed

+113
-1
lines changed

2 files changed

+113
-1
lines changed

cmd/logs/analyze_traffic.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package logs
2+
3+
import (
4+
"bufio"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"regexp"
9+
"sort"
10+
11+
"github.com/spf13/cobra"
12+
"stackroost/internal"
13+
"stackroost/internal/logger"
14+
)
15+
16+
var AnalyzeTrafficCmd = &cobra.Command{
17+
Use: "analyze-log-traffic",
18+
Short: "Analyze traffic from a domain's access log (IP, URLs, request count)",
19+
Run: runAnalyzeTraffic,
20+
}
21+
22+
func init() {
23+
AnalyzeTrafficCmd.Flags().String("domain", "", "Domain to analyze")
24+
AnalyzeTrafficCmd.Flags().Int("lines", 1000, "Number of lines to scan from the log file")
25+
AnalyzeTrafficCmd.MarkFlagRequired("domain")
26+
}
27+
28+
func runAnalyzeTraffic(cmd *cobra.Command, args []string) {
29+
domain, _ := cmd.Flags().GetString("domain")
30+
lines, _ := cmd.Flags().GetInt("lines")
31+
32+
if internal.IsNilOrEmpty(domain) {
33+
logger.Error("Please provide a domain using --domain")
34+
os.Exit(1)
35+
}
36+
37+
server := internal.DetectServerType(domain)
38+
if internal.IsNilOrEmpty(server) {
39+
logger.Warn("Could not detect server type for domain")
40+
os.Exit(1)
41+
}
42+
43+
var accessLog string
44+
switch server {
45+
case "apache":
46+
accessLog = fmt.Sprintf("/var/log/apache2/%s-access.log", domain)
47+
case "nginx":
48+
accessLog = fmt.Sprintf("/var/log/nginx/%s.access.log", domain)
49+
case "caddy":
50+
accessLog = fmt.Sprintf("/var/log/caddy/%s.access.log", domain)
51+
default:
52+
logger.Warn("Unsupported server type for log analysis")
53+
os.Exit(1)
54+
}
55+
56+
file, err := os.Open(filepath.Clean(accessLog))
57+
if err != nil {
58+
logger.Error(fmt.Sprintf("Failed to open access log: %v", err))
59+
os.Exit(1)
60+
}
61+
defer file.Close()
62+
63+
ipCount := make(map[string]int)
64+
urlCount := make(map[string]int)
65+
66+
scanner := bufio.NewScanner(file)
67+
lineNum := 0
68+
69+
for scanner.Scan() {
70+
line := scanner.Text()
71+
lineNum++
72+
if lines > 0 && lineNum > lines {
73+
break
74+
}
75+
76+
re := regexp.MustCompile(`^(\S+) .*?"\S+ (\S+) .*?" (\d{3})`)
77+
match := re.FindStringSubmatch(line)
78+
if len(match) >= 4 {
79+
ip := match[1]
80+
url := match[2]
81+
ipCount[ip]++
82+
urlCount[url]++
83+
}
84+
}
85+
86+
logger.Info("Top 5 IPs:")
87+
printTopN(ipCount, 5)
88+
89+
logger.Info("Top 5 URLs:")
90+
printTopN(urlCount, 5)
91+
}
92+
93+
func printTopN(data map[string]int, n int) {
94+
type kv struct {
95+
Key string
96+
Value int
97+
}
98+
99+
var sorted []kv
100+
for k, v := range data {
101+
sorted = append(sorted, kv{k, v})
102+
}
103+
104+
sort.Slice(sorted, func(i, j int) bool {
105+
return sorted[i].Value > sorted[j].Value
106+
})
107+
108+
for i := 0; i < n && i < len(sorted); i++ {
109+
fmt.Printf(" %s → %d\n", sorted[i].Key, sorted[i].Value)
110+
}
111+
}

cmd/root.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"stackroost/internal/logger"
1111
"strings"
1212
"stackroost/cmd/ssl"
13+
"stackroost/cmd/logs"
1314
)
1415

1516
var rootCmd = &cobra.Command{
@@ -210,7 +211,7 @@ func init() {
210211
createDomainCmd.Flags().Bool("ssl", false, "Enable Let's Encrypt SSL (Apache/Nginx only)")
211212
createDomainCmd.MarkFlagRequired("name")
212213
rootCmd.AddCommand(ssl.CheckSSLExpiryCmd)
213-
214+
rootCmd.AddCommand(logs.AnalyzeTrafficCmd)
214215
}
215216

216217
func Execute() {

0 commit comments

Comments
 (0)