You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by ha...@apache.org on 2020/08/31 04:25:56 UTC

[skywalking] branch meter-histogram created (now aa7f9cf)

This is an automated email from the ASF dual-hosted git repository.

hanahmily pushed a change to branch meter-histogram
in repository https://gitbox.apache.org/repos/asf/skywalking.git.


      at aa7f9cf  Add labeled histogram

This branch includes the following new commits:

     new aa7f9cf  Add labeled histogram

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[skywalking] 01/01: Add labeled histogram

Posted by ha...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

hanahmily pushed a commit to branch meter-histogram
in repository https://gitbox.apache.org/repos/asf/skywalking.git

commit aa7f9cf1f479bd0b00f42d44f5f7555fc083712d
Author: Gao Hongtao <ha...@gmail.com>
AuthorDate: Mon Aug 31 12:24:57 2020 +0800

    Add labeled histogram
    
    Signed-off-by: Gao Hongtao <ha...@gmail.com>
---
 .../meter/function/AvgHistogramFunction.java       |  32 ++-
 .../analysis/meter/function/BucketedValues.java    |   4 +
 .../promethues/PrometheusMetricConverter.java      |  85 ++++---
 .../oap/server/core/query/type/HeatMap.java        |  18 +-
 .../meter/function/AvgHistogramFunctionTest.java   | 263 +++++++++++++++++++++
 .../library/util/prometheus/parser/Context.java    |  41 ++--
 .../util/prometheus/parser/TextParserTest.java     |  14 ++
 .../src/test/resources/testdata/prometheus.txt     |  24 +-
 8 files changed, 412 insertions(+), 69 deletions(-)

diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/AvgHistogramFunction.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/AvgHistogramFunction.java
index 22985cf..b8346dc 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/AvgHistogramFunction.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/AvgHistogramFunction.java
@@ -18,11 +18,11 @@
 
 package org.apache.skywalking.oap.server.core.analysis.meter.function;
 
-import java.util.Comparator;
+import com.google.common.base.Strings;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import lombok.Getter;
 import lombok.Setter;
 import lombok.ToString;
@@ -32,6 +32,7 @@ import org.apache.skywalking.oap.server.core.UnexpectedException;
 import org.apache.skywalking.oap.server.core.analysis.meter.MeterEntity;
 import org.apache.skywalking.oap.server.core.analysis.metrics.DataTable;
 import org.apache.skywalking.oap.server.core.analysis.metrics.Metrics;
+import org.apache.skywalking.oap.server.core.query.type.Bucket;
 import org.apache.skywalking.oap.server.core.remote.grpc.proto.RemoteData;
 import org.apache.skywalking.oap.server.core.storage.StorageBuilder;
 import org.apache.skywalking.oap.server.core.storage.annotation.Column;
@@ -83,11 +84,17 @@ public abstract class AvgHistogramFunction extends Metrics implements Acceptable
 
         this.entityId = entity.id();
 
+        String template = "%s";
+        if (!Strings.isNullOrEmpty(value.getGroup())) {
+            template   = value.getGroup() + ":%s";
+        }
         final long[] values = value.getValues();
         for (int i = 0; i < values.length; i++) {
-            String bucketName = String.valueOf(value.getBuckets()[i]);
-            summation.valueAccumulation(bucketName, values[i]);
-            count.valueAccumulation(bucketName, 1L);
+            int bucket = value.getBuckets()[i];
+            String bucketName = bucket == Integer.MIN_VALUE ? Bucket.INFINITE_NEGATIVE : String.valueOf(bucket);
+            String key = String.format(template, bucketName);
+            summation.valueAccumulation(key, values[i]);
+            count.valueAccumulation(key, 1L);
         }
     }
 
