master
go 133 lines 2.89 KB
Raw
1 //go:build cgo
2
3 package as400
4
5 // SPDX-License-Identifier: GPL-3.0-or-later
6
7 import (
8 "encoding/json"
9 "fmt"
10 "maps"
11 "os"
12 "path/filepath"
13 "sync"
14 "time"
15 )
16
17 type dumpContext struct {
18 baseDir string
19 queriesDir string
20 rowsDir string
21 metricsDir string
22 metaDir string
23
24 mu sync.Mutex
25 seq int
26 metricSeq int
27 metadataWritten bool
28 }
29
30 func newDumpContext(baseDir string, cfg *Config) (*dumpContext, error) {
31 dirs := []string{
32 filepath.Join(baseDir, "queries"),
33 filepath.Join(baseDir, "rows"),
34 filepath.Join(baseDir, "metrics"),
35 filepath.Join(baseDir, "meta"),
36 }
37 for _, dir := range dirs {
38 if err := os.MkdirAll(dir, 0o755); err != nil {
39 return nil, fmt.Errorf("creating dump directory %s: %w", dir, err)
40 }
41 }
42
43 dc := &dumpContext{
44 baseDir: baseDir,
45 queriesDir: dirs[0],
46 rowsDir: dirs[1],
47 metricsDir: dirs[2],
48 metaDir: dirs[3],
49 }
50
51 if err := dc.writeMetadata(cfg); err != nil {
52 return nil, err
53 }
54
55 return dc, nil
56 }
57
58 func (d *dumpContext) writeMetadata(cfg *Config) error {
59 d.mu.Lock()
60 defer d.mu.Unlock()
61 if d.metadataWritten {
62 return nil
63 }
64 safeCfg := *cfg
65 safeCfg.Password = ""
66 payload := struct {
67 GeneratedAt time.Time `json:"generated_at"`
68 Config Config `json:"config"`
69 }{
70 GeneratedAt: time.Now(),
71 Config: safeCfg,
72 }
73 path := filepath.Join(d.metaDir, "config.json")
74 if err := writePrettyJSON(path, payload); err != nil {
75 return err
76 }
77 d.metadataWritten = true
78 return nil
79 }
80
81 func (d *dumpContext) recordQuery(query string, columns []string, rows [][]string) {
82 d.mu.Lock()
83 defer d.mu.Unlock()
84 d.seq++
85 name := fmt.Sprintf("query-%04d", d.seq)
86 _ = os.WriteFile(filepath.Join(d.queriesDir, name+".sql"), []byte(query), 0o644)
87
88 rowObjs := make([]map[string]string, 0, len(rows))
89 for _, row := range rows {
90 obj := make(map[string]string, len(columns))
91 for idx, col := range columns {
92 if idx < len(row) {
93 obj[col] = row[idx]
94 } else {
95 obj[col] = ""
96 }
97 }
98 rowObjs = append(rowObjs, obj)
99 }
100 payload := struct {
101 Columns []string `json:"columns"`
102 Rows []map[string]string `json:"rows"`
103 }{
104 Columns: columns,
105 Rows: rowObjs,
106 }
107 _ = writePrettyJSON(filepath.Join(d.rowsDir, name+".json"), payload)
108 }
109
110 func (d *dumpContext) recordMetrics(metrics map[string]int64) {
111 d.mu.Lock()
112 defer d.mu.Unlock()
113 d.metricSeq++
114 filename := fmt.Sprintf("metrics-%04d.json", d.metricSeq)
115 copyMetrics := make(map[string]int64, len(metrics))
116 maps.Copy(copyMetrics, metrics)
117 payload := struct {
118 GeneratedAt time.Time `json:"generated_at"`
119 Metrics map[string]int64 `json:"metrics"`
120 }{
121 GeneratedAt: time.Now(),
122 Metrics: copyMetrics,
123 }
124 _ = writePrettyJSON(filepath.Join(d.metricsDir, filename), payload)
125 }
126
127 func writePrettyJSON(path string, payload any) error {
128 data, err := json.MarshalIndent(payload, "", " ")
129 if err != nil {
130 return err
131 }
132 return os.WriteFile(path, data, 0o644)
133 }