]> de.git.xonotic.org Git - xonotic/xonstat.git/blob - xonstat/util/xs_parselog.go
Add a simple check to prevent most blank files.
[xonotic/xonstat.git] / xonstat / util / xs_parselog.go
1 package main
2
3 import "bufio"
4 import "flag"
5 import "fmt"
6 import "os"
7 import "strings"
8
9 /* xs_parselog parses a standard XonStat log and breaks it down into
10    individual requests which are then saved into files. The file format
11    is <game type>_<match_id>.txt, so one can easily identify which type
12    of request the file contains and which one it is (the match_id is the
13    'I' line in the actual request).
14
15    There is also the ability to extract a specific match as well as to
16    anonymize the hashkeys in the requests to prevent exposing player data
17    unnecessarily. */
18 func main() {
19         var fn = flag.String("file", "xonstat.log", "XonStat log file name")
20         var target_match_id = flag.String("match", "", "the specific match_id (I line) to extract from the log")
21         var anonymize = flag.Bool("anonymize", false, "whether or not to anonymize player hashkeys")
22         flag.Parse()
23
24         f, err := os.Open(*fn)
25         if err != nil {
26                 fmt.Println("Issue opening file")
27                 os.Exit(1)
28         }
29         defer f.Close()
30
31         r := bufio.NewReader(f)
32
33         var inreq bool
34         var match_id string
35         var game_type_cd string
36         lines := make([]string, 0, 100)
37         hashkeys := make(map[string]int, 500)
38         max_player_id := 0
39
40         line, err := r.ReadString('\n')
41         for err == nil {
42                 switch {
43                 case strings.Contains(line, "BEGIN REQUEST BODY"):
44                         inreq = true
45                 case strings.Contains(line, "END REQUEST BODY"):
46                         if *target_match_id == "" || match_id == *target_match_id {
47                                 // a simple heuristic to filter *most* empty games
48                                 if len(lines) >= 20 {
49                                         create_match_file(game_type_cd, match_id, lines)
50                                 }
51                         }
52                         inreq = false
53                         lines = make([]string, 0, 100)
54                 case inreq:
55                         if *anonymize && line[0] == 'P' {
56                                 if line[2:5] != "bot" && line[2:8] != "player" {
57                                         hashkey := line[2 : len(line)-1]
58                                         if _, ok := hashkeys[hashkey]; !ok {
59                                                 hashkeys[hashkey] = max_player_id
60                                                 max_player_id = max_player_id + 1
61                                         }
62                                         line = fmt.Sprintf("P %d\n", hashkeys[hashkey])
63                                 }
64                         }
65                         if line[0] == 'I' {
66                                 match_id = line[2 : len(line)-1]
67                         }
68                         if line[0] == 'G' {
69                                 game_type_cd = line[2 : len(line)-1]
70                         }
71                         lines = append(lines, line)
72                 }
73
74                 line, err = r.ReadString('\n')
75         }
76 }
77
78 func create_match_file(game_type_cd string, match_id string, body []string) {
79         fn := fmt.Sprintf("%s_%s.txt", game_type_cd, match_id)
80         f_raw, err := os.Create(fn)
81         if err != nil {
82                 fmt.Printf("Error creating match_id %s.\n", match_id)
83         }
84         defer f_raw.Close()
85
86         f := bufio.NewWriter(f_raw)
87         for _, v := range body {
88                 fmt.Fprint(f, v)
89         }
90         f.Flush()
91 }