实现一个 日志分析工具(CLI),主要功能包括日志文件读取,日志过滤,日志统计,日志时间段过滤。以下功能说明:
-
日志文件读取:
- 实现读取本地日志文件的功能。
- 支持逐行读取日志文件。
- 支持读取文本日志文件(比如
.log格式)。
-
日志过滤:
- 实现按日志单词(如INFO, ERROR, WARN)过滤日志。
- 实现通过命令行参数指定日志单词进行过滤。
-
统计日志级别出现次数:
- 统计每个日志级别(INFO, ERROR, WARN)出现的次数。
-
统计关键词出现次数:
- 实现一个功能,统计特定关键词在日志中出现的次数(例如,统计某个错误代码的出现次数)。
-
按日期过滤:
- 实现按时间范围过滤日志(例如筛选出特定日期范围的日志)。
package main
import (
"bufio"
"errors"
"fmt"
"os"
"strings"
"time"
)
func main() {
logFile := "test.log" // 这里是日志文件路径
var keyword, logGreep string
if len(os.Args) > 1 && os.Args[1] == "-p" {
startTime, endTime, err := getInputDate()
if err != nil {
fmt.Println(err)
return
}
filterLogsByDate(logFile, startTime, endTime)
return
}
// 检查命令行参数
for i := 1; i < len(os.Args); i++ {
switch os.Args[i] {
case "-l":
if i+1 < len(os.Args) && os.Args[i+1] != "-f" && os.Args[i+1] != "-k" { // 确保下一个参数不是-f或-k
logGreep = os.Args[i+1] // 从命令行获取过滤条件
i++ // 跳过下一个参数
} else {
fmt.Println("请提供过滤信息,例如: -l ERROR")
return
}
case "-f":
if i+1 < len(os.Args) && os.Args[i+1] != "-l" && os.Args[i+1] != "-k" { // 确保下一个参数不是-l或-k
logFile = os.Args[i+1] // 从命令行获取日志文件路径
i++ // 跳过下一个参数
} else {
fmt.Println("请提供日志文件路径,例如: -f test.log")
return
}
case "-k":
if i+1 < len(os.Args) && os.Args[i+1] != "-l" && os.Args[i+1] != "-f" { // 确保下一个参数不是-l或-f
// 处理-k参数的逻辑
keyword = os.Args[i+1] // 从命令行获取关键字
i++ // 跳过下一个参数
fmt.Println("关键字:", keyword) // 输出关键字
} else {
fmt.Println("请提供统计关键字,例如: -k keyword")
return
}
default:
fmt.Println("无效的参数,请使用 -l 过滤日志,-f 指定日志文件路径,-k 指定统计关键字")
return
}
}
file, err := os.Open(logFile)
if err != nil {
fmt.Fprintf(os.Stderr, "Error opening file: %v\n", err)
return
}
defer file.Close()
levelCounts := make(map[string]int)
keywordCount := 0
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text() // 从扫描器中读取当前行的文本
if logGreep != "" && strings.Contains(line, logGreep) {
fmt.Println(line)
}
if keyword != "" && strings.Contains(line, keyword) {
keywordCount++
}
logLevel := strings.Split(line, " ")[2]
levelCounts[logLevel]++
}
fmt.Println("日志统计结果:")
fmt.Println("--------------------------------")
fmt.Println("日志级别\t出现次数")
for level, count := range levelCounts {
fmt.Printf("%-10s\t%d\n", level, count)
}
if keyword != "" {
fmt.Println("--------------------------------")
fmt.Printf("关键字 %s 出现次数: %d\n", keyword, keywordCount)
}
if err := scanner.Err(); err != nil {
fmt.Println("Error reading file:", err)
}
}
func getInputDate() (time.Time, time.Time, error) {
var startDate, endDate string
fmt.Println("请输入开始日期(格式: 2025-01-01):")
fmt.Scanln(&startDate)
//格式判断
startTime, ok := isValidDate(startDate)
if !ok {
return time.Time{}, time.Time{}, errors.New("开始日期格式不正确")
}
fmt.Println("请输入结束日期(格式: 2025-01-01):")
fmt.Scanln(&endDate)
endTime, ok := isValidDate(endDate)
if !ok {
return time.Time{}, time.Time{}, errors.New("结束日期格式不正确")
}
return startTime, endTime, nil
}
func filterLogsByDate(logFile string, startTime, endTime time.Time) {
file, err := os.Open(logFile)
if err != nil {
fmt.Fprintf(os.Stderr, "Error opening file: %v\n", err)
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
fmt.Println("--------------------------------")
fmt.Println("过滤后的内容如下:")
for scanner.Scan() {
line := scanner.Text()
//日期判断,大于等于开始日期,小于等于结束日期,需要将日期转为time.Time类型
date := strings.Split(line, " ")[0]
dateTime, err := time.Parse("2006-01-02", date)
if err != nil {
fmt.Fprintf(os.Stderr, "Error parsing date: %v\n", err)
return
}
// 判断日期是否在指定范围内
if !dateTime.Before(startTime) && !dateTime.After(endTime) {
fmt.Println(line)
}
}
fmt.Println("--------------------------------")
}
func isValidDate(date string) (time.Time, bool) {
dateTime, err := time.Parse("2006-01-02", date)
if err != nil {
return time.Time{}, false
}
return dateTime, true
}
测试日志实例
2023-10-01 10:00:00 INFO Starting log analysis
2023-10-01 10:05:00 INFO Processing file: data1.csv
2023-10-01 10:10:00 ERROR File not found: data2.csv
2023-10-02 11:00:00 INFO Finished processing data1.csv
2023-10-02 11:05:00 INFO Starting new analysis
2023-10-03 12:00:00 INFO Processing file: data3.csv
2023-10-03 12:15:00 WARN Data inconsistency found in data3.csv
2023-10-04 13:00:00 INFO Finished processing data3.csv
2023-10-05 14:00:00 INFO Starting report generation
2023-10-06 15:00:00 INFO Report generated successfully
2023-10-07 16:00:00 INFO Sending report via email
2023-10-08 17:00:00 ERROR Email failed to send
2023-10-09 18:00:00 INFO Retrying email send
2023-10-10 19:00:00 INFO Email sent successfully