Skip to content

Commit

Permalink
Merge pull request #39 from opsdis/dev
Browse files Browse the repository at this point in the history
String concatenation performance improvments
thenodon authored Oct 6, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
2 parents f671a6b + de926fb commit 1711116
Showing 4 changed files with 56 additions and 16 deletions.
8 changes: 5 additions & 3 deletions aci-connection.go
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ import (
"bytes"
"context"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/http/cookiejar"
"strconv"
@@ -192,7 +192,7 @@ func (c AciConnection) doGet(url string) ([]byte, int, error) {
defer resp.Body.Close()

if resp.StatusCode == http.StatusOK {
bodyBytes, err := ioutil.ReadAll(resp.Body)
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
log.WithFields(log.Fields{
"requestid": c.ctx.Value("requestid"),
@@ -224,6 +224,8 @@ func (c AciConnection) doPostXML(label string, url string, requestBody []byte) (

start := time.Now()
resp, err := c.Client.Do(req)
//cook := resp.Cookies()
//print(cook)
if err != nil {
log.WithFields(log.Fields{
"requestid": c.ctx.Value("requestid"),
@@ -252,7 +254,7 @@ func (c AciConnection) doPostXML(label string, url string, requestBody []byte) (
defer resp.Body.Close()

if resp.StatusCode == http.StatusOK {
bodyBytes, err := ioutil.ReadAll(resp.Body)
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
log.WithFields(log.Fields{
"requestid": c.ctx.Value("requestid"),
27 changes: 27 additions & 0 deletions aci-exporter.go
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ import (
"context"
"flag"
"fmt"
"net/http/pprof"
"os"
"strconv"
"strings"
@@ -71,6 +72,7 @@ func main() {
config := flag.String("config", viper.GetString("config"), "Set configuration file, default config.yaml")
usage := flag.Bool("u", false, "Show usage")
writeConfig := flag.Bool("default", false, "Write default config")
profiling := flag.Bool("pprof", false, "Enable profiling")

cli := flag.Bool("cli", false, "Run single query")
class := flag.String("class", viper.GetString("class"), "The class name - only cli")
@@ -195,6 +197,17 @@ func main() {
EnableOpenMetrics: true,
},
))
// profiling endpoint
if *profiling {
log.Info(fmt.Sprintf("Starting profiling endpoint on %s", viper.GetString("pport")))
mux := http.NewServeMux()
mux.HandleFunc("/debug/pprof/", pprof.Index)
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
go func() { log.Fatal(http.ListenAndServe(viper.GetString("pport"), mux)) }()
}

log.Info(fmt.Sprintf("%s starting on port %d", ExporterName, viper.GetInt("port")))
log.Info(fmt.Sprintf("Read timeout %s, Write timeout %s", viper.GetDuration("httpserver.read_timeout")*time.Second, viper.GetDuration("httpserver.write_timeout")*time.Second))
@@ -283,15 +296,29 @@ func (h HandlerInit) getMonitorMetrics(w http.ResponseWriter, r *http.Request) {
ctx = context.WithValue(ctx, "fabric", fabric)
api := *newAciAPI(ctx, h.AllFabrics[fabric], h.AllQueries, queries)

start := time.Now()
aciName, metrics, err := api.CollectMetrics()
log.WithFields(log.Fields{
"requestid": ctx.Value("requestid"),
"exec_time": time.Since(start).Microseconds(),
"fabric": fmt.Sprintf("%v", ctx.Value("fabric")),
}).Info("total query collection time")

commonLabels := make(map[string]string)
commonLabels["aci"] = aciName
commonLabels["fabric"] = fabric

start = time.Now()
metricsFormat := NewMetricFormat(openmetrics, viper.GetBool("metric_format.label_key_to_lower_case"),
viper.GetBool("metric_format.label_key_to_snake_case"))
var bodyText = Metrics2Prometheus(metrics, api.metricPrefix, commonLabels, metricsFormat)

log.WithFields(log.Fields{
"requestid": ctx.Value("requestid"),
"exec_time": time.Since(start).Microseconds(),
"fabric": fmt.Sprintf("%v", ctx.Value("fabric")),
}).Info("processing metrics to prometheus exposition format")

if openmetrics {
w.Header().Set("Content-Type", "application/openmetrics-text; version=0.0.1; charset=utf-8")
} else {
2 changes: 2 additions & 0 deletions defaults.go
Original file line number Diff line number Diff line change
@@ -53,6 +53,8 @@ func SetDefaultValues() {
viper.BindEnv("config")
viper.SetDefault("prefix", "aci_")
viper.BindEnv("prefix")
viper.SetDefault("pport", "localhost:6060")
viper.BindEnv("pport")

// If set to true response will always be in openmetrics format
viper.SetDefault("openmetrics", false)
35 changes: 22 additions & 13 deletions metric.go
Original file line number Diff line number Diff line change
@@ -18,6 +18,8 @@ import (
"regexp"
"sort"
"strings"

log "github.com/sirupsen/logrus"
)

type MetricDefinition struct {
@@ -85,21 +87,21 @@ func (m Metric) Labels2Prometheus(commonLabels map[string]string, format MetricF

sort.Strings(keys)

labelstr := ""
var builder strings.Builder
sep := ""
for _, k := range keys {
// Filter out empty labels
if m.Labels[k] != "" {
labelstr = labelstr + fmt.Sprintf("%s%s=\"%s\"", sep, toLowerLabels(k, format), m.Labels[k])
addText(&builder, fmt.Sprintf("%s%s=\"%s\"", sep, toLowerLabels(k, format), m.Labels[k]))
sep = ","
}
}
return labelstr
return builder.String()
}

// Metrics2Prometheus convert a slice of Metric to Prometheus text output
func Metrics2Prometheus(metrics []MetricDefinition, prefix string, commonLabels map[string]string, format MetricFormat) string {
promFormat := ""
var builder strings.Builder

for _, metricDefinition := range metrics {

@@ -115,9 +117,9 @@ func Metrics2Prometheus(metrics []MetricDefinition, prefix string, commonLabels

if len(metricDefinition.Metrics) > 0 {
if metricDefinition.Description.Help == "" {
promFormat = promFormat + fmt.Sprintf("# HELP %s%s %s\n", prefix, metricName, "Missing description")
addText(&builder, fmt.Sprintf("# HELP %s%s %s\n", prefix, metricName, "Missing description"))
} else {
promFormat = promFormat + fmt.Sprintf("# HELP %s%s %s\n", prefix, metricName, metricDefinition.Description.Help)
addText(&builder, fmt.Sprintf("# HELP %s%s %s\n", prefix, metricName, metricDefinition.Description.Help))
}

promType := "gauge"
@@ -126,22 +128,29 @@ func Metrics2Prometheus(metrics []MetricDefinition, prefix string, commonLabels
}
if format.openmetrics {
if strings.HasSuffix(metricName, "_info") {
promFormat = promFormat + fmt.Sprintf("# TYPE %s%s %s\n", prefix, metricName, "info")
addText(&builder, fmt.Sprintf("# TYPE %s%s %s\n", prefix, metricName, "info"))
} else {
promFormat = promFormat + fmt.Sprintf("# TYPE %s%s %s\n", prefix, metricName, promType)
addText(&builder, fmt.Sprintf("# TYPE %s%s %s\n", prefix, metricName, promType))
}
promFormat = promFormat + fmt.Sprintf("# UNIT %s%s %s\n", prefix, metricName, metricDefinition.Description.Unit)
addText(&builder, fmt.Sprintf("# UNIT %s%s %s\n", prefix, metricName, metricDefinition.Description.Unit))
} else {
promFormat = promFormat + fmt.Sprintf("# TYPE %s%s %s\n", prefix, metricName, promType)
addText(&builder, fmt.Sprintf("# TYPE %s%s %s\n", prefix, metricName, promType))
}

for _, metric := range metricDefinition.Metrics {
promFormat = promFormat + fmt.Sprintf("%s%s{%s} %g\n", prefix, metricName, metric.Labels2Prometheus(commonLabels, format), metric.Value)
addText(&builder, fmt.Sprintf("%s%s{%s} %g\n", prefix, metricName, metric.Labels2Prometheus(commonLabels, format), metric.Value))
}
}
}
if format.openmetrics {
promFormat = promFormat + "# EOF\n"
addText(&builder, "# EOF\n")
}
return builder.String()
}

func addText(builder *strings.Builder, text string) {
_, err := builder.WriteString(text)
if err != nil {
log.Fatal(err)
}
return promFormat
}

0 comments on commit 1711116

Please sign in to comment.