Skip to content

Commit

Permalink
Merge pull request #126 from LukeWinikates/make-formatting-funcs-inte…
Browse files Browse the repository at this point in the history
…rnal

make formatters functions internal
  • Loading branch information
keep94 authored Dec 16, 2022
2 parents ebcfaea + 9bcf839 commit 2e71c31
Show file tree
Hide file tree
Showing 14 changed files with 746 additions and 669 deletions.
109 changes: 109 additions & 0 deletions internal/event/formatter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package event

import (
"encoding/json"
"fmt"
"github.com/wavefronthq/wavefront-sdk-go/event"
"github.com/wavefronthq/wavefront-sdk-go/internal"
"strconv"
)

// Line encode the event to a wf proxy format
// set endMillis to 0 for a 'Instantaneous' event
func Line(name string, startMillis, endMillis int64, source string, tags map[string]string, setters ...event.Option) (string, error) {
sb := internal.GetBuffer()
defer internal.PutBuffer(sb)

annotations := map[string]string{}
l := map[string]interface{}{
"annotations": annotations,
}
for _, set := range setters {
set(l)
}

sb.WriteString("@Event")

startMillis, endMillis = adjustStartEndTime(startMillis, endMillis)

sb.WriteString(" ")
sb.WriteString(strconv.FormatInt(startMillis, 10))
sb.WriteString(" ")
sb.WriteString(strconv.FormatInt(endMillis, 10))

sb.WriteString(" ")
sb.WriteString(strconv.Quote(name))

for k, v := range annotations {
sb.WriteString(" ")
sb.WriteString(k)
sb.WriteString("=")
sb.WriteString(strconv.Quote(v))
}

if len(source) > 0 {
sb.WriteString(" host=")
sb.WriteString(strconv.Quote(source))
}

for k, v := range tags {
sb.WriteString(" tag=")
sb.WriteString(strconv.Quote(fmt.Sprintf("%v: %v", k, v)))
}

sb.WriteString("\n")
return sb.String(), nil
}

// LineJSON encodes the event to a wf API format
func LineJSON(name string, startMillis, endMillis int64, source string, tags map[string]string, setters ...event.Option) (string, error) {
annotations := map[string]string{}
l := map[string]interface{}{
"name": name,
"annotations": annotations,
}

for _, set := range setters {
set(l)
}

startMillis, endMillis = adjustStartEndTime(startMillis, endMillis)

l["startTime"] = startMillis
l["endTime"] = endMillis

if len(tags) > 0 {
var tagList []string
for k, v := range tags {
tagList = append(tagList, fmt.Sprintf("%v: %v", k, v))
}
l["tags"] = tagList
}

if len(source) > 0 {
l["hosts"] = []string{source}
}

jsonData, err := json.Marshal(l)
if err != nil {
return "", err
}

return string(jsonData), nil
}

func adjustStartEndTime(startMillis, endMillis int64) (int64, int64) {
// secs to millis
if startMillis < 999999999999 {
startMillis = startMillis * 1000
}

if endMillis <= 999999999999 {
endMillis = endMillis * 1000
}

if endMillis == 0 {
endMillis = startMillis + 1
}
return startMillis, endMillis
}
71 changes: 71 additions & 0 deletions internal/histogram/formatter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package histogram

import (
"bytes"
"errors"
"fmt"
"github.com/wavefronthq/wavefront-sdk-go/histogram"
"github.com/wavefronthq/wavefront-sdk-go/internal"
"strconv"
)

