Commit 8ecff0a4 authored by Bernd Zeimetz's avatar Bernd Zeimetz

Imported Upstream version 0.44

parent 3f9a9d4e
......@@ -14,23 +14,26 @@
CFLAGS ?= -O2 -Wall
# if your compiler doesn't support OpenMP, comment out this line
CC += -fopenmp
# if your compiler doesn't support OpenMP, comment out this line, or
# define OPENMP_FLAGS to be empty
OPENMP_FLAGS ?= -fopenmp
override CC += $(OPENMP_FLAGS)
GIT_VERSION := $(shell git describe --abbrev=6 --dirty --always || date +%F)
GVCFLAGS += -DGIT_VERSION=\"$(GIT_VERSION)\"
override CFLAGS += $(GVCFLAGS) `pkg-config openssl --cflags` -pthread
override CFLAGS += $(GVCFLAGS) -pthread
SOCKET_LIBS =
ifeq ($(shell uname), SunOS)
SOCKET_LIBS += -lsocket -lnsl
endif
override LIBS += `pkg-config openssl --libs` $(SOCKET_LIBS) -pthread
override LIBS += $(SOCKET_LIBS) -pthread
OBJS = \
relay.o \
md5.o \
consistent-hash.o \
receptor.o \
dispatcher.o \
......
......@@ -13,7 +13,7 @@ project provides a multithreaded relay which can address multiple
targets and clusters for each and every metric based on pattern matches.
There are a couple more replacement projects out there we know of, which
are [carbon-relay-ng](https://github.com/rcrowley/carbon-relay-ng) and [graphite-relay](https://github.com/markchadwick/graphite-relay
are [carbon-relay-ng](https://github.com/graphite-ng/carbon-relay-ng) and [graphite-relay](https://github.com/markchadwick/graphite-relay
)
Compared to carbon-relay-ng, this project does provide carbon's
......@@ -42,7 +42,8 @@ rule sees the metric:
to other targets: metric, value and timestamp will be separated by
a single space only, ever)
- irregular char replacement with underscores (\_), currently
irregular is defined as not being in [0-9a-zA-Z-_:#].
irregular is defined as not being in [0-9a-zA-Z-_:#], but can be
overridden on the command line.
The route file syntax is as follows:
......@@ -51,7 +52,11 @@ The route file syntax is as follows:
cluster <name>
<forward | any_of [useall] | failover | <carbon_ch | fnv1a_ch> [replication <count>]>
<host[:port] [proto <udp | tcp>]> ...
<host[:port][=instance] [proto <udp | tcp>]> ...
;
cluster <name>
file
</path/to/file> ...
;
match <* | <expression>>
send to <cluster | blackhole>
......@@ -64,6 +69,7 @@ aggregate
<expression> ...
every <interval> seconds
expire after <expiration> seconds
[timestamp at <start | middle | end> of bucket]
compute <sum | count | max | min | average> write to
<metric>
[compute ...]
......@@ -71,7 +77,8 @@ aggregate
```
Multiple clusters can be defined, and need not to be referenced by a
match rule. All clusters point to one or more hosts. `host` may be an
match rule. All clusters point to one or more hosts, except the `file`
cluster which writes to files in the local filesystem. `host` may be an
IPv4 or IPv6 address, or a hostname. Since host is followed by an
optional `:` and port, for IPv6 addresses not to be interpreted wrongly,
either a port must be given, or the IPv6 address surrounded by brackets,
......@@ -79,8 +86,9 @@ e.g. `[::1]`. An optional `proto udp` or `proto tcp` may be added to
specify the use of UDP or TCP to connect to the remote server. When
omitted this defaults to a TCP connection.
A `forward` cluster simply sends everything it receives to all defined
members (host addresses). The `any_of` cluster is a small
The `forward` and `file` clusters simply send everything they receive to
all defined members (host addresses or files). The `any_of` cluster is
a small
variant of the `forward` cluster, but instead of sending to all defined
members, it sends each incoming metric to one of defined members. This
is not much useful in itself, but since any of the members can receive
......@@ -100,7 +108,15 @@ faster but more importantly defined to get by a limitation of
when multiple targets live on the same host just separated by port. The
instance that original carbon uses to get around this can be set by
appending it after the port, separated by an equals sign, e.g.
`127.0.0.1:2006=a` for instance `a`.
`127.0.0.1:2006=a` for instance `a`. When using the `fnv1a_ch` cluster,
this instance overrides the hash key in use. This allows for many
things, including masquerading old IP addresses, but mostly to make the
hash key location to become agnostic of the (physical) location of that
key. For example, usage like
`10.0.0.1:2003=4d79d13554fa1301476c1f9fe968b0ac` would allow to change
port and/or ip address of the server that receives data for the instance
key. Obviously, this way migration of data can be dealt with much more
conveniently.
DNS hostnames are resolved to a single address, according to the preference
rules in [RFC 3484](https://www.ietf.org/rfc/rfc3484.txt). The `any_of`
......@@ -355,6 +371,7 @@ e.g. for each hostname encountered. A typical aggregation looks like:
^sys\.dc2\.somehost-[0-9]+\.somecluster\.mysql\.replication_delay
every 10 seconds
expire after 35 seconds
timestamp at end of bucket
compute sum write to
mysql.somecluster.total_replication_delay
compute average write to
......@@ -378,6 +395,10 @@ before considering a data bucket (which is aggregated) to be complete.
In the example, 35 was used, which means after 35 + 10 seconds the first
four aggregate metrics are produced. It also means that metrics can
arrive 35 seconds late, and still be taken into account.
The `timestamp` that is used for the aggregations can be specified to be
the `start`, `middle` or `end` of the bucket. Original
carbon-aggregator.py uses `start`, while carbon-c-relay's default has
always been `end`.
The `compute` clauses demonstrate a single aggregation rule can produce
multiple aggregates, as often is the case. Internally, this comes for
free, since all possible aggregates are always calculated, whether or
......
......@@ -22,6 +22,7 @@
#include <time.h>
#include <regex.h>
#include <pthread.h>
#include <assert.h>
#include "relay.h"
#include "dispatcher.h"
......@@ -40,7 +41,10 @@ static char keep_running = 1;
* and expiry time.
*/
aggregator *
aggregator_new(unsigned int interval, unsigned int expire)
aggregator_new(
unsigned int interval,
unsigned int expire,
enum _aggr_timestamp tswhen)
{
aggregator *ret = malloc(sizeof(aggregator));
......@@ -59,6 +63,7 @@ aggregator_new(unsigned int interval, unsigned int expire)
ret->interval = interval;
ret->expire = expire;
ret->tswhen = tswhen;
ret->bucketcnt = (expire / interval) * 2 + 1 ;
ret->received = 0;
ret->sent = 0;
......@@ -86,13 +91,13 @@ aggregator_add_compute(
if (strcmp(type, "sum") == 0) {
act = SUM;
} else if (strcmp(type, "count") == 0) {
} else if (strcmp(type, "count") == 0 || strcmp(type, "cnt") == 0) {
act = CNT;
} else if (strcmp(type, "max") == 0) {
act = MAX;
} else if (strcmp(type, "min") == 0) {
act = MIN;
} else if (strcmp(type, "average") == 0) {
} else if (strcmp(type, "average") == 0 || strcmp(type, "avg") == 0) {
act = AVG;
} else {
return -1;
......@@ -147,7 +152,7 @@ aggregator_putmetric(
/* get value */
if ((v = strchr(firstspace + 1, ' ')) == NULL) {
/* metric includes \n */
if (mode == DEBUG)
if (mode == DEBUG || mode == DEBUGTEST)
logerr("aggregator: dropping incorrect metric: %s",
metric);
return;
......@@ -204,13 +209,16 @@ aggregator_putmetric(
}
invocation->hash = omhash;
/* Start buckets in the past with a splay, but before expiry
* the splay is necessary to avoid a thundering herd of
* expirations when the aggregator is spammed with metrics,
* e.g. right after startup when other relays flush their
* queues. */
/* Start buckets in the past such that expiry time
* conditions are met. Add a splay to the expiry time to
* avoid a thundering herd of expirations when the
* aggregator is spammed with metrics, e.g. right after
* startup when other relays flush their queues. This
* approach shouldn't affect the timing of the buckets as
* requested in issue #72. */
time(&now);
now -= s->expire - 1 - (rand() % s->interval);
now -= s->expire;
invocation->expire = s->expire + (rand() % s->interval);
/* allocate enough buckets to hold the past + future */
invocation->buckets =
......@@ -242,7 +250,7 @@ aggregator_putmetric(
slot = itime / s->interval;
if (slot >= s->bucketcnt) {
if (mode == DEBUG)
if (mode == DEBUG || mode == DEBUGTEST)
logerr("aggregator: dropping metric too far in the "
"future (%lld > %lld): %s from %s", epoch,
invocation->buckets[s->bucketcnt - 1].start,
......@@ -291,6 +299,7 @@ aggregator_expire(void *sub)
server *submission = (server *)sub;
char metric[METRIC_BUFSIZ];
char isempty;
long long int ts = 0;
while (1) {
work = 0;
......@@ -305,43 +314,53 @@ aggregator_expire(void *sub)
lastinv = NULL;
isempty = 0;
for (inv = c->invocations_ht[i]; inv != NULL; ) {
while (inv->buckets[0].start + s->expire < now) {
while (inv->buckets[0].start +
(keep_running ? inv->expire : s->expire) < now)
{
/* yay, let's produce something cool */
b = &inv->buckets[0];
/* avoid emitting empty/unitialised data */
isempty = b->cnt == 0;
if (!isempty) {
switch (s->tswhen) {
case TS_START:
ts = b->start;
break;
case TS_MIDDLE:
ts = b->start + (s->interval / 2);
break;
case TS_END:
ts = b->start + s->interval;
break;
default:
assert(0);
}
switch (c->type) {
case SUM:
snprintf(metric, sizeof(metric),
"%s %f %lld\n",
inv->metric, b->sum,
b->start + s->interval);
inv->metric, b->sum, ts);
break;
case CNT:
snprintf(metric, sizeof(metric),
"%s %zd %lld\n",
inv->metric, b->cnt,
b->start + s->interval);
inv->metric, b->cnt, ts);
break;
case MAX:
snprintf(metric, sizeof(metric),
"%s %f %lld\n",
inv->metric, b->max,
b->start + s->interval);
inv->metric, b->max, ts);
break;
case MIN:
snprintf(metric, sizeof(metric),
"%s %f %lld\n",
inv->metric, b->min,
b->start + s->interval);
inv->metric, b->min, ts);
break;
case AVG:
snprintf(metric, sizeof(metric),
"%s %f %lld\n",
inv->metric,
b->sum / (double)b->cnt,
b->start + s->interval);
b->sum / (double)b->cnt, ts);
break;
}
server_send(submission, strdup(metric), 1);
......
......@@ -27,6 +27,7 @@
typedef struct _aggregator {
unsigned short interval; /* when to perform the aggregation */
unsigned short expire; /* when incoming metrics are no longer valid */
enum _aggr_timestamp { TS_START, TS_MIDDLE, TS_END } tswhen;
unsigned char bucketcnt;
size_t received;
size_t sent;
......@@ -37,6 +38,7 @@ typedef struct _aggregator {
struct _aggr_invocations {
char *metric; /* actual name to emit */
unsigned int hash; /* to speed up matching */
unsigned short expire; /* expire + splay */
struct _bucket {
long long int start;
size_t cnt;
......@@ -52,7 +54,7 @@ typedef struct _aggregator {
struct _aggregator *next;
} aggregator;
aggregator *aggregator_new(unsigned int interval, unsigned int expire);
aggregator *aggregator_new(unsigned int interval, unsigned int expire, enum _aggr_timestamp tswhen);
char aggregator_add_compute(aggregator *s, const char *metric, const char *type);
void aggregator_putmetric(aggregator *s, const char *metric, const char *firstspace, size_t nmatch, regmatch_t *pmatch);
int aggregator_start(server *submission);
......
......@@ -223,6 +223,8 @@ collector_writer(void *unused)
{
int i = 0;
size_t queued;
size_t queuesize;
double queueusage;
size_t totdropped;
size_t numaggregators = aggregator_numaggregators();
server **srvs = NULL;
......@@ -243,12 +245,15 @@ collector_writer(void *unused)
totdropped = 0;
for (i = 0; srvs[i] != NULL; i++) {
queued = server_get_queue_len(srvs[i]);
queuesize = server_get_queue_size(srvs[i]);
totdropped += server_get_dropped(srvs[i]);
queueusage = (double)queued / (double)queuesize;
if (queued > 150)
if (queueusage >= 0.75)
logout("warning: metrics queuing up "
"for %s:%u: %zd metrics\n",
server_ip(srvs[i]), server_port(srvs[i]), queued);
"for %s:%u: %zd metrics (%d%% of queue size)\n",
server_ip(srvs[i]), server_port(srvs[i]), queued,
(int)(queueusage * 100));
}
if (totdropped - lastdropped > 0)
logout("warning: dropped %zd metrics\n", totdropped - lastdropped);
......@@ -296,7 +301,7 @@ collector_start(dispatcher **d, cluster *c, server *submission)
dispatchers = d;
collector_schedulereload(c);
if (mode == DEBUG)
if (mode == DEBUG || mode == DEBUGTEST)
debug = 1;
if (mode != SUBMISSION) {
......
......@@ -18,9 +18,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/md5.h>
#include <assert.h>
#include "md5.h"
#include "server.h"
#define CH_RING struct _ch_ring
......@@ -78,12 +78,60 @@ fnv1a_hashpos(const char *key, const char *end)
}
/**
* Qsort comparator for ch_ring_entry structs on pos.
* Sort comparator for ch_ring_entry structs on pos, ip and instance.
*/
static int
entrycmp(const void *l, const void *r)
entrycmp_carbon(const void *l, const void *r)
{
return ((ch_ring_entry *)l)->pos - ((ch_ring_entry *)r)->pos;
ch_ring_entry *ch_l = (ch_ring_entry *)l;
ch_ring_entry *ch_r = (ch_ring_entry *)r;
if (ch_l->pos != ch_r->pos)
return ch_l->pos - ch_r->pos;
#ifndef CH_CMP_V40_BEHAVIOUR
{
int d = strcmp(server_ip(ch_l->server), server_ip(ch_r->server));
char *i_l, *i_r;
if (d != 0)
return d;
i_l = server_instance(ch_l->server);
i_r = server_instance(ch_r->server);
if (i_l == NULL && i_r == NULL)
return 0;
if (i_l == NULL)
return 1;
if (i_r == NULL)
return -1;
return strcmp(i_l, i_r);
}
#endif
return 0;
}
/**
* Sort comparator for ch_ring_entry structs on pos, ip and port.
*/
static int
entrycmp_fnv1a(const void *l, const void *r)
{
ch_ring_entry *ch_l = (ch_ring_entry *)l;
ch_ring_entry *ch_r = (ch_ring_entry *)r;
if (ch_l->pos != ch_r->pos)
return ch_l->pos - ch_r->pos;
#ifndef CH_CMP_V40_BEHAVIOUR
{
int d = strcmp(server_ip(ch_l->server), server_ip(ch_r->server));
if (d != 0)
return d;
return server_port(ch_l->server) - server_port(ch_r->server);
}
#endif
return 0;
}
ch_ring *
......@@ -114,6 +162,8 @@ ch_addnode(ch_ring *ring, server *s)
int i;
char buf[256];
ch_ring_entry *entries;
char *instance = server_instance(s);
int (*cmp)(const void *, const void *) = NULL;
if (ring == NULL)
return NULL;
......@@ -126,7 +176,6 @@ ch_addnode(ch_ring *ring, server *s)
switch (ring->type) {
case CARBON:
for (i = 0; i < ring->hash_replicas; i++) {
char *instance = server_instance(s);
/* this format is actually Python's tuple format that is
* used in serialised form as input for the hash */
snprintf(buf, sizeof(buf), "('%s', %s%s%s):%d",
......@@ -135,33 +184,41 @@ ch_addnode(ch_ring *ring, server *s)
instance == NULL ? "None" : instance,
instance == NULL ? "" : "'",
i);
/* TODO:
/* carbon upstream committed:
* https://github.com/graphite-project/carbon/commit/024f9e67ca47619438951c59154c0dec0b0518c7
* Question is how harmful the collision is -- it will probably
* change the location of some metrics */
* this makes sure no collissions exist on pos, however,
* at the expense of being agnostic to the input order,
* therefore that change isn't implemented here, see
* https://github.com/grobian/carbon-c-relay/issues/84 */
entries[i].pos = carbon_hashpos(buf, buf + strlen(buf));
entries[i].server = s;
entries[i].next = NULL;
entries[i].malloced = 0;
}
cmp = *entrycmp_carbon;
break;
case FNV1a:
for (i = 0; i < ring->hash_replicas; i++) {
/* take all server info into account, such that
* different port numbers for the same hosts will work
* (unlike CARBON) */
snprintf(buf, sizeof(buf), "%d-%s:%u",
i, server_ip(s), server_port(s));
* (unlike CARBON), unless we got a full overrride */
if (instance == NULL) {
snprintf(buf, sizeof(buf), "%d-%s:%u",
i, server_ip(s), server_port(s));
} else {
snprintf(buf, sizeof(buf), "%d-%s", i, instance);
}
entries[i].pos = fnv1a_hashpos(buf, buf + strlen(buf));
entries[i].server = s;
entries[i].next = NULL;
entries[i].malloced = 0;
}
cmp = *entrycmp_fnv1a;
break;
}
/* sort to allow merge joins later down the road */
qsort(entries, ring->hash_replicas, sizeof(ch_ring_entry), *entrycmp);
qsort(entries, ring->hash_replicas, sizeof(ch_ring_entry), cmp);
entries[0].malloced = 1;
if (ring->entries == NULL) {
......@@ -175,7 +232,7 @@ ch_addnode(ch_ring *ring, server *s)
last = NULL;
assert(ring->hash_replicas > 0);
for (w = ring->entries; w != NULL && i < ring->hash_replicas; ) {
if (w->pos < entries[i].pos) {
if (cmp(&w->pos, &entries[i].pos) <= 0) {
last = w;
w = w->next;
} else {
......@@ -256,6 +313,45 @@ ch_get_nodes(
}
}
void
ch_printhashring(ch_ring *ring, FILE *f)
{
ch_ring_entry *w;
char column = 0;
char srvbuf[21];
for (w = ring->entries; w != NULL; w = w->next) {
snprintf(srvbuf, sizeof(srvbuf), "%s:%d%s%s",
server_ip(w->server),
server_port(w->server),
server_instance(w->server) ? "=" : "",
server_instance(w->server) ? server_instance(w->server) : "");
fprintf(f, "%5d@%-20s", w->pos, srvbuf);
if (column < 2) {
fprintf(f, " ");
column++;
} else {
fprintf(f, "\n");
column = 0;
}
}
}
unsigned short
ch_gethashpos(ch_ring *ring, const char *key, const char *end)
{
switch (ring->type) {
case CARBON:
return carbon_hashpos(key, end);
case FNV1a:
return fnv1a_hashpos(key, end);
default:
assert(0); /* this shouldn't happen */
}
return 0; /* pacify compiler */
}
/**
* Frees the ring structure and its added nodes.
*/
......
......@@ -18,6 +18,8 @@
#ifndef CONSISTENT_HASH_H
#define CONSISTENT_HASH_H 1
#include <stdio.h>
#include "server.h"
#include "router.h"
......@@ -35,6 +37,8 @@ void ch_get_nodes(
const char replcnt,
const char *metric,
const char *firstspace);
void ch_printhashring(ch_ring *ring, FILE *out);
unsigned short ch_gethashpos(ch_ring *ring, const char *key, const char *end);
void ch_free(ch_ring *ring);
#endif
../../LICENSE.md
\ No newline at end of file
# -----------------------------------------------------------------------------
# Makefile for carbon-c-relay
#
# Author: Jose Riguera <jriguera@gmail.com> based on the work of Fabian Groffen
# Date : 2014-10-12
#
# -----------------------------------------------------------------------------
# creation of initial debian folder:
# dh_make --native -c apache -s -p carbon-c-relay_0.35
CC = gcc
LINKER = gcc -o
RM = rm -f
MD = mkdir -p
GIT = git
INSTALL = install
DEBUILD = debuild
DEBCLEAN = debclean
# project name (generate executable with this name)
DESTDIR = /usr/local/etc/carbon
PREFIX = /usr/local
TARGET = carbon-c-relay
GIT_VERSION := $(shell git describe --abbrev=6 --dirty --always || date +%F)
GVCFLAGS += -DGIT_VERSION=\"$(GIT_VERSION)\"
# change these to set the proper directories where each files shoould be
SRCDIR = src
OBJDIR = obj
BINDIR = sbin
# compiling flags here
CFLAGS ?= -O2 -Wall
override CFLAGS += $(GVCFLAGS) `pkg-config openssl --cflags` -pthread
# linking flags here
override LIBS += `pkg-config openssl --libs` -pthread
ifeq ($(shell uname), SunOS)
override LIBS += -lsocket -lnsl
endif
LFLAGS = -O2 -Wall -lm $(LIBS)
SOURCES := $(wildcard $(SRCDIR)/*.c)
INCLUDES := $(wildcard $(SRCDIR)/*.h)
OBJECTS := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
all: folders $(BINDIR)/$(TARGET)
$(BINDIR)/$(TARGET): $(OBJECTS)
$(LINKER) $@ $(OBJECTS) $(LFLAGS)
@echo "Linking complete. Binary file created!"
$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
$(CC) $(CFLAGS) -c $< -o $@
@echo "Compiled "$<" successfully!"
.PHONY: folders
folders:
@$(MD) $(BINDIR)
@$(MD) $(OBJDIR)
.PHONY: install
install: $(BINDIR)/$(TARGET)
$(INSTALL) -m 0755 $< $(PREFIX)/$(BINDIR)/$(TARGET)
$(INSTALL) -d $(DESTDIR)
$(INSTALL) -m 0644 conf/* $(DESTDIR)
.PHONY: uninstall
uninstall:
$(RM) $(PREFIX)/$(BINDIR)/$(TARGET)
$(RM) -rf $(DESTDIR)/etc/$(TARGET)
VERSION = $(shell sed -n '/VERSION/s/^.*"\([0-9.]\+\)".*$$/\1/p' $(SRCDIR)/relay.h)
.PHONY: dist
dist:
@$(GIT) archive \
--format=tar.gz \
--prefix=$(TARGET)-$(VERSION)/ v$(VERSION) \
> $(TARGET)-$(VERSION).tar.gz
@echo "Created $(TARGET)-$(VERSION).tar.gz successfully!"
# check if the local environment is suitable to generate a package
# we check environment variables and a gpg private key matching
# these variables. this is necessary as we sign our packages.
.PHONY: checkenv
checkenv:
ifndef DEBEMAIL
echo "Missing environment variable DEBEMAIL"
@exit 1
endif
ifndef DEBFULLNAME
echo "Missing environment variable DEBFULLNAME"
@exit 1
endif
#gpg --list-secret-keys "$(DEBFULLNAME) <$(DEBEMAIL)>" >/dev/null
# creates the .deb package and other related files
# all files are placed in ../
.PHONY: builddeb
debuild: checkenv
# dpkg-buildpackage + lintian
# -sa Forces the inclusion of the original source.
# -us Do not sign the source package.
# -uc Do not sign the .changes file.
$(DEBUILD) -us -uc -sa -b -rfakeroot -i -I.git -I.gitignore -I$(BINDIR) -I$(OBJDIR) -I$(TARGET)-$(VERSION).tar.gz
# create a new release based on PW_VERSION variable
.PHONY: newrelease
newrelease:
debchange --changelog debian/changelog --urgency high --newversion $(VERSION)-1 "Releasing $(TARGET) $(VERSION)"
# creates a new version in debian/changelog
.PHONY: newversion
newversion:
debchange --changelog debian/changelog -i --urgency high
# allow user to enter one or more changelog comment manually
.PHONY: changelog
changelog:
debchange --changelog debian/changelog --force-distribution dist --urgency high -r
debchange --changelog debian/changelog -a
.PHONY: clean
clean:
@$(RM) $(OBJECTS)
@echo "Cleanup complete!"
.PHONY: dist-clean
distclean: clean
@$(RM) -r $(BINDIR)
@$(RM) -r $(OBJDIR)
@$(RM) $(TARGET)-$(VERSION).tar.gz
@echo "Executable removed!"
../../README.md
\ No newline at end of file
# comments are allowed in any place and start with a hash (#)
#
#cluster <name>
# <forward | any_of | <carbon_ch | fnv1a_ch> [replication <count>]>
# <host[:port] [proto <udp | tcp>]> ...
# ;
#
#rewrite <expression>
# into <replacement>
# ;
#
#aggregate
# <expression> ...
# every <interval> seconds
# expire after <expiration> seconds
# compute <sum | count | max | min | average> write to
# <metric>
# [compute ...]
# ;
#
#match <* | <expression>>
# send to <cluster | blackhole>
# [stop]
# ;
#
# example configuration for simply forwarding everything to a locally
# running process that listens on port 2013:
cluster local_carbon
forward
127.0.0.1:2013
;
match *
send to local_carbon
stop
;
The Debian Package carbon-c-relay
----------------------------
carbon-c-relay
-- Jose Riguera <jriguera@gmail.com> Mon, 13 Oct 2014 00:07:24 +0200