@@ -107,9 +114,16 @@ public abstract class AvgHistogramFunction extends Metrics implements Acceptable
 
     @Override
     public void calculate() {
-        final List<String> sortedKeys = summation.sortedKeys(Comparator.comparingInt(Integer::parseInt));
-        for (String key : sortedKeys) {
-            dataset.put(key, summation.get(key) / count.get(key));
+        final Set<String> keys = summation.keys();
+        for (String key : keys) {
+            long value = 0;
+            if (count.get(key) != 0) {
+                value = summation.get(key) / count.get(key);
+                if (value == 0L && summation.get(key) > 0L) {
+                    value = 1;
+                }
+            }
+            dataset.put(key, value);
         }
     }
 
@@ -146,6 +160,7 @@ public abstract class AvgHistogramFunction extends Metrics implements Acceptable
 
         this.setCount(new DataTable(remoteData.getDataObjectStrings(0)));
         this.setSummation(new DataTable(remoteData.getDataObjectStrings(1)));
+        this.setDataset(new DataTable(remoteData.getDataObjectStrings(2)));
     }
 
     @Override
@@ -157,6 +172,7 @@ public abstract class AvgHistogramFunction extends Metrics implements Acceptable
 
         remoteBuilder.addDataObjectStrings(count.toStorageData());
         remoteBuilder.addDataObjectStrings(summation.toStorageData());
+        remoteBuilder.addDataObjectStrings(dataset.toStorageData());
 
         return remoteBuilder;
     }
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/BucketedValues.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/BucketedValues.java
index de5e1b9..0f8f825 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/BucketedValues.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/BucketedValues.java
@@ -21,6 +21,7 @@ package org.apache.skywalking.oap.server.core.analysis.meter.function;
 import java.util.Arrays;
 import java.util.List;
 import lombok.Getter;
+import lombok.Setter;
 import lombok.ToString;
 import org.apache.skywalking.oap.server.core.analysis.metrics.DataTable;
 import org.apache.skywalking.oap.server.core.query.type.Bucket;
