diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 48e12426..e0bc49d2 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -20,7 +20,7 @@ jobs: steps: - uses: actions/setup-go@v5 with: - go-version: '>=1.20' + go-version: '>=1.24' - uses: actions/checkout@v4 diff --git a/buckets.go b/buckets.go index 610e3b87..30cfbb8d 100644 --- a/buckets.go +++ b/buckets.go @@ -39,5 +39,5 @@ func splitMeasureField(s string) (measure, field string) { } else { field = s } - return + return measure, field } diff --git a/cmd/dogstatsd/main.go b/cmd/dogstatsd/main.go index 9ad82f9d..1de9d785 100644 --- a/cmd/dogstatsd/main.go +++ b/cmd/dogstatsd/main.go @@ -148,7 +148,7 @@ func split(args []string, sep string) (head, tail []string) { } else { head, tail = args[:i], args[i+1:] } - return + return head, tail } func indexOf(args []string, s string) int { @@ -187,5 +187,5 @@ func (tags *tags) Set(s string) (err error) { } *tags = append(*tags, tag) } - return + return err } diff --git a/datadog/append_test.go b/datadog/append_test.go index 8f6fe578..b9dc0b55 100644 --- a/datadog/append_test.go +++ b/datadog/append_test.go @@ -17,7 +17,7 @@ func BenchmarkAppendMetric(b *testing.B) { for _, test := range testMetrics { b.Run(test.m.Name, func(b *testing.B) { - for i := 0; i != b.N; i++ { + for b.Loop() { appendMetric(buffer[:0], test.m) } }) diff --git a/datadog/client.go b/datadog/client.go index 3431ae19..41b96f41 100644 --- a/datadog/client.go +++ b/datadog/client.go @@ -162,7 +162,7 @@ func bufSizeFromFD(f *os.File, sizehint int) (bufsize int, err error) { // to accept larger datagrams, or fallback to the default socket buffer size // if it failed. if bufsize, err = unix.GetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_SNDBUF); err != nil { - return + return bufsize, err } // The kernel applies a 2x factor on the socket buffer size, only half of it @@ -197,7 +197,7 @@ func bufSizeFromFD(f *os.File, sizehint int) (bufsize int, err error) { // Creating the file put the socket in blocking mode, reverting. _ = unix.SetNonblock(fd, true) - return + return bufsize, err } type ddWriter interface { diff --git a/datadog/parse.go b/datadog/parse.go index b2a5a70d..70363ab0 100644 --- a/datadog/parse.go +++ b/datadog/parse.go @@ -20,7 +20,7 @@ func parseEvent(s string) (e Event, err error) { header, next = nextToken(next, ':') if len(header) < 7 { err = fmt.Errorf("datadog: %#v has a malformed event header", s) - return + return e, err } header = header[3 : len(header)-1] // Strip off '_e{' and '}' @@ -30,13 +30,13 @@ func parseEvent(s string) (e Event, err error) { titleLen, err = strconv.ParseInt(rawTitleLen, 10, 64) if err != nil { err = fmt.Errorf("datadog: %#v has a malformed title length", s) - return + return e, err } textLen, err = strconv.ParseInt(rawTextLen, 10, 64) if err != nil { err = fmt.Errorf("datadog: %#v has a malformed text length", s) - return + return e, err } rawTitle := next[:titleLen] @@ -45,12 +45,12 @@ func parseEvent(s string) (e Event, err error) { if len(rawTitle) == 0 { err = fmt.Errorf("datadog: %#v has a malformed title", s) - return + return e, err } if len(rawText) == 0 { err = fmt.Errorf("datadog: %#v has malformed text", s) - return + return e, err } e = Event{ @@ -72,7 +72,7 @@ func parseEvent(s string) (e Event, err error) { ts, err = strconv.ParseInt(rawMetadataFields[i][2:], 10, 64) if err != nil { err = fmt.Errorf("datadog: %#v has a malformed timestamp", s) - return + return e, err } e.Ts = ts case 'p': @@ -89,7 +89,7 @@ func parseEvent(s string) (e Event, err error) { tags = rawMetadataFields[i][1:] default: err = fmt.Errorf("datadog: %#v has unexpected metadata field", s) - return + return e, err } } } @@ -107,7 +107,7 @@ func parseEvent(s string) (e Event, err error) { } } - return + return e, err } func parseMetric(s string) (m Metric, err error) { @@ -125,17 +125,17 @@ func parseMetric(s string) (m Metric, err error) { if len(name) == 0 { err = fmt.Errorf("datadog: %#v is missing a metric name", s) - return + return m, err } if len(val) == 0 { err = fmt.Errorf("datadog: %#v is missing a metric value", s) - return + return m, err } if len(typ) == 0 { err = fmt.Errorf("datadog: %#v is missing a metric type", s) - return + return m, err } if len(rate) != 0 { @@ -146,7 +146,7 @@ func parseMetric(s string) (m Metric, err error) { rate = rate[1:] default: err = fmt.Errorf("datadog: %#v has a malformed sample rate", s) - return + return m, err } } @@ -156,7 +156,7 @@ func parseMetric(s string) (m Metric, err error) { tags = tags[1:] default: err = fmt.Errorf("datadog: %#v has malformed tags", s) - return + return m, err } } @@ -165,13 +165,13 @@ func parseMetric(s string) (m Metric, err error) { if value, err = strconv.ParseFloat(val, 64); err != nil { err = fmt.Errorf("datadog: %#v has a malformed value", s) - return + return m, err } if len(rate) != 0 { if sampleRate, err = strconv.ParseFloat(rate, 64); err != nil { err = fmt.Errorf("datadog: %#v has a malformed sample rate", s) - return + return m, err } } @@ -199,7 +199,7 @@ func parseMetric(s string) (m Metric, err error) { } } - return + return m, err } func nextToken(s string, b byte) (token, next string) { @@ -208,7 +208,7 @@ func nextToken(s string, b byte) (token, next string) { } else { token = s } - return + return token, next } func split(s string, b byte) (head, tail string) { @@ -217,7 +217,7 @@ func split(s string, b byte) (head, tail string) { } else { head = s } - return + return head, tail } func count(s string, b byte) (n int) { @@ -231,5 +231,5 @@ func count(s string, b byte) (n int) { s = s[off+1:] } - return + return n } diff --git a/datadog/parse_test.go b/datadog/parse_test.go index bc4eff4e..ea598b9e 100644 --- a/datadog/parse_test.go +++ b/datadog/parse_test.go @@ -42,7 +42,7 @@ func TestParseMetricFailure(t *testing.T) { func BenchmarkParseMetric(b *testing.B) { for _, test := range testMetrics { b.Run(test.m.Name, func(b *testing.B) { - for i := 0; i != b.N; i++ { + for b.Loop() { parseMetric(test.s) } }) @@ -64,7 +64,7 @@ func TestParseEventSuccess(t *testing.T) { func BenchmarkParseEvent(b *testing.B) { for _, test := range testEvents { b.Run(test.e.Title, func(b *testing.B) { - for i := 0; i != b.N; i++ { + for b.Loop() { parseEvent(test.s) } }) diff --git a/datadog/serializer.go b/datadog/serializer.go index 00c90f8c..ba22cfc6 100644 --- a/datadog/serializer.go +++ b/datadog/serializer.go @@ -223,87 +223,47 @@ func isTrim(b byte) bool { return b == '.' || b == '_' || b == '-' } // appendSanitizedMetricName converts *any* string into something that StatsD / Graphite // accepts without complaints. func appendSanitizedMetricName(dst []byte, raw string) []byte { - nameLen := 0 - orig := len(dst) + origLen := len(dst) if raw == "" { if len(dst) == 0 { return append(dst, "_unnamed_"...) } return dst } - // ── 1. accent folding (creates one temporary ↴) - // tmp := stripUnicodeAccents([]byte(raw)) - // ── 2. run the same ASCII sanitizer, but write into dst + // Simple transformation: iterate through runes and convert/replace as needed lastWasRepl := false - for i := 0; i < len(raw); i++ { - c := byte(raw[i]) + for i, r := range raw { + if i >= maxLen { + break + } - if c < 128 && valid[c] { - // ASCII valid chars - dst = append(dst, c) - nameLen++ + if r < utf8.RuneSelf && valid[byte(r)] { + // Valid ASCII character + dst = append(dst, byte(r)) lastWasRepl = false - } else if c >= 0xC2 && c <= 0xC3 && i+1 < len(raw) { - // Check for 2-byte UTF-8 sequences that are common accented letters - c2 := byte(raw[i+1]) - if c2 >= 0x80 && c2 <= 0xBF { // Valid second byte - // Decode the 2-byte sequence - codepoint := uint16(c&0x1F)<<6 | uint16(c2&0x3F) - - // Map common accented characters (U+00C0-U+00FF range) - if codepoint >= 0xC0 && codepoint <= 0xFF { - mapped := latin1SupplementMap[codepoint] - if valid[mapped] { - dst = append(dst, mapped) - nameLen++ - lastWasRepl = false - i++ // Skip the second byte - continue - } - } - } - // If we get here, treat as invalid - if !lastWasRepl { - dst = append(dst, replacement) - nameLen++ - lastWasRepl = true - } - } else { - // Everything else (3-byte, 4-byte sequences, invalid chars) - // Skip continuation bytes (0x80-0xBF) to avoid creating invalid UTF-8 - for i+1 < len(raw) && (raw[i+1]&0xC0) == 0x80 { - i++ - } - if !lastWasRepl { + } else if r >= 0xC0 && r <= 0xFF { + // Latin-1 Supplement block (common accented characters like À, É, ñ) + mapped := latin1SupplementMap[r] + if valid[mapped] { + dst = append(dst, mapped) + lastWasRepl = false + } else if !lastWasRepl { dst = append(dst, replacement) - nameLen++ lastWasRepl = true } - } - - if nameLen >= maxLen { - break + } else if !lastWasRepl { + // Invalid or unsupported character - only append if we didn't just add a replacement + dst = append(dst, replacement) + lastWasRepl = true } } - // 3. trim leading / trailing '.', '_' or '-' - start, end := orig, len(dst) - for start < end && isTrim(dst[start]) { - start++ - } - for end > start && isTrim(dst[end-1]) { - end-- - } - - // 4. compact if we trimmed something - if start > orig || end < len(dst) { - copy(dst[orig:], dst[start:end]) - dst = dst[:orig+(end-start)] - } + // Trim leading/trailing '.', '_' or '-' + trimmed := bytes.Trim(dst[origLen:], "._-") + dst = append(dst[:origLen], trimmed...) - // 5. fallback if everything vanished - if len(dst) == orig { + if len(dst) == origLen { return append(dst, "_truncated_"...) } return dst diff --git a/datadog/serializer_test.go b/datadog/serializer_test.go index 1975d4b2..5fd801e0 100644 --- a/datadog/serializer_test.go +++ b/datadog/serializer_test.go @@ -331,6 +331,18 @@ func TestAppendSanitizedMetricName(t *testing.T) { {"prefix_", "", "prefix_"}, {"prefix_", "!!!", "prefix__truncated_"}, + // only trim characters (dots, underscores, dashes) - fast path edge case + {"", "...", "_truncated_"}, + {"", "___", "_truncated_"}, + {"", "---", "_truncated_"}, + {"", "._-._-", "_truncated_"}, + {"", "......", "_truncated_"}, + {"", "______", "_truncated_"}, + {"", "------", "_truncated_"}, + {"prefix_", "...", "prefix__truncated_"}, + {"prefix_", "___", "prefix__truncated_"}, + {"prefix_", "._-", "prefix__truncated_"}, + // over-long → truncated (but preserve prefix if it fits) {"", long, strings.Repeat("x", maxLen)}, {"short_", long, "short_" + strings.Repeat("x", maxLen)}, // 6 = len("short_") @@ -524,7 +536,7 @@ func BenchmarkAppendSanitizedMetricName(b *testing.B) { b.Run(bm.name, func(b *testing.B) { buf := make([]byte, 0, 512) b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { buf = appendSanitizedMetricName(buf[:0], bm.input) } }) @@ -534,10 +546,76 @@ func BenchmarkAppendSanitizedMetricName(b *testing.B) { b.Run("with prefix", func(b *testing.B) { buf := make([]byte, 0, 512) b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { buf = buf[:0] buf = append(buf, "myapp."...) buf = appendSanitizedMetricName(buf, "café.metrics") } }) } + +// BenchmarkTrimComparison compares manual trimming vs bytes.TrimFunc. +func BenchmarkTrimComparison(b *testing.B) { + testCases := []struct { + name string + prefix string + input string + }{ + {"no trim needed", "prefix.", "simple.metric"}, + {"trim leading", "prefix.", "...trimmed"}, + {"trim trailing", "prefix.", "trimmed..."}, + {"trim both", "prefix.", "...trimmed..."}, + {"trim all", "prefix.", "......"}, + {"mixed trim chars", "prefix.", "._-test._-"}, + {"typical metric", "myapp.", "http.server.duration"}, + } + + for _, tc := range testCases { + // Benchmark current manual approach + b.Run(tc.name+"/manual", func(b *testing.B) { + buf := make([]byte, 0, 512) + b.ResetTimer() + for b.Loop() { + buf = buf[:0] + buf = append(buf, tc.prefix...) + origLen := len(buf) + + // Copy input + buf = append(buf, tc.input...) + + // Manual trim (current approach) + start, end := origLen, len(buf) + for start < end && isTrim(buf[start]) { + start++ + } + for end > start && isTrim(buf[end-1]) { + end-- + } + + if start > origLen || end < len(buf) { + copy(buf[origLen:], buf[start:end]) + buf = buf[:origLen+(end-start)] + } + } + }) + + // Benchmark bytes.Trim* approach + b.Run(tc.name+"/bytesTrim", func(b *testing.B) { + buf := make([]byte, 0, 512) + cutset := "._-" + b.ResetTimer() + for b.Loop() { + buf = buf[:0] + buf = append(buf, tc.prefix...) + origLen := len(buf) + + // Copy input + buf = append(buf, tc.input...) + + // bytes.Trim approach (TrimLeft then TrimRight) + trimmed := bytes.Trim(buf[origLen:], cutset) + buf = append(buf[:origLen], trimmed...) + } + }) + } +} diff --git a/datadog/server.go b/datadog/server.go index 8be04fd2..026a6577 100644 --- a/datadog/server.go +++ b/datadog/server.go @@ -41,11 +41,11 @@ func ListenAndServe(addr string, handler Handler) (err error) { var conn net.PacketConn if conn, err = net.ListenPacket("udp", addr); err != nil { - return + return err } err = Serve(conn, handler) - return + return err } // Serve runs a dogstatsd server, listening for datagrams on conn and forwarding diff --git a/engine.go b/engine.go index 61d0b97d..fb2d3d2e 100644 --- a/engine.go +++ b/engine.go @@ -364,5 +364,5 @@ func progname() (name string) { if args := os.Args; len(args) != 0 { name = filepath.Base(args[0]) } - return + return name } diff --git a/field_test.go b/field_test.go index 1a395e3a..0fdd7388 100644 --- a/field_test.go +++ b/field_test.go @@ -19,7 +19,7 @@ func BenchmarkAssign40BytesStruct(b *testing.B) { var s S - for i := 0; i != b.N; i++ { + for b.Loop() { s = S{a: "hello", b: "", c: 0} _ = s } @@ -33,7 +33,7 @@ func BenchmarkAssign32BytesStruct(b *testing.B) { var s S - for i := 0; i != b.N; i++ { + for b.Loop() { s = S{a: "hello", b: ""} _ = s } diff --git a/go.mod b/go.mod index 227de496..11f0d72c 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/segmentio/stats/v5 -go 1.23.0 +go 1.24.0 require ( github.com/mdlayher/taskstats v0.0.0-20241219020249-a291fa5f5a69 diff --git a/httpstats/handler.go b/httpstats/handler.go index 960d9dc3..e26ca4ea 100644 --- a/httpstats/handler.go +++ b/httpstats/handler.go @@ -85,7 +85,7 @@ func (w *responseWriter) Write(b []byte) (n int, err error) { w.bytes += n } - return + return n, err } func (w *responseWriter) Hijack() (conn net.Conn, buf *bufio.ReadWriter, err error) { @@ -93,7 +93,7 @@ func (w *responseWriter) Hijack() (conn net.Conn, buf *bufio.ReadWriter, err err w.wroteHeader = true w.complete() } - return + return conn, buf, err } func (w *responseWriter) complete() { diff --git a/httpstats/metrics.go b/httpstats/metrics.go index 2af6fd73..a34d6b89 100644 --- a/httpstats/metrics.go +++ b/httpstats/metrics.go @@ -73,14 +73,14 @@ type requestBody struct { func (r *requestBody) Close() (err error) { err = r.body.Close() r.close() - return + return err } func (r *requestBody) Read(b []byte) (n int, err error) { if n, err = r.body.Read(b); n > 0 { r.bytes += n } - return + return n, err } func (r *requestBody) close() { @@ -105,14 +105,14 @@ type responseBody struct { func (r *responseBody) Close() (err error) { err = r.body.Close() r.close() - return + return err } func (r *responseBody) Read(b []byte) (n int, err error) { if n, err = r.body.Read(b); n > 0 { r.bytes += n } - return + return n, err } func (r *responseBody) close() { @@ -322,7 +322,7 @@ func requestHost(req *http.Request) (host string) { host = req.URL.Host } } - return + return host } func responseStatusBucket(status int) string { @@ -375,7 +375,7 @@ func parseContentType(s string) (contentType, charset string) { contentType = t } } - return + return contentType, charset } func parseHeaderToken(s string) (token, next string) { @@ -384,7 +384,7 @@ func parseHeaderToken(s string) (token, next string) { } else { token = strings.TrimSpace(s) } - return + return token, next } // headerValues is equivalent to http.Header.Get but assumes that the keys and diff --git a/httpstats/transport.go b/httpstats/transport.go index 9663f628..324057e3 100644 --- a/httpstats/transport.go +++ b/httpstats/transport.go @@ -62,7 +62,7 @@ func (t *transport) RoundTrip(req *http.Request) (res *http.Response, err error) if err != nil { m.observeError(time.Since(start)) eng.ReportAt(start, m) - return + return res, err } res.Body = &responseBody{ @@ -74,5 +74,5 @@ func (t *transport) RoundTrip(req *http.Request) (res *http.Response, err error) start: start, } - return + return res, err } diff --git a/influxdb/client.go b/influxdb/client.go index bb2ee3cb..d5b509f7 100644 --- a/influxdb/client.go +++ b/influxdb/client.go @@ -161,7 +161,7 @@ func (s *serializer) Write(b []byte) (n int, err error) { case <-time.After(s.http.Timeout): case <-s.done: err = context.Canceled - return + return n, err } } @@ -181,7 +181,7 @@ func (s *serializer) Write(b []byte) (n int, err error) { } n = len(b) - return + return n, err } func makeURL(address, database string) *url.URL { diff --git a/iostats/io.go b/iostats/io.go index e8f52959..bf2584cd 100644 --- a/iostats/io.go +++ b/iostats/io.go @@ -14,7 +14,7 @@ func (r *CountReader) Read(b []byte) (n int, err error) { if n, err = r.R.Read(b); n > 0 { r.N += n } - return + return n, err } // CountWriter is an io.Writer that counts how many bytes are written by calls @@ -29,7 +29,7 @@ func (w *CountWriter) Write(b []byte) (n int, err error) { if n, err = w.W.Write(b); n > 0 { w.N += n } - return + return n, err } // ReaderFunc makes it possible for function types to be used as io.Reader. diff --git a/netstats/conn.go b/netstats/conn.go index 30f28f8f..e6573667 100644 --- a/netstats/conn.go +++ b/netstats/conn.go @@ -91,7 +91,7 @@ func (c *conn) Close() (err error) { c.error("close", err) } }) - return + return err } func (c *conn) Read(b []byte) (n int, err error) { @@ -113,7 +113,7 @@ func (c *conn) Read(b []byte) (n int, err error) { c.error("read", err) } - return + return n, err } func (c *conn) Write(b []byte) (n int, err error) { @@ -135,28 +135,28 @@ func (c *conn) Write(b []byte) (n int, err error) { c.error("write", err) } - return + return n, err } func (c *conn) SetDeadline(t time.Time) (err error) { if err = c.Conn.SetDeadline(t); err != nil { c.error("set-deadline", err) } - return + return err } func (c *conn) SetReadDeadline(t time.Time) (err error) { if err = c.Conn.SetReadDeadline(t); err != nil { c.error("set-read-deadline", err) } - return + return err } func (c *conn) SetWriteDeadline(t time.Time) (err error) { if err = c.Conn.SetWriteDeadline(t); err != nil { c.error("set-write-deadline", err) } - return + return err } func (c *conn) error(op string, err error) { diff --git a/netstats/listener.go b/netstats/listener.go index 876f0dd7..8050bb45 100644 --- a/netstats/listener.go +++ b/netstats/listener.go @@ -37,7 +37,7 @@ func (l *listener) Accept() (conn net.Conn, err error) { conn = NewConnWith(l.eng, conn) } - return + return conn, err } func (l *listener) Close() (err error) { diff --git a/procstats/error.go b/procstats/error.go index 771aac8d..a163331d 100644 --- a/procstats/error.go +++ b/procstats/error.go @@ -11,7 +11,7 @@ func convertPanicToError(v interface{}) (err error) { err = fmt.Errorf("%v", e) } } - return + return err } func check(err error) { diff --git a/procstats/go.go b/procstats/go.go index 2378fc65..de4d93ad 100644 --- a/procstats/go.go +++ b/procstats/go.go @@ -232,7 +232,7 @@ func makePauses(head, tail []uint64) (pauses []time.Duration) { pauses = make([]time.Duration, 0, len(head)+len(tail)) pauses = appendPauses(pauses, head) pauses = appendPauses(pauses, tail) - return + return pauses } func appendPauses(pauses []time.Duration, values []uint64) []time.Duration { diff --git a/procstats/linux/cgroup.go b/procstats/linux/cgroup.go index 223039f7..42cc2325 100644 --- a/procstats/linux/cgroup.go +++ b/procstats/linux/cgroup.go @@ -27,7 +27,7 @@ func (pcg ProcCGroup) Lookup(name string) (cgroup CGroup, ok bool) { }) } }) - return + return cgroup, ok } // ReadProcCGroup takes an int argument representing a PID @@ -35,14 +35,14 @@ func (pcg ProcCGroup) Lookup(name string) (cgroup CGroup, ok bool) { func ReadProcCGroup(pid int) (proc ProcCGroup, err error) { defer func() { err = convertPanicToError(recover()) }() proc = parseProcCGroup(readProcFile(pid, "cgroup")) - return + return proc, err } // ParseProcCGroup parses Linux system cgroup data and returns a ProcCGroup and error, if any is encountered. func ParseProcCGroup(s string) (proc ProcCGroup, err error) { defer func() { err = convertPanicToError(recover()) }() proc = parseProcCGroup(s) - return + return proc, err } func parseProcCGroup(s string) (proc ProcCGroup) { @@ -62,7 +62,7 @@ func parseProcCGroup(s string) (proc ProcCGroup) { name = next } }) - return + return proc } // ReadCPUPeriod takes a string representing a Linux cgroup and returns @@ -70,7 +70,7 @@ func parseProcCGroup(s string) (proc ProcCGroup) { func ReadCPUPeriod(cgroup string) (period time.Duration, err error) { defer func() { err = convertPanicToError(recover()) }() period = readCPUPeriod(cgroup) - return + return period, err } // ReadCPUQuota takes a string representing a Linux cgroup and returns @@ -78,7 +78,7 @@ func ReadCPUPeriod(cgroup string) (period time.Duration, err error) { func ReadCPUQuota(cgroup string) (quota time.Duration, err error) { defer func() { err = convertPanicToError(recover()) }() quota = readCPUQuota(cgroup) - return + return quota, err } // ReadCPUShares takes a string representing a Linux cgroup and returns @@ -86,7 +86,7 @@ func ReadCPUQuota(cgroup string) (quota time.Duration, err error) { func ReadCPUShares(cgroup string) (shares int64, err error) { defer func() { err = convertPanicToError(recover()) }() shares = readCPUShares(cgroup) - return + return shares, err } func readCPUPeriod(cgroup string) time.Duration { diff --git a/procstats/linux/cgroup_linux_test.go b/procstats/linux/cgroup_linux_test.go index 2a7eccca..eceb6ad7 100644 --- a/procstats/linux/cgroup_linux_test.go +++ b/procstats/linux/cgroup_linux_test.go @@ -27,7 +27,6 @@ func TestParseProcCGroup(t *testing.T) { ` proc, err := ParseProcCGroup(text) - if err != nil { t.Error(err) return diff --git a/procstats/linux/error.go b/procstats/linux/error.go index 12d7b430..2a6889c5 100644 --- a/procstats/linux/error.go +++ b/procstats/linux/error.go @@ -11,7 +11,7 @@ func convertPanicToError(v interface{}) (err error) { err = fmt.Errorf("%v", e) } } - return + return err } func check(err error) { diff --git a/procstats/linux/files.go b/procstats/linux/files.go index 0b09bf2d..49c8ee2f 100644 --- a/procstats/linux/files.go +++ b/procstats/linux/files.go @@ -8,7 +8,7 @@ import "os" func ReadOpenFileCount(pid int) (n uint64, err error) { defer func() { err = convertPanicToError(recover()) }() n = readOpenFileCount(pid) - return + return n, err } func readOpenFileCount(pid int) uint64 { diff --git a/procstats/linux/limits.go b/procstats/linux/limits.go index 4b2e9952..6c526391 100644 --- a/procstats/linux/limits.go +++ b/procstats/linux/limits.go @@ -39,14 +39,14 @@ type ProcLimits struct { func ReadProcLimits(pid int) (proc ProcLimits, err error) { defer func() { err = convertPanicToError(recover()) }() proc = parseProcLimits(readProcFile(pid, "limits")) - return + return proc, err } // ParseProcLimits parses system process limits and returns a ProcLimits and error, if any. func ParseProcLimits(s string) (proc ProcLimits, err error) { defer func() { err = convertPanicToError(recover()) }() proc = parseProcLimits(s) - return + return proc, err } func parseProcLimits(s string) (proc ProcLimits) { @@ -98,7 +98,7 @@ func parseProcLimits(s string) (proc ProcLimits) { } }) - return + return proc } func parseLimitUint(s string) uint64 { diff --git a/procstats/linux/memory_darwin.go b/procstats/linux/memory_darwin.go index f3017a7e..7e1c8a63 100644 --- a/procstats/linux/memory_darwin.go +++ b/procstats/linux/memory_darwin.go @@ -2,5 +2,5 @@ package linux func readMemoryLimit(_ int) (limit uint64, err error) { limit = unlimitedMemoryLimit - return + return limit, err } diff --git a/procstats/linux/memory_linux.go b/procstats/linux/memory_linux.go index afc126c1..f5d167f3 100644 --- a/procstats/linux/memory_linux.go +++ b/procstats/linux/memory_linux.go @@ -13,21 +13,21 @@ func readMemoryLimit(pid int) (limit uint64, err error) { if limit = readCGroupMemoryLimit(pid); limit == unlimitedMemoryLimit { limit, err = readSysinfoMemoryLimit() } - return + return limit, err } func readCGroupMemoryLimit(pid int) (limit uint64) { if cgroups, err := ReadProcCGroup(pid); err == nil { limit = readProcCGroupMemoryLimit(cgroups) } - return + return limit } func readProcCGroupMemoryLimit(cgroups ProcCGroup) (limit uint64) { if memory, ok := cgroups.Lookup("memory"); ok { limit = readMemoryCGroupMemoryLimit(memory) } - return + return limit } func readMemoryCGroupMemoryLimit(cgroup CGroup) (limit uint64) { @@ -39,7 +39,7 @@ func readMemoryCGroupMemoryLimit(cgroup CGroup) (limit uint64) { } } - return + return limit } func readMemoryCGroupMemoryLimitFilePath(cgroupPath string) string { @@ -62,5 +62,5 @@ func readSysinfoMemoryLimit() (limit uint64, err error) { limit = uint64(sysinfo.Unit) * uint64(sysinfo.Totalram) } - return + return limit, err } diff --git a/procstats/linux/memory_linux_test.go b/procstats/linux/memory_linux_test.go index a9ae64e6..e7177810 100644 --- a/procstats/linux/memory_linux_test.go +++ b/procstats/linux/memory_linux_test.go @@ -17,10 +17,8 @@ func TestReadMemoryLimit(t *testing.T) { } if limit, err := ReadMemoryLimit(os.Getpid()); err != nil { t.Error(err) - } else if limit == 0 { t.Error("memory should not be zero") - } else if limit == unlimitedMemoryLimit { t.Error("memory should not be unlimited") } diff --git a/procstats/linux/parse.go b/procstats/linux/parse.go index f4a7fad1..94d6b6ef 100644 --- a/procstats/linux/parse.go +++ b/procstats/linux/parse.go @@ -69,7 +69,7 @@ func split(text string, sep byte) (head, tail string) { } head = strings.TrimSpace(head) tail = strings.TrimSpace(tail) - return + return head, tail } func skipSpaces(text string) string { diff --git a/procstats/linux/sched.go b/procstats/linux/sched.go index dd548ed5..f154cde4 100644 --- a/procstats/linux/sched.go +++ b/procstats/linux/sched.go @@ -17,14 +17,14 @@ type ProcSched struct { func ReadProcSched(pid int) (proc ProcSched, err error) { defer func() { err = convertPanicToError(recover()) }() proc = parseProcSched(readProcFile(pid, "sched")) - return + return proc, err } // ParseProcSched processes system process scheduling data and returns a ProcSched and error, if any. func ParseProcSched(s string) (proc ProcSched, err error) { defer func() { err = convertPanicToError(recover()) }() proc = parseProcSched(s) - return + return proc, err } func parseProcSched(s string) (proc ProcSched) { @@ -53,5 +53,5 @@ func parseProcSched(s string) (proc ProcSched) { } }) - return + return proc } diff --git a/procstats/linux/stat.go b/procstats/linux/stat.go index 2c66cc86..8e023d5a 100644 --- a/procstats/linux/stat.go +++ b/procstats/linux/stat.go @@ -30,7 +30,7 @@ func (ps *ProcState) Scan(s fmt.ScanState, _ rune) (err error) { *ps = ProcState(c) } - return + return err } // ProcStat contains statistics associated with a process. @@ -151,5 +151,5 @@ func ParseProcStat(s string) (proc ProcStat, err error) { &proc.EnvEnd, &proc.ExitCode, ) - return + return proc, err } diff --git a/procstats/linux/statm.go b/procstats/linux/statm.go index 054b375b..8fc5f4bc 100644 --- a/procstats/linux/statm.go +++ b/procstats/linux/statm.go @@ -30,5 +30,5 @@ func ParseProcStatm(s string) (proc ProcStatm, err error) { &proc.Data, &proc.Dt, ) - return + return proc, err } diff --git a/procstats/proc_darwin.go b/procstats/proc_darwin.go index a50cc158..c8cdeb24 100644 --- a/procstats/proc_darwin.go +++ b/procstats/proc_darwin.go @@ -63,7 +63,7 @@ func collectProcInfo(pid int) (info ProcInfo, err error) { info.Threads.Num = threadCount(task) info.Threads.InvoluntaryContextSwitches = suspend - return + return info, err } func memoryAvailable() uint64 { diff --git a/procstats/proc_linux.go b/procstats/proc_linux.go index ffa2784d..fbc5abc8 100644 --- a/procstats/proc_linux.go +++ b/procstats/proc_linux.go @@ -165,5 +165,5 @@ func collectProcInfo(pid int) (info ProcInfo, err error) { }, } - return + return info, err } diff --git a/procstats/proc_windows.go b/procstats/proc_windows.go index 5e30acdd..fc202dc7 100644 --- a/procstats/proc_windows.go +++ b/procstats/proc_windows.go @@ -2,5 +2,5 @@ package procstats func collectProcMetrics(pid int) (m proc, err error) { // TODO - return + return m, err } diff --git a/prometheus/append_test.go b/prometheus/append_test.go index c706ab4b..35640f39 100644 --- a/prometheus/append_test.go +++ b/prometheus/append_test.go @@ -85,7 +85,7 @@ func BenchmarkAppendMetric(b *testing.B) { for _, test := range testMetrics { b.Run(test.scenario, func(b *testing.B) { - for i := 0; i != b.N; i++ { + for b.Loop() { appendMetric(a[:0], test.metric) } }) diff --git a/prometheus/handler_test.go b/prometheus/handler_test.go index 49f49a7e..7e5d5aee 100644 --- a/prometheus/handler_test.go +++ b/prometheus/handler_test.go @@ -151,7 +151,7 @@ func BenchmarkHandleMetric(b *testing.B) { Buckets: buckets, } - for i := 0; i != b.N; i++ { + for b.Loop() { handler.HandleMeasures(now, metric) } }) diff --git a/prometheus/metric.go b/prometheus/metric.go index 82652d7f..65a3b3b3 100644 --- a/prometheus/metric.go +++ b/prometheus/metric.go @@ -417,7 +417,7 @@ func nextLe(s string) (head, tail string) { } else { head = s } - return + return head, tail } func appendFloat(b []byte, f float64) []byte { diff --git a/prometheus/metric_test.go b/prometheus/metric_test.go index 7c5aafa9..c8204934 100644 --- a/prometheus/metric_test.go +++ b/prometheus/metric_test.go @@ -265,7 +265,7 @@ func BenchmarkLE(b *testing.B) { stats.ValueOf(math.Inf(+1)), } - for i := 0; i != b.N; i++ { + for b.Loop() { le(buckets) } } diff --git a/tag_test.go b/tag_test.go index 1378ec1c..7c0ca25f 100644 --- a/tag_test.go +++ b/tag_test.go @@ -207,7 +207,7 @@ func benchmarkTagsOrder(b *testing.B, isSorted func([]Tag) bool) { {"some longer tag name", "1234"}, } - for i := 0; i != b.N; i++ { + for b.Loop() { isSorted(tags) } } @@ -289,7 +289,7 @@ func benchmarkSortTagsFunc(b *testing.B, t0 []Tag, fn func([]Tag)) { t1 := make([]Tag, len(t0)) - for i := 0; i != b.N; i++ { + for b.Loop() { copy(t1, t0) fn(t1) } @@ -315,7 +315,7 @@ func BenchmarkTagsBufferSortSorted(b *testing.B) { tags: make([]Tag, len(tags)), } - for i := 0; i < b.N; i++ { + for b.Loop() { copy(buf.tags, tags) buf.sort() } @@ -341,7 +341,7 @@ func BenchmarkTagsBufferSortUnsorted(b *testing.B) { tags: make([]Tag, len(tags)), } - for i := 0; i < b.N; i++ { + for b.Loop() { copy(buf.tags, tags) buf.sort() } @@ -380,7 +380,7 @@ func BenchmarkMergeTags(b *testing.B) { t1 := make([]Tag, len(origT1)) t2 := make([]Tag, len(origT2)) - for i := 0; i < b.N; i++ { + for b.Loop() { copy(t1, origT1) copy(t2, origT2) diff --git a/util/objconv/adapter.go b/util/objconv/adapter.go index 9e738ffe..92c331c1 100644 --- a/util/objconv/adapter.go +++ b/util/objconv/adapter.go @@ -46,7 +46,7 @@ func AdapterOf(typ reflect.Type) (a Adapter, ok bool) { adapterMutex.RLock() a, ok = adapterStore[typ] adapterMutex.RUnlock() - return + return a, ok } var ( diff --git a/util/objconv/codec.go b/util/objconv/codec.go index e5903fc9..819e38b9 100644 --- a/util/objconv/codec.go +++ b/util/objconv/codec.go @@ -65,7 +65,7 @@ func (reg *Registry) Lookup(mimetype string) (codec Codec, ok bool) { reg.mutex.RLock() codec, ok = reg.codecs[mimetype] reg.mutex.RUnlock() - return + return codec, ok } // Codecs returns a map of all codecs registered in reg. @@ -76,7 +76,7 @@ func (reg *Registry) Codecs() (codecs map[string]Codec) { codecs[mimetype] = codec } reg.mutex.RUnlock() - return + return codecs } // The global registry to which packages add their codecs. diff --git a/util/objconv/encode_test.go b/util/objconv/encode_test.go index a2bd526a..c06a86f4 100644 --- a/util/objconv/encode_test.go +++ b/util/objconv/encode_test.go @@ -258,7 +258,7 @@ func BenchmarkEncoder(b *testing.B) { for _, test := range tests { b.Run(fmt.Sprintf("%T", test), func(b *testing.B) { - for i := 0; i != b.N; i++ { + for b.Loop() { enc.Encode(test) } }) diff --git a/util/objconv/json/bench_test.go b/util/objconv/json/bench_test.go index dfdb2353..8b1312ff 100644 --- a/util/objconv/json/bench_test.go +++ b/util/objconv/json/bench_test.go @@ -1,5 +1,4 @@ //go:build ignore -// +build ignore // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -39,9 +38,11 @@ type codeNode struct { MeanT int64 `json:"mean_t"` } -var codeOnce sync.Once -var codeJSON []byte -var codeStruct codeResponse +var ( + codeOnce sync.Once + codeJSON []byte + codeStruct codeResponse +) func codeInit() { codeOnce.Do(func() { @@ -71,7 +72,7 @@ func BenchmarkCodeEncoder(b *testing.B) { codeInit() b.ResetTimer() enc := NewEncoder(io.Discard) - for i := 0; i < b.N; i++ { + for b.Loop() { if err := enc.Encode(&codeStruct); err != nil { b.Fatal("Encode:", err) } @@ -82,7 +83,7 @@ func BenchmarkCodeEncoder(b *testing.B) { func BenchmarkCodeMarshal(b *testing.B) { codeInit() b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { if _, err := Marshal(&codeStruct); err != nil { b.Fatal("Marshal:", err) } @@ -96,7 +97,7 @@ func BenchmarkCodeDecoder(b *testing.B) { var buf bytes.Buffer dec := NewDecoder(&buf) var r codeResponse - for i := 0; i < b.N; i++ { + for b.Loop() { buf.Write(codeJSON) // hide EOF buf.WriteByte('\n') @@ -119,7 +120,8 @@ func BenchmarkDecoderStream(b *testing.B) { } ones := strings.Repeat(" 1\n", 300000) + "\n\n\n" b.ResetTimer() - for i := 0; i < b.N; i++ { + i := 0 + for b.Loop() { if i%300000 == 0 { buf.WriteString(ones) } @@ -127,13 +129,14 @@ func BenchmarkDecoderStream(b *testing.B) { if err := dec.Decode(&x); err != nil || x != int64(1) { b.Fatalf("Decode: %v after %d", err, i) } + i++ } } func BenchmarkCodeUnmarshal(b *testing.B) { codeInit() b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { var r codeResponse if err := Unmarshal(codeJSON, &r); err != nil { b.Fatal("Unmarshal:", err) @@ -146,7 +149,7 @@ func BenchmarkCodeUnmarshalReuse(b *testing.B) { codeInit() b.ResetTimer() var r codeResponse - for i := 0; i < b.N; i++ { + for b.Loop() { if err := Unmarshal(codeJSON, &r); err != nil { b.Fatal("Unmarshal:", err) } @@ -158,7 +161,7 @@ func BenchmarkUnmarshalString(b *testing.B) { data := []byte(`"hello, world"`) var s string - for i := 0; i < b.N; i++ { + for b.Loop() { if err := Unmarshal(data, &s); err != nil { b.Fatal("Unmarshal:", err) } @@ -169,7 +172,7 @@ func BenchmarkUnmarshalFloat64(b *testing.B) { var f float64 data := []byte(`3.14`) - for i := 0; i < b.N; i++ { + for b.Loop() { if err := Unmarshal(data, &f); err != nil { b.Fatal("Unmarshal:", err) } @@ -180,7 +183,7 @@ func BenchmarkUnmarshalInt64(b *testing.B) { var x int64 data := []byte(`3`) - for i := 0; i < b.N; i++ { + for b.Loop() { if err := Unmarshal(data, &x); err != nil { b.Fatal("Unmarshal:", err) } @@ -191,7 +194,7 @@ func BenchmarkIssue10335(b *testing.B) { b.ReportAllocs() var s struct{} j := []byte(`{"a":{ }}`) - for n := 0; n < b.N; n++ { + for b.Loop() { if err := Unmarshal(j, &s); err != nil { b.Fatal(err) } diff --git a/util/objconv/json/encode.go b/util/objconv/json/encode.go index 78135c06..64071c3f 100644 --- a/util/objconv/json/encode.go +++ b/util/objconv/json/encode.go @@ -39,7 +39,7 @@ func Marshal(v interface{}) (b []byte, err error) { } marshalerPool.Put(m) - return + return b, err } var marshalerPool = sync.Pool{ diff --git a/util/objconv/objtests/test_codec.go b/util/objconv/objtests/test_codec.go index 4ba987af..cfe75142 100644 --- a/util/objconv/objtests/test_codec.go +++ b/util/objconv/objtests/test_codec.go @@ -304,7 +304,7 @@ type counter struct { func (c *counter) Write(b []byte) (n int, err error) { n = len(b) c.n += n - return + return n, err } // BenchmarkCodec implements a benchmark suite for codecs, making it easy to get @@ -323,7 +323,7 @@ func benchmarkEncoder(b *testing.B, codec objconv.Codec) { c := &counter{} e := objconv.NewEncoder(codec.NewEmitter(c)) - for i := 0; i != b.N; i++ { + for b.Loop() { if err := e.Encode(v); err != nil { b.Fatal(err) } @@ -350,7 +350,7 @@ func benchmarkDecoder(b *testing.B, codec objconv.Codec) { b.Run(testName(v), func(b *testing.B) { d := objconv.NewDecoder(codec.NewParser(r)) - for i := 0; i != b.N; i++ { + for b.Loop() { var x interface{} if err := d.Decode(&x); err != nil { b.Fatal(err) @@ -371,7 +371,7 @@ func benchmarkStreamEncoder(b *testing.B, codec objconv.Codec) { c := &counter{} e := objconv.NewStreamEncoder(codec.NewEmitter(c)) - for i := 0; i != b.N; i++ { + for b.Loop() { if err := e.Encode(v); err != nil { b.Fatal(err) } @@ -404,7 +404,7 @@ func benchmarkStreamDecoder(b *testing.B, codec objconv.Codec) { b.Run(testName(v), func(b *testing.B) { d := objconv.NewStreamDecoder(codec.NewParser(r)) - for i := 0; i != b.N; i++ { + for b.Loop() { var x interface{} if err := d.Decode(&x); err != nil { b.Fatal(err) diff --git a/util/objconv/objutil/duration_test.go b/util/objconv/objutil/duration_test.go index 22eebbd1..141b8005 100644 --- a/util/objconv/objutil/duration_test.go +++ b/util/objconv/objutil/duration_test.go @@ -33,7 +33,7 @@ func BenchmarkAppendDuration(b *testing.B) { for _, test := range durationTests { b.Run(test.String(), func(b *testing.B) { var a [32]byte - for i := 0; i != b.N; i++ { + for b.Loop() { AppendDuration(a[:0], test) } }) diff --git a/util/objconv/objutil/int_test.go b/util/objconv/objutil/int_test.go index 007e4467..f2e216a5 100644 --- a/util/objconv/objutil/int_test.go +++ b/util/objconv/objutil/int_test.go @@ -35,7 +35,7 @@ func BenchmarkParseInt(b *testing.B) { b.Run(test.s, func(b *testing.B) { s := []byte(test.s) - for i := 0; i != b.N; i++ { + for b.Loop() { ParseInt(s) } }) @@ -76,7 +76,7 @@ func BenchmarkParseUintHex(b *testing.B) { b.Run(test.s, func(b *testing.B) { s := []byte(test.s) - for i := 0; i != b.N; i++ { + for b.Loop() { ParseUintHex(s) } }) diff --git a/value_test.go b/value_test.go index 4b3c595b..96e429e2 100644 --- a/value_test.go +++ b/value_test.go @@ -87,7 +87,7 @@ func TestValueOf(t *testing.T) { } func BenchmarkValueOf(b *testing.B) { - for i := 0; i != b.N; i++ { + for b.Loop() { ValueOf(42) } }