<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          使用Go進(jìn)行日志分析并生成excel,再也不擔(dān)心做日志分析了

          共 10405字,需瀏覽 21分鐘

           ·

          2021-03-07 22:22

          前言

          工作的時(shí)候接到了一個(gè)需求,需要對(duì)一個(gè)日志文件進(jìn)行分析,分析請(qǐng)求次數(shù)以及耗費(fèi)的時(shí)間平均時(shí)間等信息,整理成excel表格,方便分析做優(yōu)化。剛拿到這個(gè)需求的時(shí)候,著實(shí)有點(diǎn)懵逼。那么多日志,我該怎么分析呢?該使用什么工具去分析呢。最后還要生成excel表格。哇,給我愁壞了。所以我開始并沒有直接去做需求,而是去查資料、問同事、朋友,怎么做日志分析。確實(shí)搜到了一些日志分析的方法:awk、python。無疑是用腳本來做。但是我對(duì)這些不太熟悉呀,而且只有一下午的時(shí)間去做。最后我選擇了使用golang來做。相比于其他,我對(duì)golang更熟悉。確定了語言,我就開始分析日志了,下面我就來詳細(xì)介紹一下我是怎么使用go完成的日志分析,并成功生成excel表格。

          代碼已上傳GitHub,可自行下載學(xué)習(xí)。傳送門

          前期準(zhǔn)備

          因?yàn)楣镜膌og不能在這里直接展示,所以本次教程我自己生成了幾個(gè)測(cè)試log。

          {"httpRequest":{"request":"method:post,path:/api/user/login"},"params":{"query":"username=asong&password=123456"},"timings":{"evalTotalTime":0.420787431}}
          {"httpRequest":{"request":"method:post,path:/api/user/login"},"params":{"query":"username=asong&password=123456"},"timings":{"evalTotalTime":0.420787431}}
          {"httpRequest":{"request":"method:post,path:/api/user/login"},"params":{"query":"username=asong&password=123456"},"timings":{"evalTotalTime":0.420787431}}
          {"httpRequest":{"request":"method:post,path:/api/user/login"},"params":{"query":"username=asong&password=123456"},"timings":{"evalTotalTime":0.420787431}}
          {"httpRequest":{"request":"method:post,path:/api/user/login"},"params":{"query":"username=asong&password=123456"},"timings":{"evalTotalTime":0.420787431}}
          {"httpRequest":{"request":"method:post,path:/api/user/login"},"params":{"query":"username=asong&password=123456"},"timings":{"evalTotalTime":0.420787431}}
          {"httpRequest":{"request":"method:post,path:/api/user/login"},"params":{"query":"username=asong&password=123456"},"timings":{"evalTotalTime":0.420787431}}
          {"httpRequest":{"request":"method:post,path:/api/user/login"},"params":{"query":"username=asong&password=123456"},"timings":{"evalTotalTime":0.420787431}}
          {"httpRequest":{"request":"method:post,path:/api/user/login"},"params":{"query":"username=asong&password=123456"},"timings":{"evalTotalTime":0.420787431}}
          {"httpRequest":{"request":"method:post,path:/api/user/login"},"params":{"query":"username=asong&password=123456"},"timings":{"evalTotalTime":0.420787431}}

          這些log正常都在一行的,因?yàn)閙arkdown顯示問題,顯示了多行。

          日志分析

          分析之前,先看一下我們的需求:分析每個(gè)請(qǐng)求的次數(shù),查詢參數(shù),平均時(shí)間。

          確定了需求,下面我們開始對(duì)日志進(jìn)行分析。每一行代表一個(gè)完整的日志請(qǐng)求。每一行日志都是一個(gè)json字符串,這樣看起來確實(shí)不方便,我們格式化一下來看一下。

          {
              "httpRequest":{
                  "request":"method:post,path:/api/user/login"
              },
              "params":{
                  "query":"username=asong&password=123456"
              },
              "timings":{
                  "evalTotalTime":0.420787431
              }
          }

          這樣看起來就很方便了,層次結(jié)構(gòu)一眼就能看出來。我們要統(tǒng)計(jì)請(qǐng)求的次數(shù),可以通過requrst這個(gè)字段判斷是否是同一個(gè)請(qǐng)求。query這個(gè)字段代表的是查詢參數(shù),evalTotalTime這個(gè)字段需要求和,然后求出平均數(shù)。日志分析好了,下面就是實(shí)現(xiàn)部分了。

          代碼實(shí)現(xiàn)

          代碼實(shí)現(xiàn)日志分析

          這里我使用一個(gè)map來存放不同的請(qǐng)求,以請(qǐng)求作為key,請(qǐng)求次數(shù)、時(shí)間等作為value,不過這里存的時(shí)間所有請(qǐng)求的時(shí)間和,統(tǒng)計(jì)好所有請(qǐng)求次數(shù)與時(shí)間和后再計(jì)算平均時(shí)間。這樣所有分析好的數(shù)據(jù)就都在map里了,最后可針對(duì)這個(gè)map進(jìn)行excel導(dǎo)出,是不是很完美,哈哈。

          • 定義map,需要統(tǒng)計(jì)的字段用struct封裝。
          var (
           result map[string]*requestBody
           analysis map[string]*requestBody
          )

          type requestBody struct {
           count int32
           query string
           time float64
          }
          • 因?yàn)槿罩疚募幸恍写硪粋€(gè)完整的日志,所以我們可以按行讀取日志,然后分析處理。
          func openFile() *os.File {
           file,err := os.Open("./request.log")
           if err != nil{
            log.Println("open log err: ",err)
           }
           return file
          }

          func logDeal(file *os.File)  {
           // 按行讀取
           br := bufio.NewReader(file)
           for{
            line,_,err := br.ReadLine()
            // file read complete
            if err == io.EOF{
             log.Println("file read complete")
             return
            }
            //json deal
            var data interface{}
            err = json.Unmarshal(line,&data)
            if err != nil{
             fmt.Errorf("json marshal error")
            }
            deal(data)
           }
          }
          • 按行讀取好數(shù)據(jù)后,開始對(duì)每一條日志進(jìn)行分析,提取字段??梢允褂胓olang的json.Unmarshal,配合類型斷言,分析出每一個(gè)字段做處理。
          func deal(data interface{})  {
           var request string
           var query string
           var time float64
           value,ok := data.(map[string]interface{})
           if ok{
            for k,v := range value{
             if k == "httpRequest"{
              switch v1 := v.(type) {
              case map[string]interface{}:
               for k1,v11 := range v1{
                if k1 == "request"{
                 switch val := v11.(type) {
                 case string:
                  request = val
                  //fmt.Println(request)
                 }
                }
               }
              }
             }
             if k == "params"{
              switch v1 := v.(type) {
              case map[string]interface{}:
               for k1,v11 := range v1{
                if k1 == "query"{
                 switch val := v11.(type) {
                 case string:
                  query = val
                  //fmt.Println(query)
                 }
                }
               }
              }
             }
             if k == "timings"{
              switch v1 := v.(type) {
              case map[string]interface{}:
               for k1,v11 := range v1{
                if k1 == "evalTotalTime"{
                 switch val := v11.(type) {
                 case float64:
                  time = val
                 // fmt.Println(time)
                 }
                }
               }
              }
             }
            }
            b := &requestBody{
             query: query,
             time: time,
            }
            if _,o := result[request];o{
             b.count = result[request].count + 1
             b.time = b.time + result[request].time
             result[request] = b
            }else {
             b.count = 1
             result[request] = b
            }
           }
          }
          • 統(tǒng)計(jì)好所有的請(qǐng)求次數(shù)與請(qǐng)求時(shí)間和后,我們還需要進(jìn)一步處理,得到每次請(qǐng)求的平均時(shí)間。
          //analysis data
          func analysisBody()  {
           for k,v := range result{
            req := &requestBody{}
            req.time = v.time / float64(v.count)
            req.count = v.count
            req.query = v.query
            analysis[k] = req
           }
          }

          分析好了日志后,下面我們開始導(dǎo)出excel。

          倒出excel文件

          這里使用的是excelize庫。首先進(jìn)行安裝:

          go get github.com/360EntSecGroup-Skylar/excelize

          excelize 詳細(xì)的文檔請(qǐng)點(diǎn)擊:https://xuri.me/excelize/zh-hans/。這里就不講解具體的使用方法了,直接上代碼了??梢酝扑]一個(gè)博客,我也是在這上面學(xué)習(xí)的。傳送門。這個(gè)庫還可以合并單元格,更多玩法,歡迎解鎖。

          導(dǎo)出代碼示例如下:

          type cellValue struct {
           sheet string
           cell string
           value string
          }
          //export excel
          func exportExcel()  {
           file := excelize.NewFile()
           //insert title
           cellValues := make([]*cellValue,0)
           cellValues = append(cellValues,&cellValue{
            sheet: "sheet1",
            cell: "A1",
            value: "request",
           },&cellValue{
            sheet: "sheet1",
            cell: "B1",
            value: "count",
           },&cellValue{
            sheet: "sheet1",
            cell: "C1",
            value: "query",
           },&cellValue{
            sheet: "sheet1",
            cell: "D1",
            value: "avgTime",
           })
           index := file.NewSheet("Sheet1")
           // 設(shè)置工作簿的默認(rèn)工作表
           file.SetActiveSheet(index)
           for _, cellValue := range cellValues {
            file.SetCellValue(cellValue.sheet, cellValue.cell, cellValue.value)
           }
           //insert data
           cnt := 1
           for k,v := range analysis{
            cnt = cnt + 1
            for k1,v1 := range cellValues{
             switch k1 {
             case 0:
              v1.cell = fmt.Sprintf("A%d",cnt)
              v1.value = k
             case 1:
              v1.cell = fmt.Sprintf("B%d",cnt)
              v1.value = fmt.Sprintf("%d",v.count)
             case 2:
              v1.cell = fmt.Sprintf("C%d",cnt)
              v1.value = v.query
             case 3:
              v1.cell = fmt.Sprintf("D%d",cnt)
              v1.value = strconv.FormatFloat(v.time,'f',-1,64)
             }
            }
            for _,vc := range cellValues{
             file.SetCellValue(vc.sheet,vc.cell,vc.value)
            }
           }

           //generate file
           err := file.SaveAs("./log.xlsx")
           if err != nil{
            fmt.Errorf("generate excel error")
           }
          }

          結(jié)果展示

          怎么樣,還可以吧,我們可以看到請(qǐng)求次數(shù)與平均時(shí)間,一目了然。

          總結(jié)

          我也是第一次使用go進(jìn)行日志分析??傮w來說還是挺方便的。最主要是導(dǎo)出excel真的很方便。你學(xué)會(huì)了嗎?



          推薦閱讀


          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù) ebook 獲??;還可以回復(fù)「進(jìn)群」,和數(shù)萬 Gopher 交流學(xué)習(xí)。

          瀏覽 25
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  精品成人久久久久久久 | 老太色HD色老太HD. | 欧美日韩性色情AⅤ在线一级二级 | 日逼网站免费 | 成人视频日本 |