// Gets a histogram line in the Wavefront histogram data format:
// {!M | !H | !D} [<timestamp>] #<count> <mean> [centroids] <histogramName> source=<source> [pointTags]
// Example: "!M 1533531013 #20 30.0 #10 5.1 request.latency source=appServer1 region=us-west"
func HistogramLine(name string, centroids histogram.Centroids, hgs map[histogram.Granularity]bool, ts int64, source string, tags map[string]string, defaultSource string) (string, error) {
if name == "" {
return "", errors.New("empty distribution name")
}

if len(centroids) == 0 {
return "", fmt.Errorf("distribution should have at least one centroid: histogram=%s", name)
}

if len(hgs) == 0 {
return "", fmt.Errorf("histogram granularities cannot be empty: histogram=%s", name)
}

if source == "" {
source = defaultSource
}

sb := internal.GetBuffer()
defer internal.PutBuffer(sb)

if ts != 0 {
sb.WriteString(" ")
sb.WriteString(strconv.FormatInt(ts, 10))
}
// Preprocess line. We know len(hgs) > 0 here.
for _, centroid := range centroids.Compact() {
sb.WriteString(" #")
sb.WriteString(strconv.Itoa(centroid.Count))
sb.WriteString(" ")
sb.WriteString(strconv.FormatFloat(centroid.Value, 'f', -1, 64))
}
sb.WriteString(" ")
sb.WriteString(strconv.Quote(internal.Sanitize(name)))
sb.WriteString(" source=")
sb.WriteString(internal.SanitizeValue(source))

for k, v := range tags {
if v == "" {
return "", fmt.Errorf("tag values cannot be empty: histogram=%s tag=%s", name, k)
}
sb.WriteString(" ")
sb.WriteString(strconv.Quote(internal.Sanitize(k)))
sb.WriteString("=")
sb.WriteString(internal.SanitizeValue(v))
}
sbBytes := sb.Bytes()

sbg := bytes.Buffer{}
for hg, on := range hgs {
if on {
sbg.WriteString(hg.String())
sbg.Write(sbBytes)
sbg.WriteString("\n")
}
}
return sbg.String(), nil
}
99 changes: 99 additions & 0 deletions internal/histogram/formatter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package histogram

import (
"github.com/stretchr/testify/assert"
"github.com/wavefronthq/wavefront-sdk-go/histogram"
"testing"
)

var line string

func BenchmarkHistogramLine(b *testing.B) {
name := "request.latency"
centroids := makeCentroids()
hgs := map[histogram.Granularity]bool{histogram.MINUTE: true}
ts := int64(1533529977)
src := "test_source"
tags := map[string]string{"env": "test"}

var r string
for n := 0; n < b.N; n++ {
r, _ = HistogramLine(name, centroids, hgs, ts, src, tags, "")
}
line = r
}

func TestHistogramLineCentroidsFormat(t *testing.T) {
centroids := histogram.Centroids{
{Value: 30.0, Count: 20},
{Value: 5.1, Count: 10},
{Value: 30.0, Count: 20},
{Value: 5.1, Count: 10},
{Value: 30.0, Count: 20},
}

line, err := HistogramLine("request.latency", centroids, map[histogram.Granularity]bool{histogram.MINUTE: true},
1533529977, "test_source", map[string]string{"env": "test"}, "")

assert.Nil(t, err)
expected := []string{
"!M 1533529977 #60 30 #20 5.1 \"request.latency\" source=\"test_source\" \"env\"=\"test\"\n",
"!M 1533529977 #20 5.1 #60 30 \"request.latency\" source=\"test_source\" \"env\"=\"test\"\n",
}
ok := false
for _, exp := range expected {
if assert.ObjectsAreEqual(exp, line) {
ok = true
}
}
if !ok {
assert.Equal(t, expected[0], line)
assert.Equal(t, expected[1], line)
}
}

func TestHistogramLine(t *testing.T) {
centroids := makeCentroids()

line, err := HistogramLine("request.latency", centroids, map[histogram.Granularity]bool{histogram.MINUTE: true},
1533529977, "test_source", map[string]string{"env": "test"}, "")
expected := "!M 1533529977 #20 30 \"request.latency\" source=\"test_source\" \"env\"=\"test\"\n"
assert.Nil(t, err)
assert.Equal(t, expected, line)

line, err = HistogramLine("request.latency", centroids, map[histogram.Granularity]bool{histogram.MINUTE: true, histogram.HOUR: false},
1533529977, "", map[string]string{"env": "test"}, "default")
expected = "!M 1533529977 #20 30 \"request.latency\" source=\"default\" \"env\"=\"test\"\n"
assert.Nil(t, err)
assert.Equal(t, expected, line)

line, err = HistogramLine("request.latency", centroids, map[histogram.Granularity]bool{histogram.HOUR: true, histogram.MINUTE: false},
1533529977, "", map[string]string{"env": "test"}, "default")
expected = "!H 1533529977 #20 30 \"request.latency\" source=\"default\" \"env\"=\"test\"\n"
assert.Nil(t, err)
assert.Equal(t, expected, line)

line, err = HistogramLine("request.latency", centroids, map[histogram.Granularity]bool{histogram.DAY: true},
1533529977, "", map[string]string{"env": "test"}, "default")
expected = "!D 1533529977 #20 30 \"request.latency\" source=\"default\" \"env\"=\"test\"\n"
assert.Nil(t, err)
assert.Equal(t, expected, line)

line, err = HistogramLine("request.latency", centroids, map[histogram.Granularity]bool{histogram.MINUTE: true, histogram.HOUR: true, histogram.DAY: false},
1533529977, "test_source", map[string]string{"env": "test"}, "")
expected = "!M 1533529977 #20 30 \"request.latency\" source=\"test_source\" \"env\"=\"test\"\n" +
"!H 1533529977 #20 30 \"request.latency\" source=\"test_source\" \"env\"=\"test\"\n"
if len(line) != len(expected) {
t.Errorf("lines don't match. expected: %s, actual: %s", expected, line)
}
}

