63 lines
1.6 KiB
Go
63 lines
1.6 KiB
Go
|
|
package main
|
||
|
|
|
||
|
|
import (
|
||
|
|
"bytes"
|
||
|
|
"encoding/json"
|
||
|
|
"net/http"
|
||
|
|
"os"
|
||
|
|
"strconv"
|
||
|
|
"strings"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
// usageJob is the Loki stream job label for homelab usage telemetry.
|
||
|
|
const usageJob = "homelab-usage"
|
||
|
|
|
||
|
|
// emitUsage best-effort records one verb invocation to Loki for cross-user
|
||
|
|
// usage analytics. Labels are low-cardinality (job/user/verb); the line carries
|
||
|
|
// only exit code + CLI version. NEVER args, paths, flags, or secrets. It must
|
||
|
|
// never affect the command: all errors are swallowed and a tight timeout bounds
|
||
|
|
// the cost. Opt out with HOMELAB_TELEMETRY=0.
|
||
|
|
func emitUsage(verb string, runErr error) {
|
||
|
|
switch os.Getenv("HOMELAB_TELEMETRY") {
|
||
|
|
case "0", "off", "false", "no":
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if verb == "" || strings.HasPrefix(verb, "usage") {
|
||
|
|
return // don't self-record the analytics reader
|
||
|
|
}
|
||
|
|
exit := 0
|
||
|
|
if runErr != nil {
|
||
|
|
exit = 1
|
||
|
|
}
|
||
|
|
body, err := json.Marshal(lokiPush{Streams: []lokiStream{{
|
||
|
|
Stream: map[string]string{"job": usageJob, "user": currentUser(), "verb": verb},
|
||
|
|
Values: [][2]string{{
|
||
|
|
strconv.FormatInt(time.Now().UnixNano(), 10),
|
||
|
|
"exit=" + strconv.Itoa(exit) + " ver=" + version,
|
||
|
|
}},
|
||
|
|
}}})
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
req, err := http.NewRequest("POST", "https://"+lokiHost+"/loki/api/v1/push", bytes.NewReader(body))
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
req.Header.Set("Content-Type", "application/json")
|
||
|
|
resp, err := clientDialingIP(internalLBIP, 800*time.Millisecond).Do(req)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
resp.Body.Close()
|
||
|
|
}
|
||
|
|
|
||
|
|
type lokiPush struct {
|
||
|
|
Streams []lokiStream `json:"streams"`
|
||
|
|
}
|
||
|
|
|
||
|
|
type lokiStream struct {
|
||
|
|
Stream map[string]string `json:"stream"`
|
||
|
|
Values [][2]string `json:"values"`
|
||
|
|
}
|