@@ -32,6 +33,9 @@ import org.apache.skywalking.oap.server.core.query.type.HeatMap;
 @ToString
 @Getter
 public class BucketedValues {
+
+    @Setter
+    private String group;
     /**
      * The element in the buckets represent the minimal value of this bucket, the max is defined by the next element.
      * Such as 0, 10, 50, 100 means buckets are [0, 10), [10, 50), [50, 100), [100, infinite+).
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/metric/promethues/PrometheusMetricConverter.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/metric/promethues/PrometheusMetricConverter.java
index 449d525..02b3395 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/metric/promethues/PrometheusMetricConverter.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/metric/promethues/PrometheusMetricConverter.java
@@ -31,6 +31,7 @@ import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.StringJoiner;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
@@ -78,6 +79,10 @@ public class PrometheusMetricConverter {
 
     private final static String LATEST = "latest";
 
+    private final static String DEFAULT_GROUP = "default";
+
+    private final static List<String> DEFAULT_GROUP_LIST = Collections.singletonList(DEFAULT_GROUP);
+
     private final Window window = new Window();
 
     private final List<MetricsRule> rules;
@@ -192,41 +197,51 @@ public class PrometheusMetricConverter {
                         case AVG_PERCENTILE:
                             Validate.isTrue(sources.size() == 1, "Can't get source for histogram");
                             Map.Entry<MetricSource, List<Metric>> smm = sources.entrySet().iterator().next();
-                            Histogram h = (Histogram) sum(smm.getValue());
-
-                            long[] vv = new long[h.getBuckets().size()];
-                            int[] bb = new int[h.getBuckets().size()];
-                            long v = 0L;
-                            int i = 0;
-                            for (Map.Entry<Double, Long> entry : h.getBuckets().entrySet()) {
-                                long increase = entry.getValue() - v;
-                                vv[i] = window.get(operation.getMetricName(), ImmutableMap.of("le", entry.getKey().toString()))
-                                    .apply(smm.getKey(), (double) increase).longValue();
-                                v = entry.getValue();
-
-                                if (i + 1 < h.getBuckets().size()) {
-                                    bb[i + 1] = BigDecimal.valueOf(entry.getKey()).multiply(SECOND_TO_MILLISECOND).intValue();
-                                }
-
-                                i++;
-                            }
-
-                            if (operation.getName().equals(AVG_HISTOGRAM)) {
-                                AcceptableValue<BucketedValues> heatmapMetrics = service.buildMetrics(
-                                    formatMetricName(operation.getMetricName()), BucketedValues.class);
-                                heatmapMetrics.setTimeBucket(TimeBucket.getMinuteTimeBucket(smm.getKey().getTimestamp()));
-                                heatmapMetrics.accept(smm.getKey().getEntity(), new BucketedValues(bb, vv));
-                                service.doStreamingCalculation(heatmapMetrics);
-                            } else {
-                                AcceptableValue<AvgHistogramPercentileFunction.AvgPercentileArgument> percentileMetrics =
-                                    service.buildMetrics(formatMetricName(operation.getMetricName()), AvgHistogramPercentileFunction.AvgPercentileArgument.class);
-                                percentileMetrics.setTimeBucket(TimeBucket.getMinuteTimeBucket(smm.getKey().getTimestamp()));
-                                percentileMetrics.accept(smm.getKey().getEntity(),
-                                    new AvgHistogramPercentileFunction.AvgPercentileArgument(new BucketedValues(bb, vv), operation.getPercentiles().stream().mapToInt(Integer::intValue).toArray()));
-                                service.doStreamingCalculation(percentileMetrics);
-                            }
-
-                            generateTraffic(smm.getKey().getEntity());
+
+                            smm.getValue().stream()
+                                .collect(groupingBy(m -> Optional.ofNullable(smm.getKey().getGroupBy()).orElse(DEFAULT_GROUP_LIST).stream().map(m.getLabels()::get).collect(Collectors.joining("-"))))
+                                .forEach((group, mm) -> {
+                                    Histogram h = (Histogram) sum(mm);
+
+                                    long[] vv = new long[h.getBuckets().size()];
+                                    int[] bb = new int[h.getBuckets().size()];
+                                    long v = 0L;
+                                    int i = 0;
+                                    for (Map.Entry<Double, Long> entry : h.getBuckets().entrySet()) {
+                                        long increase = entry.getValue() - v;
+                                        vv[i] = window.get(operation.getMetricName(), ImmutableMap.of("group", group, "le", entry.getKey().toString()))
+                                            .apply(smm.getKey(), (double) increase).longValue();
+                                        v = entry.getValue();
+
+                                        if (i + 1 < h.getBuckets().size()) {
+                                            bb[i + 1] = BigDecimal.valueOf(entry.getKey()).multiply(SECOND_TO_MILLISECOND).intValue();
+                                        }
+
+                                        i++;
+                                    }
+
+                                    if (operation.getName().equals(AVG_HISTOGRAM)) {
+                                        AcceptableValue<BucketedValues> heatmapMetrics = service.buildMetrics(
+                                            formatMetricName(operation.getMetricName()), BucketedValues.class);
+                                        heatmapMetrics.setTimeBucket(TimeBucket.getMinuteTimeBucket(smm.getKey().getTimestamp()));
+                                        BucketedValues bv = new BucketedValues(bb, vv);
+                                        if (!group.equals(DEFAULT_GROUP)) {
+                                            bv.setGroup(group);
+                                        }
+                                        heatmapMetrics.accept(smm.getKey().getEntity(), bv);
+                                        service.doStreamingCalculation(heatmapMetrics);
+                                    } else {
+                                        AcceptableValue<AvgHistogramPercentileFunction.AvgPercentileArgument> percentileMetrics =
+                                            service.buildMetrics(formatMetricName(operation.getMetricName()), AvgHistogramPercentileFunction.AvgPercentileArgument.class);
+                                        percentileMetrics.setTimeBucket(TimeBucket.getMinuteTimeBucket(smm.getKey().getTimestamp()));
+                                        percentileMetrics.accept(smm.getKey().getEntity(),
+                                            new AvgHistogramPercentileFunction.AvgPercentileArgument(new BucketedValues(bb, vv), operation.getPercentiles().stream().mapToInt(Integer::intValue).toArray()));
+                                        service.doStreamingCalculation(percentileMetrics);
+                                    }
+
+                                    generateTraffic(smm.getKey().getEntity());
+                                });
+
                             break;
                         default:
                             throw new IllegalArgumentException(String.format("Unsupported downSampling %s", operation.getName()));
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/HeatMap.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/HeatMap.java
index d8b11c1..c10fcb0 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/HeatMap.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/HeatMap.java
@@ -132,9 +132,16 @@ public class HeatMap {
         private final boolean asc;
 
         @Override
-        public int compare(final String key1, final String key2) {
+        public int compare(final String k1, final String k2) {
             int result;
-
+            String[] kk1 = parseKey(k1);
+            String[] kk2 = parseKey(k2);
+            result = kk1[0].compareTo(kk2[0]);
+            if (result != 0) {
+                return result;
+            }
+            final String key1 = kk1[1];
+            final String key2 = kk2[1];
             if (key1.equals(key2)) {
                 result = 0;
             } else if (Bucket.INFINITE_NEGATIVE.equals(key1) || Bucket.INFINITE_POSITIVE.equals(key2)) {
@@ -147,5 +154,12 @@ public class HeatMap {
 
             return asc ? result : 0 - result;
         }
+
+        private String[] parseKey(String key) {
+            if (key.contains(":")) {
+                return key.split(":");
+            }
+            return new String[] {"default", key};
+        }
     }
 }
diff --git a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/AvgHistogramFunctionTest.java b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/AvgHistogramFunctionTest.java
new file mode 100644
index 0000000..1b02499
--- /dev/null
+++ b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/AvgHistogramFunctionTest.java
@@ -0,0 +1,263 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.skywalking.oap.server.core.analysis.meter.function;
+
+import java.util.Map;
+import java.util.stream.IntStream;
+import org.apache.skywalking.oap.server.core.analysis.meter.MeterEntity;
+import org.apache.skywalking.oap.server.core.analysis.metrics.DataTable;
+import org.apache.skywalking.oap.server.core.query.type.Bucket;
+import org.apache.skywalking.oap.server.core.query.type.HeatMap;
+import org.apache.skywalking.oap.server.core.storage.StorageBuilder;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.apache.skywalking.oap.server.core.analysis.meter.function.AvgHistogramFunction.DATASET;
+import static org.apache.skywalking.oap.server.core.analysis.meter.function.AvgLabeledFunction.COUNT;
+import static org.apache.skywalking.oap.server.core.analysis.meter.function.AvgLabeledFunction.SUMMATION;
+
+public class AvgHistogramFunctionTest {
+    private static final int[] BUCKETS = new int[] {
+        0,
+        50,
+        100,
+        250
+    };
+
+    private static final int[] BUCKETS_2ND = new int[] {
+        0,
+        51,
+        100,
+        250
+    };
+
+    private static final int[] INFINITE_BUCKETS = new int[] {
+        Integer.MIN_VALUE,
+        -5,
+        0,
+        10
+    };
+
+    @Test
+    public void testFunction() {
+        HistogramFunctionInst inst = new HistogramFunctionInst();
+        inst.accept(
+            MeterEntity.newService("service-test"),
+            new BucketedValues(
+                BUCKETS, new long[] {
+                0,
+                4,
+                10,
+                10
+            })
+        );
+
+        inst.accept(
+            MeterEntity.newService("service-test"),
+            new BucketedValues(
+                BUCKETS, new long[] {
+                1,
+                2,
+                3,
+                4
+            })
+        );
+        inst.calculate();
+
+        final int[] results = inst.getDataset().sortedValues(new HeatMap.KeyComparator(true)).stream()
+            .flatMapToInt(l -> IntStream.of(l.intValue()))
+            .toArray();
+        Assert.assertArrayEquals(new int[] {
+            1,
+            3,
+            6,
+            7
+        }, results);
+    }
+
+    @Test
+    public void testFunctionWithInfinite() {
+        HistogramFunctionInst inst = new HistogramFunctionInst();
+        inst.accept(
+            MeterEntity.newService("service-test"),
+            new BucketedValues(
+                INFINITE_BUCKETS, new long[] {
+                0,
+                4,
+                10,
+                10
+            })
+        );
+
+        inst.accept(
+            MeterEntity.newService("service-test"),
+            new BucketedValues(
+                INFINITE_BUCKETS, new long[] {
+                1,
+                2,
+                3,
+                4
+            })
+        );
+
+        inst.calculate();
+
+        Assert.assertEquals(1L, inst.getDataset().get(Bucket.INFINITE_NEGATIVE).longValue());
+    }
+
+    @Test
+    public void testSerialization() {
+        HistogramFunctionInst inst = new HistogramFunctionInst();
+        inst.accept(
+            MeterEntity.newService("service-test"),
+            new BucketedValues(
+                BUCKETS, new long[] {
+                1,
+                4,
+                10,
+                10
+            })
+        );
+        inst.calculate();
+
+        final HistogramFunctionInst inst2 = new HistogramFunctionInst();
+        inst2.deserialize(inst.serialize().build());
+
+        Assert.assertEquals(inst, inst2);
+        // HistogramFunction equal doesn't include dataset.
+        Assert.assertEquals(inst.getDataset(), inst2.getDataset());
+    }
+
+    @Test
+    public void testSerializationInInfinite() {
+        HistogramFunctionInst inst = new HistogramFunctionInst();
+        inst.accept(
+            MeterEntity.newService("service-test"),
+            new BucketedValues(
+                INFINITE_BUCKETS, new long[] {
+                1,
+                4,
+                10,
+                10
+            })
+        );
+
+        final HistogramFunctionInst inst2 = new HistogramFunctionInst();
+        inst2.deserialize(inst.serialize().build());
+
+        Assert.assertEquals(inst, inst2);
+        // HistogramFunction equal doesn't include dataset.
+        Assert.assertEquals(inst.getDataset(), inst2.getDataset());
+    }
+
+    @Test
+    public void testBuilder() throws IllegalAccessException, InstantiationException {
+        HistogramFunctionInst inst = new HistogramFunctionInst();
+        inst.accept(
+            MeterEntity.newService("service-test"),
+            new BucketedValues(
+                BUCKETS, new long[] {
+                1,
+                4,
+                10,
+                10
+            })
+        );
+        inst.calculate();
+
+        final StorageBuilder storageBuilder = inst.builder().newInstance();
+
+        // Simulate the storage layer do, convert the datatable to string.
+        Map<String, Object> map = storageBuilder.data2Map(inst);
+        map.put(SUMMATION, ((DataTable) map.get(SUMMATION)).toStorageData());
+        map.put(COUNT, ((DataTable) map.get(COUNT)).toStorageData());
+        map.put(DATASET, ((DataTable) map.get(DATASET)).toStorageData());
+
+        final AvgHistogramFunction inst2 = (AvgHistogramFunction) storageBuilder.map2Data(map);
+        Assert.assertEquals(inst, inst2);
+        // HistogramFunction equal doesn't include dataset.
+        Assert.assertEquals(inst.getDataset(), inst2.getDataset());
+    }
+
+    @Test
+    public void testGroup() {
+
+        HistogramFunctionInst inst = new HistogramFunctionInst();
+        BucketedValues bv1 = new BucketedValues(
+            BUCKETS, new long[] {
+            0,
+            4,
+            10,
+            10
+        });
+        bv1.setGroup("g1");
+        inst.accept(
+            MeterEntity.newService("service-test"),
+            bv1
+        );
+
+        BucketedValues bv2 = new BucketedValues(
+            BUCKETS, new long[] {
+            1,
+            2,
+            3,
+            4
+        });
+        bv2.setGroup("g1");
+        inst.accept(
+            MeterEntity.newService("service-test"),
+            bv2
+        );
+        BucketedValues bv3 = new BucketedValues(
+            BUCKETS, new long[] {
+            2,
+            4,
+            6,
+            8
+        });
+        bv3.setGroup("g2");
+        inst.accept(
+            MeterEntity.newService("service-test"),
+            bv3
+        );
+        inst.calculate();
+
+        int[] results = inst.getDataset().sortedValues(new HeatMap.KeyComparator(true)).stream()
+            .flatMapToInt(l -> IntStream.of(l.intValue()))
+            .toArray();
+        Assert.assertArrayEquals(new int[] {
+            1,
+            3,
+            6,
+            7,
+            2,
+            4,
+            6,
+            8
+        }, results);
+    }
+
+    private static class HistogramFunctionInst extends AvgHistogramFunction {
+
+        @Override
+        public AcceptableValue<BucketedValues> createNew() {
+            return new HistogramFunctionInst();
+        }
+    }
+}
diff --git a/oap-server/server-library/library-util/src/main/java/org/apache/skywalking/oap/server/library/util/prometheus/parser/Context.java b/oap-server/server-library/library-util/src/main/java/org/apache/skywalking/oap/server/library/util/prometheus/parser/Context.java
index d0482fe..e583809 100644
--- a/oap-server/server-library/library-util/src/main/java/org/apache/skywalking/oap/server/library/util/prometheus/parser/Context.java
+++ b/oap-server/server-library/library-util/src/main/java/org/apache/skywalking/oap/server/library/util/prometheus/parser/Context.java
@@ -113,22 +113,31 @@ public class Context {
                         .build()));
                 break;
             case HISTOGRAM:
-                Histogram.HistogramBuilder hBuilder = Histogram.builder();
-                hBuilder.name(name).timestamp(now);
-                samples.forEach(textSample -> {
-                    hBuilder.labels(textSample.getLabels());
-                    if (textSample.getName().endsWith("_count")) {
-                        hBuilder.sampleCount((long) convertStringToDouble(textSample.getValue()));
-                    } else if (textSample.getName().endsWith("_sum")) {
-                        hBuilder.sampleSum(convertStringToDouble(textSample.getValue()));
-                    } else if (textSample.getLabels().containsKey("le")) {
-                        hBuilder.bucket(
-                            convertStringToDouble(textSample.getLabels().remove("le")),
-                            (long) convertStringToDouble(textSample.getValue())
-                        );
-                    }
-                });
-                metricFamilyBuilder.addMetric(hBuilder.build());
+                samples.stream()
+                    .map(sample -> {
+                        Map<String, String> labels = Maps.newHashMap(sample.getLabels());
+                        labels.remove("le");
+                        return Pair.of(labels, sample);
+                    })
+                    .collect(groupingBy(Pair::getLeft, mapping(Pair::getRight, toList())))
+                    .forEach((labels, samples) -> {
+                        Histogram.HistogramBuilder hBuilder = Histogram.builder();
+                        hBuilder.name(name).timestamp(now);
+                        hBuilder.labels(labels);
+                        samples.forEach(textSample -> {
+                            if (textSample.getName().endsWith("_count")) {
+                                hBuilder.sampleCount((long) convertStringToDouble(textSample.getValue()));
+                            } else if (textSample.getName().endsWith("_sum")) {
+                                hBuilder.sampleSum(convertStringToDouble(textSample.getValue()));
+                            } else if (textSample.getLabels().containsKey("le")) {
+                                hBuilder.bucket(
+                                    convertStringToDouble(textSample.getLabels().remove("le")),
+                                    (long) convertStringToDouble(textSample.getValue())
+                                );
+                            }
+                        });
+                        metricFamilyBuilder.addMetric(hBuilder.build());
+                    });
                 break;
             case SUMMARY:
                 samples.stream()
diff --git a/oap-server/server-library/library-util/src/test/java/org/apache/skywalking/oap/server/library/util/prometheus/parser/TextParserTest.java b/oap-server/server-library/library-util/src/test/java/org/apache/skywalking/oap/server/library/util/prometheus/parser/TextParserTest.java
index 13c156f..c34e915 100644
--- a/oap-server/server-library/library-util/src/test/java/org/apache/skywalking/oap/server/library/util/prometheus/parser/TextParserTest.java
+++ b/oap-server/server-library/library-util/src/test/java/org/apache/skywalking/oap/server/library/util/prometheus/parser/TextParserTest.java
@@ -68,7 +68,21 @@ public class TextParserTest {
                               .setType(MetricType.HISTOGRAM)
                               .setHelp("A histogram of the request duration.")
                               .addMetric(Histogram.builder()
+                                                   .name("http_request_duration_seconds")
+                                                   .label("status", "400")
+                                                   .sampleCount(55)
+                                                   .sampleSum(12D)
+                                                   .bucket(0.05D, 20L)
+                                                   .bucket(0.1D, 20L)
+                                                   .bucket(0.2D, 20L)
+                                                   .bucket(0.5D, 25L)
+                                                   .bucket(1.0D, 30L)
+                                                   .bucket(Double.POSITIVE_INFINITY, 30L)
+                                                   .timestamp(now)
+                                                   .build())
+                              .addMetric(Histogram.builder()
                                                   .name("http_request_duration_seconds")
+                                                  .label("status", "200")
                                                   .sampleCount(144320L)
                                                   .sampleSum(53423.0D)
                                                   .bucket(0.05D, 24054L)
diff --git a/oap-server/server-library/library-util/src/test/resources/testdata/prometheus.txt b/oap-server/server-library/library-util/src/test/resources/testdata/prometheus.txt
index b6eec13..7b5c4ac 100644
--- a/oap-server/server-library/library-util/src/test/resources/testdata/prometheus.txt
+++ b/oap-server/server-library/library-util/src/test/resources/testdata/prometheus.txt
@@ -15,14 +15,22 @@ something_weird{problem="division by zero"} +Inf -3982045
 # A histogram, which has a pretty complex representation in the text format:
 # HELP http_request_duration_seconds A histogram of the request duration.
 # TYPE http_request_duration_seconds histogram
-http_request_duration_seconds_bucket{le="0.05"} 24054
-http_request_duration_seconds_bucket{le="0.1"} 33444
-http_request_duration_seconds_bucket{le="0.2"} 100392
-http_request_duration_seconds_bucket{le="0.5"} 129389
-http_request_duration_seconds_bucket{le="1"} 133988
-http_request_duration_seconds_bucket{le="+Inf"} 144320
-http_request_duration_seconds_sum 53423
-http_request_duration_seconds_count 144320
+http_request_duration_seconds_bucket{le="0.05",status="200"} 24054
+http_request_duration_seconds_bucket{le="0.1",status="200"} 33444
+http_request_duration_seconds_bucket{le="0.2",status="200"} 100392
+http_request_duration_seconds_bucket{le="0.5",status="200"} 129389
+http_request_duration_seconds_bucket{le="1",status="200"} 133988
+http_request_duration_seconds_bucket{le="+Inf",status="200"} 144320
+http_request_duration_seconds_sum{status="200"} 53423
+http_request_duration_seconds_count{status="200"} 144320
+http_request_duration_seconds_bucket{le="0.05",status="400"} 20
+http_request_duration_seconds_bucket{le="0.1",status="400"} 20
+http_request_duration_seconds_bucket{le="0.2",status="400"} 20
+http_request_duration_seconds_bucket{le="0.5",status="400"} 25
+http_request_duration_seconds_bucket{le="1",status="400"} 30
+http_request_duration_seconds_bucket{le="+Inf",status="400"} 30
+http_request_duration_seconds_sum{status="400"} 12
+http_request_duration_seconds_count{status="400"} 55
 
 # Finally a summary, which has a complex representation, too:
 # HELP rpc_duration_seconds A summary of the RPC duration in seconds.