Skip to content

Commit 45dd6b7

Browse files
authored
Introduce run metrics (#122)
1 parent 493f214 commit 45dd6b7

File tree

6 files changed

+128
-42
lines changed

6 files changed

+128
-42
lines changed

.golangci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,6 @@ linters:
4242
linters-settings:
4343
funlen:
4444
ignore-comments: true
45-
lines: 75
45+
lines: 80
4646
nestif:
4747
min-complexity: 5

internal/collection/collection.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package collection
33
import (
44
"archive/zip"
55
"bytes"
6+
"encoding/json"
67
"fmt"
78
"io"
89
"strings"
@@ -23,6 +24,7 @@ type Collection struct {
2324
JournalLoggingInterval string
2425
}
2526

27+
// New initializes new collection
2628
func New(w io.Writer) (c *Collection) {
2729
c = &Collection{
2830
Output: zip.NewWriter(w),
@@ -141,7 +143,22 @@ func (c *Collection) AddFileYAML(fileName string, data interface{}) {
141143
}
142144

143145
func (c *Collection) AddFileJSON(fileName string, data []byte) {
144-
c.AddFileDataRaw(fileName, data)
146+
var jsonData interface{}
147+
148+
err := json.Unmarshal(data, &jsonData)
149+
if err != nil {
150+
c.Log.Debugf("could not unmarshal JSON data for '%s': %s", fileName, err)
151+
}
152+
153+
prettyJSON, err := json.MarshalIndent(jsonData, "", "")
154+
if err != nil {
155+
c.Log.Debugf("could not marshal JSON data for '%s': %s", fileName, err)
156+
}
157+
158+
file := NewFile(fileName)
159+
file.Data = prettyJSON
160+
161+
_ = c.AddFileToOutput(file)
145162
}
146163

147164
func (c *Collection) AddFiles(prefix, source string) {

internal/metrics/metrics.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package metrics
2+
3+
import (
4+
"github.com/NETWAYS/support-collector/internal/obfuscate"
5+
"os"
6+
"strings"
7+
"time"
8+
)
9+
10+
type Metrics struct {
11+
Command string `json:"command"`
12+
Version string `json:"version"`
13+
Timings map[string]time.Duration `json:"timings"`
14+
}
15+
16+
// New creates new Metrics
17+
func New(version string) (m *Metrics) {
18+
return &Metrics{
19+
Command: getCommand(),
20+
Version: version,
21+
Timings: make(map[string]time.Duration),
22+
}
23+
}
24+
25+
// getCommand returns the executed command and obfusactes *--icinga2* arguments
26+
func getCommand() string {
27+
args := os.Args
28+
29+
// Obfuscate icinga 2 api user and password
30+
for i, arg := range args {
31+
if strings.Contains(arg, "--icinga2") && i+1 < len(args) {
32+
args[i+1] = obfuscate.Replacement
33+
}
34+
}
35+
36+
return strings.Join(args, " ")
37+
}

main.go

Lines changed: 62 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package main
22

33
import (
4+
"encoding/json"
45
"fmt"
6+
"github.com/NETWAYS/support-collector/internal/metrics"
57
"os"
68
"path/filepath"
79
"strings"
@@ -111,16 +113,31 @@ var (
111113
outputFile string
112114
commandTimeout = 60 * time.Second
113115
noDetailedCollection bool
116+
startTime = time.Now()
117+
metric *metrics.Metrics
114118
)
115119

116120
func main() {
117121
handleArguments()
118122

119-
// set locale to C, to avoid translations in command output
123+
// Set locale to C, to avoid translations in command output
120124
_ = os.Setenv("LANG", "C")
121125

122-
c, cleanup := NewCollection(outputFile)
123-
defer cleanup()
126+
c, closeCollection := NewCollection(outputFile)
127+
// Close collection
128+
defer closeCollection()
129+
130+
// Initialize new metrics and defer function to save it to json
131+
metric = metrics.New(getVersion())
132+
defer func() {
133+
// Save metrics to file
134+
body, err := json.Marshal(metric)
135+
if err != nil {
136+
c.Log.Warn("cant unmarshal metrics: %w", err)
137+
}
138+
139+
c.AddFileJSON("metrics.json", body)
140+
}()
124141

125142
if noDetailedCollection {
126143
c.Detailed = false
@@ -133,37 +150,16 @@ func main() {
133150
c.Log.Warn("This tool should be run as a privileged user (root) to collect all necessary information")
134151
}
135152

136-
var (
137-
startTime = time.Now()
138-
timings = map[string]time.Duration{}
139-
)
140-
141153
// Set command Timeout from argument
142154
c.ExecTimeout = commandTimeout
143155

144-
// Call all enabled modules
145-
for _, name := range moduleOrder {
146-
switch {
147-
case util.StringInSlice(name, disabledModules):
148-
c.Log.Debugf("Module %s is disabled", name)
149-
case !util.StringInSlice(name, enabledModules):
150-
c.Log.Debugf("Module %s is not enabled", name)
151-
default:
152-
moduleStart := time.Now()
156+
// Collect modules
157+
collectModules(c)
153158

154-
c.Log.Debugf("Start collecting data for module %s", name)
159+
// Save overall timing
160+
metric.Timings["total"] = time.Since(startTime)
155161

156-
for _, o := range extraObfuscators {
157-
c.Log.Debugf("Adding custom obfuscator for '%s' to module %s", o, name)
158-
c.RegisterObfuscator(obfuscate.NewAny(o))
159-
}
160-
161-
modules[name](c)
162-
163-
timings[name] = time.Since(moduleStart)
164-
c.Log.Debugf("Finished with module %s in %.3f seconds", name, timings[name].Seconds())
165-
}
166-
}
162+
c.Log.Infof("Collection complete, took us %.3f seconds", metric.Timings["total"].Seconds())
167163

168164
// Collect obfuscation info
169165
var files, count uint
@@ -178,12 +174,7 @@ func main() {
178174
c.Log.Infof("Obfuscation replaced %d token in %d files (%d definitions)", count, files, len(c.Obfuscators))
179175
}
180176

181-
// Save timings
182-
timings["total"] = time.Since(startTime)
183-
c.Log.Infof("Collection complete, took us %.3f seconds", timings["total"].Seconds())
184-
185-
c.AddFileYAML("timing.yml", timings)
186-
177+
// get absolute path of outputFile
187178
path, err := filepath.Abs(outputFile)
188179
if err != nil {
189180
c.Log.Debug(err)
@@ -224,7 +215,7 @@ func handleArguments() {
224215
flag.Parse()
225216

226217
if printVersion {
227-
fmt.Println(Product, "version", buildVersion()) //nolint:forbidigo
218+
fmt.Println(Product, "version", getBuildInfo()) //nolint:forbidigo
228219
os.Exit(0)
229220
}
230221

@@ -241,6 +232,9 @@ func buildFileName() string {
241232
return util.GetHostnameWithoutDomain() + "-" + FilePrefix + "-" + time.Now().Format("20060102-1504") + ".zip"
242233
}
243234

235+
// NewCollection starts a new collection. outputFile will be created.
236+
//
237+
// Collection and cleanup function to defer are returned
244238
func NewCollection(outputFile string) (*collection.Collection, func()) {
245239
file, err := os.Create(outputFile)
246240
if err != nil {
@@ -263,9 +257,8 @@ func NewCollection(outputFile string) (*collection.Collection, func()) {
263257
Level: consoleLevel,
264258
})
265259

266-
versionString := buildVersion()
260+
versionString := getBuildInfo()
267261
c.Log.Infof("Starting %s version %s", Product, versionString)
268-
c.AddFileDataRaw("version", []byte(versionString+"\n"))
269262

270263
return c, func() {
271264
// Close all open outputs in order, but only log errors
@@ -281,3 +274,34 @@ func NewCollection(outputFile string) (*collection.Collection, func()) {
281274
}
282275
}
283276
}
277+
278+
func collectModules(c *collection.Collection) {
279+
// Check if module is enabled / disabled and call it
280+
for _, name := range moduleOrder {
281+
switch {
282+
case util.StringInSlice(name, disabledModules):
283+
c.Log.Debugf("Module %s is disabled", name)
284+
case !util.StringInSlice(name, enabledModules):
285+
c.Log.Debugf("Module %s is not enabled", name)
286+
default:
287+
// Save current time
288+
moduleStart := time.Now()
289+
290+
c.Log.Debugf("Start collecting data for module %s", name)
291+
292+
// Register custom obfuscators
293+
for _, o := range extraObfuscators {
294+
c.Log.Debugf("Adding custom obfuscator for '%s' to module %s", o, name)
295+
c.RegisterObfuscator(obfuscate.NewAny(o))
296+
}
297+
298+
// Call collection function for module
299+
modules[name](c)
300+
301+
// Save runtime of module
302+
metric.Timings[name] = time.Since(moduleStart)
303+
304+
c.Log.Debugf("Finished with module %s in %.3f seconds", name, metric.Timings[name].Seconds())
305+
}
306+
}
307+
}

modules/icinga2/collector.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,12 @@ func Collect(c *collection.Collection) {
125125
}
126126

127127
// With Icinga 2 >= 2.14 the icinga2.debug cache is no longer built automatically on every reload. To retrieve a current state we build it manually (only possible from 2.14.0)
128+
// Needs to be done before commands are collected
128129
if icinga2version >= "2.14.0" {
129-
c.AddCommandOutput("dump-objects.txt", "icinga2", "daemon", "-C", "--dump-objects")
130+
_, err = collection.LoadCommandOutput("icinga2", "daemon", "-C", "--dump-objects")
131+
if err != nil {
132+
c.Log.Warn(err)
133+
}
130134
}
131135

132136
for name, cmd := range commands {

version.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ var (
1010
)
1111

1212
//goland:noinspection GoBoolExpressions
13-
func buildVersion() string {
13+
func getBuildInfo() string {
1414
result := version
1515

1616
if commit != "" {
@@ -23,3 +23,7 @@ func buildVersion() string {
2323

2424
return result
2525
}
26+
27+
func getVersion() string {
28+
return version
29+
}

0 commit comments

Comments
 (0)