go练习日志cli实现

152 阅读4分钟

实现一个 日志分析工具(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