func makeCentroids() []histogram.Centroid {
centroids := []histogram.Centroid{
{
Value: 30.0,
Count: 20,
},
}
return centroids
}
48 changes: 48 additions & 0 deletions internal/metric/formatter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package metric

import (
"errors"
"fmt"
"github.com/wavefronthq/wavefront-sdk-go/internal"
"strconv"
)

// Gets a metric line in the Wavefront metrics data format:
// <metricName> <metricValue> [<timestamp>] source=<source> [pointTags]
// Example: "new-york.power.usage 42422.0 1533531013 source=localhost datacenter=dc1"
func Line(name string, value float64, ts int64, source string, tags map[string]string, defaultSource string) (string, error) {
if name == "" {
return "", errors.New("empty metric name")
}

if source == "" {
source = defaultSource
}

sb := internal.GetBuffer()
defer internal.PutBuffer(sb)

sb.WriteString(strconv.Quote(internal.Sanitize(name)))
sb.WriteString(" ")
sb.WriteString(strconv.FormatFloat(value, 'f', -1, 64))

if ts != 0 {
sb.WriteString(" ")
sb.WriteString(strconv.FormatInt(ts, 10))
}

sb.WriteString(" source=")
sb.WriteString(internal.SanitizeValue(source))

for k, v := range tags {
if v == "" {
return "", fmt.Errorf("tag values cannot be empty: metric=%s tag=%s", name, k)
}
sb.WriteString(" ")
sb.WriteString(strconv.Quote(internal.Sanitize(k)))
sb.WriteString("=")
sb.WriteString(internal.SanitizeValue(v))
}
sb.WriteString("\n")
return sb.String(), nil
}
42 changes: 42 additions & 0 deletions internal/metric/formatter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package metric

import (
"github.com/stretchr/testify/assert"
"testing"
)

var line string

func BenchmarkMetricLine(b *testing.B) {
name := "foo.metric"
value := 1.2
ts := int64(1533529977)
src := "test_source"
tags := map[string]string{"env": "test"}

var r string
for n := 0; n < b.N; n++ {
r, _ = Line(name, value, ts, src, tags, "")
}
line = r
}

func TestMetricLine(t *testing.T) {
line, err := Line("foo.metric", 1.2, 1533529977, "test_source",
map[string]string{"env": "test"}, "")
expected := "\"foo.metric\" 1.2 1533529977 source=\"test_source\" \"env\"=\"test\"\n"
assert.Nil(t, err)
assert.Equal(t, expected, line)

line, err = Line("foo.metric", 1.2, 1533529977, "",
map[string]string{"env": "test"}, "default")
expected = "\"foo.metric\" 1.2 1533529977 source=\"default\" \"env\"=\"test\"\n"
assert.Nil(t, err)
assert.Equal(t, expected, line)

line, err = Line("foo.metric", 1.2, 1533529977, "1.2.3.4:8080",
map[string]string{"env": "test"}, "default")
expected = "\"foo.metric\" 1.2 1533529977 source=\"1.2.3.4:8080\" \"env\"=\"test\"\n"
assert.Nil(t, err)
assert.Equal(t, expected, line)
}
Loading

0 comments on commit 2e71c31

Please sign in to comment.