labels = new LinkedHashMap<>();
+ private ParseState state;
+
+ public void reset() {
+ name.setLength(0);
+ labelname.setLength(0);
+ labelvalue.setLength(0);
+ value.setLength(0);
+ timestamp.setLength(0);
+ labels.clear();
+ state = ParseState.NAME;
+ }
+
+ public TextSample parse(String line) {
+ reset();
+
+ for (int c = 0; c < line.length(); c++) {
+ char charAt = line.charAt(c);
+
+ switch (state) {
+ case NAME:
+ if (charAt == '{') {
+ state = ParseState.STARTOFLABELNAME;
+ } else if (charAt == ' ' || charAt == '\t') {
+ state = ParseState.ENDOFNAME;
+ } else {
+ name.append(charAt);
+ }
+ break;
+ case ENDOFNAME:
+ if (charAt == ' ' || charAt == '\t') {
+ // do nothing
+ } else if (charAt == '{') {
+ state = ParseState.STARTOFLABELNAME;
+ } else {
+ value.append(charAt);
+ state = ParseState.VALUE;
+ }
+ break;
+ case STARTOFLABELNAME:
+ if (charAt == ' ' || charAt == '\t') {
+ // do nothing
+ } else if (charAt == '}') {
+ state = ParseState.ENDOFLABELS;
+ } else {
+ labelname.append(charAt);
+ state = ParseState.LABELNAME;
+ }
+ break;
+ case LABELNAME:
+ if (charAt == '=') {
+ state = ParseState.LABELVALUEQUOTE;
+ } else if (charAt == '}') {
+ state = ParseState.ENDOFLABELS;
+ } else if (charAt == ' ' || charAt == '\t') {
+ state = ParseState.LABELVALUEEQUALS;
+ } else {
+ labelname.append(charAt);
+ }
+ break;
+ case LABELVALUEEQUALS:
+ if (charAt == '=') {
+ state = ParseState.LABELVALUEQUOTE;
+ } else if (charAt == ' ' || charAt == '\t') {
+ // do nothing
+ } else {
+ throw new IllegalStateException("Invalid line: " + line);
+ }
+ break;
+ case LABELVALUEQUOTE:
+ if (charAt == '"') {
+ state = ParseState.LABELVALUE;
+ } else if (charAt == ' ' || charAt == '\t') {
+ // do nothing
+ } else {
+ throw new IllegalStateException("Invalid line: " + line);
+ }
+ break;
+ case LABELVALUE:
+ if (charAt == '\\') {
+ state = ParseState.LABELVALUESLASH;
+ } else if (charAt == '"') {
+ labels.put(labelname.toString(), labelvalue.toString());
+ labelname.setLength(0);
+ labelvalue.setLength(0);
+ state = ParseState.NEXTLABEL;
+ } else {
+ labelvalue.append(charAt);
+ }
+ break;
+ case LABELVALUESLASH:
+ state = ParseState.LABELVALUE;
+ if (charAt == '\\') {
+ labelvalue.append('\\');
+ } else if (charAt == 'n') {
+ labelvalue.append('\n');
+ } else if (charAt == '"') {
+ labelvalue.append('"');
+ } else {
+ labelvalue.append('\\').append(charAt);
+ }
+ break;
+ case NEXTLABEL:
+ if (charAt == ',') {
+ state = ParseState.LABELNAME;
+ } else if (charAt == '}') {
+ state = ParseState.ENDOFLABELS;
+ } else if (charAt == ' ' || charAt == '\t') {
+ // do nothing
+ } else {
+ throw new IllegalStateException("Invalid line: " + line);
+ }
+ break;
+ case ENDOFLABELS:
+ if (charAt == ' ' || charAt == '\t') {
+ // do nothing
+ } else {
+ value.append(charAt);
+ state = ParseState.VALUE;
+ }
+ break;
+ case VALUE:
+ if (charAt == ' ' || charAt == '\t') {
+ state = ParseState.TIMESTAMP;
+ } else {
+ value.append(charAt);
+ }
+ break;
+ case TIMESTAMP:
+ if (charAt == ' ' || charAt == '\t') {
+ return buildSample(line);
+ }
+ else {
+ timestamp.append(charAt);
+ }
+ break;
+ }
+ }
+
+ return buildSample(line);
+ }
+
+ private TextSample buildSample(String line) {
+ return new TextSample.Builder()
+ .setLine(line)
+ .setName(name.toString())
+ .setValue(value.toString())
+ .setTimestamp(Util.tryConvertTimestamp(timestamp))
+ .addLabels(new LinkedHashMap<>(labels))
+ .build();
+ }
+
+}
\ No newline at end of file
diff --git a/features/prometheus-collector/src/main/java/org/hawkular/agent/prometheus/text/OpenMetricsFormatStrategy.java b/features/prometheus-collector/src/main/java/org/hawkular/agent/prometheus/text/OpenMetricsFormatStrategy.java
new file mode 100644
index 000000000000..715bcbc9d491
--- /dev/null
+++ b/features/prometheus-collector/src/main/java/org/hawkular/agent/prometheus/text/OpenMetricsFormatStrategy.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to The OpenNMS Group, Inc (TOG) under one or more
+ * contributor license agreements. See the LICENSE.md file
+ * distributed with this work for additional information
+ * regarding copyright ownership.
+ *
+ * TOG licenses this file to You under the GNU Affero General
+ * Public License Version 3 (the "License") or (at your option)
+ * any later version. You may not use this file except in
+ * compliance with the License. You may obtain a copy of the
+ * License at:
+ *
+ * https://www.gnu.org/licenses/agpl-3.0.txt
+ *
+ * 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 org.hawkular.agent.prometheus.text;
+
+import java.util.Map;
+
+import org.hawkular.agent.prometheus.Util;
+import org.hawkular.agent.prometheus.types.Counter;
+import org.hawkular.agent.prometheus.types.Gauge;
+import org.hawkular.agent.prometheus.types.Histogram;
+import org.hawkular.agent.prometheus.types.Metric;
+import org.hawkular.agent.prometheus.types.MetricType;
+import org.hawkular.agent.prometheus.types.Summary;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ * Strategy for processing OpenMetrics v1 text data.
+ *
+ * The strategy is very similar to the Prometheus one but counters have a mandatory _total suffix and there is supported for the "created" data sample.
+ *
+ *
+ */
+enum OpenMetricsFormatStrategy implements TextFormatStrategy {
+ INSTANCE;
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(OpenMetricsFormatStrategy.class);
+
+ @Override
+ public TextSampleProcessor getSampleProcessor(MetricType type) {
+
+ switch (type) {
+ case COUNTER:
+ return OpenMetricsFormatStrategy::handleCounterSample;
+ case GAUGE:
+ //treat UNKNOWN as gauge
+ case UNKNOWN:
+ return OpenMetricsFormatStrategy::handleGaugeSample;
+ case SUMMARY:
+ return OpenMetricsFormatStrategy::handleSummarySample;
+ case HISTOGRAM:
+ return OpenMetricsFormatStrategy::handleHistogramSample;
+ default:
+ throw new IllegalArgumentException("Unknown metric type: " + type);
+ }
+ }
+
+ @Override
+ public SampleNameValidator getNameValidator(MetricType type, String familyName) {
+
+ switch (type) {
+ case COUNTER:
+ return SampleNameValidator.matchingNameWithSuffixes(familyName, "_total", "_created");
+ case GAUGE:
+ case UNKNOWN:
+ return SampleNameValidator.matchingNameWithSuffixes(familyName, "", "_created");
+ case SUMMARY:
+ return SampleNameValidator.matchingNameWithSuffixes(familyName, "", "_count", "_sum", "_created");
+ case HISTOGRAM:
+ return SampleNameValidator.matchingNameWithSuffixes(familyName, "_count", "_sum", "_bucket", "_created");
+ default:
+ throw new IllegalArgumentException("Unknown metric type: " + type);
+ }
+ }
+
+
+ private static void handleCounterSample(Map