Pure parsing for the Graphite plaintext line protocol (spec §10.1).
No dependency on thousand_island — compiles and is testable
regardless of whether that optional dependency is present.
Summary
Functions
Splits a dotted path into {metric, labels} per a compiled template.
Compiles a dot-separated template string into matchable tokens.
Parses one Graphite plaintext line: metric value timestamp.
Types
@type template() :: [String.t()]
Functions
Splits a dotted path into {metric, labels} per a compiled template.
Compiles a dot-separated template string into matchable tokens.
"*" skips a path segment. Any other token is a literal label key,
matched against the segment at that position. The token "metric"
must appear exactly once, only as the final token — it then greedily
consumes all remaining path segments, joined with ..
compile_template("*.forklift.metric") #=> {:ok, ["*", "forklift", "metric"]}nil compiles to nil: the whole dotted path is used verbatim as
the metric name, with no labels — zero config for plain ingest.
@spec parse_line(String.t(), template() | nil) :: {:ok, {String.t(), map(), float(), integer()}} | :error
Parses one Graphite plaintext line: metric value timestamp.
Dispatches to Graphite 1.1+ tag syntax (metric;tag=val;...) when a
; is present in the metric field, independent of any template;
otherwise applies template to the dotted path.
parse_line("forklift.FL-07.engine.temp 94.2 1752931200", ["*", "forklift", "metric"])
#=> {:ok, {"engine.temp", %{"forklift" => "FL-07"}, 94.2, 1752931200}}Trailing \r is stripped so CRLF-terminated lines work. Rejects
malformed field counts, non-numeric values, the literal "nan"
value (used by collectd's write_graphite for undefined
datapoints — bg_samples.value is REAL NOT NULL, and letting NaN
through would poison aggregate SUM/AVG/MIN/MAX), and
non-integer timestamps (a float-looking timestamp is malformed, not
truncated).