Skip to content

Commit 1444b91

Browse files
authored
Merge pull request #489 from sevagh/master
WriteToTextfile function for node exporter textfiles
2 parents f30f428 + 88f4223 commit 1444b91

File tree

2 files changed

+137
-0
lines changed

2 files changed

+137
-0
lines changed

prometheus/registry.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,17 @@ package prometheus
1616
import (
1717
"bytes"
1818
"fmt"
19+
"io/ioutil"
20+
"os"
21+
"path/filepath"
1922
"runtime"
2023
"sort"
2124
"strings"
2225
"sync"
2326
"unicode/utf8"
2427

2528
"github.com/golang/protobuf/proto"
29+
"github.com/prometheus/common/expfmt"
2630

2731
dto "github.com/prometheus/client_model/go"
2832

@@ -533,6 +537,38 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) {
533537
return internal.NormalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap()
534538
}
535539

540+
// WriteToTextfile calls Gather on the provided Gatherer, encodes the result in the
541+
// Prometheus text format, and writes it to a temporary file. Upon success, the
542+
// temporary file is renamed to the provided filename.
543+
//
544+
// This is intended for use with the textfile collector of the node exporter.
545+
// Note that the node exporter expects the filename to be suffixed with ".prom".
546+
func WriteToTextfile(filename string, g Gatherer) error {
547+
tmp, err := ioutil.TempFile(filepath.Dir(filename), filepath.Base(filename))
548+
if err != nil {
549+
return err
550+
}
551+
defer os.Remove(tmp.Name())
552+
553+
mfs, err := g.Gather()
554+
if err != nil {
555+
return err
556+
}
557+
for _, mf := range mfs {
558+
if _, err := expfmt.MetricFamilyToText(tmp, mf); err != nil {
559+
return err
560+
}
561+
}
562+
if err := tmp.Close(); err != nil {
563+
return err
564+
}
565+
566+
if err := os.Chmod(tmp.Name(), 0644); err != nil {
567+
return err
568+
}
569+
return os.Rename(tmp.Name(), filename)
570+
}
571+
536572
// processMetric is an internal helper method only used by the Gather method.
537573
func processMetric(
538574
metric Metric,

prometheus/registry_test.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ package prometheus_test
2121

2222
import (
2323
"bytes"
24+
"io/ioutil"
2425
"math/rand"
2526
"net/http"
2627
"net/http/httptest"
28+
"os"
2729
"sync"
2830
"testing"
2931
"time"
@@ -871,3 +873,102 @@ func TestHistogramVecRegisterGatherConcurrency(t *testing.T) {
871873
close(quit)
872874
wg.Wait()
873875
}
876+
877+
func TestWriteToTextfile(t *testing.T) {
878+
expectedOut := `# HELP test_counter test counter
879+
# TYPE test_counter counter
880+
test_counter{name="qux"} 1
881+
# HELP test_gauge test gauge
882+
# TYPE test_gauge gauge
883+
test_gauge{name="baz"} 1.1
884+
# HELP test_hist test histogram
885+
# TYPE test_hist histogram
886+
test_hist_bucket{name="bar",le="0.005"} 0
887+
test_hist_bucket{name="bar",le="0.01"} 0
888+
test_hist_bucket{name="bar",le="0.025"} 0
889+
test_hist_bucket{name="bar",le="0.05"} 0
890+
test_hist_bucket{name="bar",le="0.1"} 0
891+
test_hist_bucket{name="bar",le="0.25"} 0
892+
test_hist_bucket{name="bar",le="0.5"} 0
893+
test_hist_bucket{name="bar",le="1"} 1
894+
test_hist_bucket{name="bar",le="2.5"} 1
895+
test_hist_bucket{name="bar",le="5"} 2
896+
test_hist_bucket{name="bar",le="10"} 2
897+
test_hist_bucket{name="bar",le="+Inf"} 2
898+
test_hist_sum{name="bar"} 3.64
899+
test_hist_count{name="bar"} 2
900+
# HELP test_summary test summary
901+
# TYPE test_summary summary
902+
test_summary{name="foo",quantile="0.5"} 10
903+
test_summary{name="foo",quantile="0.9"} 20
904+
test_summary{name="foo",quantile="0.99"} 20
905+
test_summary_sum{name="foo"} 30
906+
test_summary_count{name="foo"} 2
907+
`
908+
909+
registry := prometheus.NewRegistry()
910+
911+
summary := prometheus.NewSummaryVec(
912+
prometheus.SummaryOpts{
913+
Name: "test_summary",
914+
Help: "test summary",
915+
},
916+
[]string{"name"},
917+
)
918+
919+
histogram := prometheus.NewHistogramVec(
920+
prometheus.HistogramOpts{
921+
Name: "test_hist",
922+
Help: "test histogram",
923+
},
924+
[]string{"name"},
925+
)
926+
927+
gauge := prometheus.NewGaugeVec(
928+
prometheus.GaugeOpts{
929+
Name: "test_gauge",
930+
Help: "test gauge",
931+
},
932+
[]string{"name"},
933+
)
934+
935+
counter := prometheus.NewCounterVec(
936+
prometheus.CounterOpts{
937+
Name: "test_counter",
938+
Help: "test counter",
939+
},
940+
[]string{"name"},
941+
)
942+
943+
registry.MustRegister(summary)
944+
registry.MustRegister(histogram)
945+
registry.MustRegister(gauge)
946+
registry.MustRegister(counter)
947+
948+
summary.With(prometheus.Labels{"name": "foo"}).Observe(10)
949+
summary.With(prometheus.Labels{"name": "foo"}).Observe(20)
950+
histogram.With(prometheus.Labels{"name": "bar"}).Observe(0.93)
951+
histogram.With(prometheus.Labels{"name": "bar"}).Observe(2.71)
952+
gauge.With(prometheus.Labels{"name": "baz"}).Set(1.1)
953+
counter.With(prometheus.Labels{"name": "qux"}).Inc()
954+
955+
tmpfile, err := ioutil.TempFile("", "prom_registry_test")
956+
if err != nil {
957+
t.Fatal(err)
958+
}
959+
defer os.Remove(tmpfile.Name())
960+
961+
if err := prometheus.WriteToTextfile(tmpfile.Name(), registry); err != nil {
962+
t.Fatal(err)
963+
}
964+
965+
fileBytes, err := ioutil.ReadFile(tmpfile.Name())
966+
if err != nil {
967+
t.Fatal(err)
968+
}
969+
fileContents := string(fileBytes)
970+
971+
if fileContents != expectedOut {
972+
t.Error("file contents didn't match unexpected")
973+
}
974+
}

0 commit comments

Comments
 (0)