From a2e443908679c2d7c452195a3d178580cbd43a19 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Mon, 2 Nov 2015 08:32:55 +0100 Subject: [PATCH] Add support for remote storage on Graphite Allows to use graphite over tcp or udp. Metrics labels and values are used to construct a valid Graphite path in a way that will allow us to eventually read them back and reconstruct the metrics. For example, this metric: model.Metric{ model.MetricNameLabel: "test:metric", "testlabel": "test:value", "testlabel2": "test:value", ) Will become: test:metric.testlabel=test:value.testlabel2=test:value escape.go takes care of escaping values to match Graphite character set, it basically uses percent-encoding as a fallback wich will work pretty will in the graphite/grafana world. The remote storage module also has an optional 'prefix' parameter to prefix all metrics with a path (for example, 'prometheus.'). Graphite URLs are simply in the form tcp://host:port or udp://host:port. --- cmd/prometheus/config.go | 12 +++ storage/remote/graphite/client.go | 107 +++++++++++++++++++++++++ storage/remote/graphite/client_test.go | 57 +++++++++++++ storage/remote/graphite/escape.go | 103 ++++++++++++++++++++++++ storage/remote/remote.go | 10 +++ 5 files changed, 289 insertions(+) create mode 100644 storage/remote/graphite/client.go create mode 100644 storage/remote/graphite/client_test.go create mode 100644 storage/remote/graphite/escape.go diff --git a/cmd/prometheus/config.go b/cmd/prometheus/config.go index 585619bb2..1f5c099d0 100644 --- a/cmd/prometheus/config.go +++ b/cmd/prometheus/config.go @@ -163,6 +163,18 @@ func init() { ) // Remote storage. + cfg.fs.StringVar( + &cfg.remote.GraphiteAddress, "storage.remote.graphite-address", "", + "The host:port of the remote Graphite server to send samples to. None, if empty.", + ) + cfg.fs.StringVar( + &cfg.remote.GraphiteTransport, "storage.remote.graphite-transport", "tcp", + "Transport protocol to use to communicate with Graphite. 'tcp', if empty.", + ) + cfg.fs.StringVar( + &cfg.remote.GraphitePrefix, "storage.remote.graphite-prefix", "", + "The prefix to prepend to all metrics exported to Graphite. None, if empty.", + ) cfg.fs.StringVar( &cfg.remote.OpentsdbURL, "storage.remote.opentsdb-url", "", "The URL of the remote OpenTSDB server to send samples to. None, if empty.", diff --git a/storage/remote/graphite/client.go b/storage/remote/graphite/client.go new file mode 100644 index 000000000..7aa5a3efe --- /dev/null +++ b/storage/remote/graphite/client.go @@ -0,0 +1,107 @@ +// Copyright 2015 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package graphite + +import ( + "bytes" + "fmt" + "math" + "net" + "sort" + "time" + + "github.com/prometheus/common/log" + "github.com/prometheus/common/model" +) + +// Client allows sending batches of Prometheus samples to Graphite. +type Client struct { + address string + transport string + timeout time.Duration + prefix string +} + +// NewClient creates a new Client. +func NewClient(address string, transport string, timeout time.Duration, prefix string) *Client { + return &Client{ + address: address, + transport: transport, + timeout: timeout, + prefix: prefix, + } +} + +func pathFromMetric(m model.Metric, prefix string) string { + var buffer bytes.Buffer + + buffer.WriteString(prefix) + buffer.WriteString(escape(m[model.MetricNameLabel])) + + // We want to sort the labels. + labels := make(model.LabelNames, 0, len(m)) + for l, _ := range m { + labels = append(labels, l) + } + sort.Sort(labels) + + // For each label, in order, add ".