diff --git a/docs/dev/index.rst b/docs/dev/index.rst
index b1c958c..b37444d 100644
--- a/docs/dev/index.rst
+++ b/docs/dev/index.rst
@@ -39,7 +39,7 @@ commands are pointing at the correct tools.
     $ python -m venv myvenv
     $ cd myvenv
     $ source bin/activate
-    $ cd ..
+    (myvenv) $ cd ..
 
 To exit the virtual environment simply type ``deactivate``.
 
diff --git a/docs/index.rst b/docs/index.rst
index 2ef50ab..919115b 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -91,9 +91,9 @@ to read on the command line.
     $ curl http://127.0.0.1:50624/metrics -H "ACCEPT: application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited"
 
 The metrics service also responds to requests sent to its ``/`` route. The
-response is simple HTML. This route can be useful as a Kubernetes ``/healthz``
-style health indicator as it does not incur any overhead within the service
-to serialize a full metrics response.
+response is simple HTML. This route can be useful as a Kubernetes health
+indicator as it does not incur any overhead within the service to
+serialize a full metrics response.
 
 .. code-block:: console
 
diff --git a/docs/user/index.rst b/docs/user/index.rst
index f33e9d5..f9030f1 100644
--- a/docs/user/index.rst
+++ b/docs/user/index.rst
@@ -83,7 +83,19 @@ A summary metrics provides:
     from aioprometheus import Summary
 
     http_access =  Summary("http_access_time", "HTTP access time")
-    http_access.add({'time': '/static'}, 3.142)
+    http_access.observe({'time': '/static'}, 3.142)
+
+The default invariants ([(0.50, 0.05), (0.90, 0.01), (0.99, 0.001)])
+can be overridden by passing `invariants` keyword argument to Summary.
+
+.. code-block:: python
+
+    from aioprometheus import Summary
+
+    http_access =  Summary(
+        "http_access_time",
+        "HTTP access time",
+        invariants=[(0.50, 0.05), (0.99, 0.001)])
 
 
 Histogram
@@ -91,16 +103,29 @@ Histogram
 
 A Histogram tracks the size and number of events in buckets.
 
-You can use Histograms for aggregatable calculation of quantiles. The set
-of buckets used can be overridden by passing `buckets` keyword argument to
-``Histogram``.
+You can use Histograms for aggregatable calculation of quantiles.
+
 
 .. code-block:: python
 
     from aioprometheus import Histogram
 
     http_access =  Histogram("http_access_time", "HTTP access time")
-    http_access.add({'time': '/static'}, 3.142)
+    http_access.observe({'time': '/static'}, 3.142)
+
+The default buckets cover the range 0.005, 0.01, 0.025, 0.05, 0.1,
+0.25, 0.5, 1.0, 2.5, 5.0, 10.0. All bucket ranges will include a
++Inf bucket. The buckets can be overridden by passing `buckets` keyword
+argument to Histogram.
+
+.. code-block:: python
+
+    from aioprometheus import Histogram
+
+    http_access =  Histogram(
+        "http_access_time",
+        "HTTP access time",
+        buckets=[0.1, 0.5, 1.0, 5.0])
 
 
 Labels
diff --git a/src/aioprometheus/__init__.py b/src/aioprometheus/__init__.py
index f851cab..2a12977 100644
--- a/src/aioprometheus/__init__.py
+++ b/src/aioprometheus/__init__.py
@@ -8,4 +8,4 @@ from .registry import Registry, CollectorRegistry
 from .service import Service
 
 
-__version__ = "18.01.03"
+__version__ = "18.01.04"
diff --git a/src/aioprometheus/collectors.py b/src/aioprometheus/collectors.py
index 4c42a08..bc04c9c 100644
--- a/src/aioprometheus/collectors.py
+++ b/src/aioprometheus/collectors.py
@@ -342,7 +342,9 @@ class Summary(Collector):
 
         :raises: KeyError if an item with matching labels is not present.
         """
-        return_data = {}  # type: Dict[Union[float, str], NumericValueType]
+        return_data = (
+            collections.OrderedDict()
+        )  # type: Dict[Union[float, str], NumericValueType]
 
         e = self.get_value(labels)
         e = cast(Any, e)  # typing check, no runtime behaviour.
@@ -353,15 +355,26 @@ class Summary(Collector):
             return_data[q] = e.query(q)  # type: ignore
 
         # Set sum and count
-        return_data[self.SUM_KEY] = e._sum  # type: ignore
         return_data[self.COUNT_KEY] = e._observations  # type: ignore
+        return_data[self.SUM_KEY] = e._sum  # type: ignore
 
         return return_data
 
 
 class Histogram(Collector):
     """
-    A Histogram metric tracks the size and number of events in buckets.
+    A histogram samples observations (usually things like request durations
+    or response sizes) and counts them in configurable buckets. It also
+    provides a sum of all observed values.
+
+    A histogram with a base metric name of <basename> exposes multiple time
+    series during a scrape:
+
+      - cumulative counters for the observation buckets, exposed as
+        <basename>_bucket{le="<upper inclusive bound>"}
+      - the total sum of all observed values, exposed as <basename>_sum
+      - the count of events that have been observed, exposed as
+        <basename>_count (identical to <basename>_bucket{le="+Inf"} above)
 
     Example use cases:
     - Response latency
@@ -426,7 +439,9 @@ class Histogram(Collector):
 
         :raises: KeyError if an item with matching labels is not present.
         """
-        return_data = {}  # type: Dict[Union[float, str], NumericValueType]
+        return_data = (
+            collections.OrderedDict()
+        )  # type: Dict[Union[float, str], NumericValueType]
 
         h = self.get_value(labels)
         h = cast(histogram.Histogram, h)  # typing check, no runtime behaviour.
@@ -435,7 +450,7 @@ class Histogram(Collector):
             return_data[upper_bound] = cumulative_count  # keys are floats
 
         # Set sum and count
-        return_data[self.SUM_KEY] = h.sum
         return_data[self.COUNT_KEY] = h.observations
+        return_data[self.SUM_KEY] = h.sum
 
         return return_data
diff --git a/src/aioprometheus/formats/text.py b/src/aioprometheus/formats/text.py
index 5f709cc..52cd93a 100644
--- a/src/aioprometheus/formats/text.py
+++ b/src/aioprometheus/formats/text.py
@@ -38,7 +38,27 @@ TEXT_CONTENT_TYPE = "text/plain; version=0.0.4; charset=utf-8"
 
 
 class TextFormatter(IFormatter):
-    """ This formatter encodes into the Protocol Buffers binary format """
+    """ This formatter encodes into the Text format.
+
+    The histogram and summary types are difficult to represent in the text
+    format. The following conventions apply:
+
+      - The sample sum for a summary or histogram named x is given as a
+        separate sample named x_sum.
+      - The sample count for a summary or histogram named x is given as a
+        separate sample named x_count.
+      - Each quantile of a summary named x is given as a separate sample line
+        with the same name x and a label {quantile="y"}.
+      - Each bucket count of a histogram named x is given as a separate sample
+        line with the name x_bucket and a label {le="y"} (where y is the upper
+        bound of the bucket).
+      - A histogram must have a bucket with {le="+Inf"}. Its value must be
+        identical to the value of x_count.
+      - The buckets of a histogram and the quantiles of a summary must appear
+        in increasing numerical order of their label values (for the le or
+        the quantile label, respectively).
+
+    """
 
     def __init__(self, timestamp: bool = False) -> None:
         """ Initialise the text formatter.
@@ -140,7 +160,10 @@ class TextFormatter(IFormatter):
     def _format_histogram(
         self, histogram: MetricTupleType, name: str, const_labels: LabelsType
     ) -> List[str]:
-        """
+        """ Format a histogram into the text format.
+
+        Buckets must be sorted and +Inf should be last.
+
         :param histogram: a 2-tuple containing labels and a dict representing
           the histogram value. The dict contains keys for each bucket as
           well as the sum and count fields.
@@ -159,6 +182,7 @@ class TextFormatter(IFormatter):
             if histogram_labels:
                 labels = histogram_labels.copy()
 
+            v = float(v)
             # Buckets need labels and not special name (like sum and count)
             if type(k) is not float:
                 name_str = "{0}_{1}".format(name, k)
@@ -214,8 +238,7 @@ class TextFormatter(IFormatter):
         """
         Marshalls a collector into a string containing one or more lines
         """
-        # need sort?
-        result = sorted(self.marshall_lines(collector))
+        result = self.marshall_lines(collector)
         return LINE_SEPARATOR_FMT.join(result)
 
     def marshall(self, registry: CollectorRegistry) -> bytes:
diff --git a/src/aioprometheus/histogram.py b/src/aioprometheus/histogram.py
index 8337734..5d9f3fa 100644
--- a/src/aioprometheus/histogram.py
+++ b/src/aioprometheus/histogram.py
@@ -75,17 +75,14 @@ class Histogram(object):
     def observe(self, value: Union[float, int]) -> None:
         """ Observe the given amount.
 
-        Observing a value into the histogram will increment the count of
-        observations, add the value to the sum of all observations and
-        increment the appropriate bucket counter.
+        Observing a value into the histogram will cumulatively increment the
+        count of observations for the buckets that the observed values falls
+        within. It also adds the value to the sum of all observations.
 
         :param value: A metric value to add to the histogram.
         """
-        # The last bucket is +Inf, so we will always increment at least
-        # one bucket
         for upper_bound in self.buckets:
             if value <= upper_bound:
                 self.buckets[upper_bound] += 1
-                break
         self.sum += value
         self.observations += 1
diff --git a/src/aioprometheus/service.py b/src/aioprometheus/service.py
index 7ce1da4..67bcf04 100644
--- a/src/aioprometheus/service.py
+++ b/src/aioprometheus/service.py
@@ -11,7 +11,7 @@ from aiohttp.hdrs import METH_GET as GET, ACCEPT
 
 from .negotiator import negotiate
 from .registry import Registry, CollectorsType
-from typing import Set
+from typing import Optional, Set
 
 # imports only used for type annotations
 from asyncio.base_events import BaseEventLoop, Server
@@ -51,9 +51,9 @@ class Service(object):
         self._site = None  # type: Server
         self._app = None  # type: aiohttp.web.Application
         self._runner = None  # type: aiohttp.web.RequestHandlerFactory
-        self._https = None  # type: bool
+        self._https = None  # type: Optional[bool]
         self._root_url = "/"
-        self._metrics_url = None  # type: str
+        self._metrics_url = None  # type: Optional[str]
 
     @property
     def base_url(self) -> str:

Histograms are not compatible with prometheus histogram_quantile() 
Prometheus histograms are meant to be cumulative. In addition the format specifies that buckets should be ordered so +Inf should be last and +Inf should match _count.

More information can be found here: https://github.com/prometheus/prometheus/issues/4264
diff --git a/tests/test_decorators.py b/tests/test_decorators.py
index 181f029..5299a02 100644
--- a/tests/test_decorators.py
+++ b/tests/test_decorators.py
@@ -12,7 +12,6 @@ from aioprometheus import (
 
 
 class TestDecorators(asynctest.TestCase):
-
     async def test_timer(self):
         m = Summary("metric_label", "metric help")
 
@@ -28,7 +27,6 @@ class TestDecorators(asynctest.TestCase):
 
         # decorator should work methods as well as functions
         class B(object):
-
             @timer(m, {"kind": "method"})
             async def b(self, arg1, arg2=None):
                 return arg1 == "b_arg", arg2 == "arg_2"
@@ -68,7 +66,6 @@ class TestDecorators(asynctest.TestCase):
 
         # decorator should work methods as well as functions
         class B(object):
-
             @inprogress(m, {"kind": "method"})
             async def b(self, arg1, arg2=None):
                 return arg1 == "b_arg", arg2 == "arg_2"
@@ -118,7 +115,6 @@ class TestDecorators(asynctest.TestCase):
 
         # decorator should work methods as well as functions
         class B(object):
-
             @count_exceptions(m, {"kind": "method"})
             async def b(self, arg1, arg2=None, raise_exc=False):
                 if raise_exc:
diff --git a/tests/test_export.py b/tests/test_export.py
index 73e7149..45e00e8 100644
--- a/tests/test_export.py
+++ b/tests/test_export.py
@@ -16,7 +16,6 @@ format_kinds = {TEXT: TEXT_CONTENT_TYPE, BINARY: BINARY_CONTENT_TYPE}
 
 
 class TestTextExporter(asynctest.TestCase):
-
     async def setUp(self):
         self.registry = Registry()
         self.server = Service(registry=self.registry)
@@ -210,11 +209,11 @@ test_gauge{data="3",test="test_gauge"} 300
 
         expected_data = """# HELP test_summary Test Summary.
 # TYPE test_summary summary
-test_summary_count{data="1",test="test_summary"} 4
-test_summary_sum{data="1",test="test_summary"} 25.2
 test_summary{data="1",quantile="0.5",test="test_summary"} 4.0
 test_summary{data="1",quantile="0.9",test="test_summary"} 5.2
 test_summary{data="1",quantile="0.99",test="test_summary"} 5.2
+test_summary_count{data="1",test="test_summary"} 4
+test_summary_sum{data="1",test="test_summary"} 25.2
 """
 
         async with aiohttp.ClientSession() as session:
@@ -263,11 +262,11 @@ test_summary{data="1",quantile="0.99",test="test_summary"} 5.2
 
         expected_data = """# HELP histogram_test Test Histogram.
 # TYPE histogram_test histogram
-histogram_test_bucket{data="1",le="+Inf",type="test_histogram"} 0
-histogram_test_bucket{data="1",le="10.0",type="test_histogram"} 1
-histogram_test_bucket{data="1",le="15.0",type="test_histogram"} 1
-histogram_test_bucket{data="1",le="5.0",type="test_histogram"} 2
-histogram_test_count{data="1",type="test_histogram"} 4
+histogram_test_bucket{data="1",le="5.0",type="test_histogram"} 2.0
+histogram_test_bucket{data="1",le="10.0",type="test_histogram"} 3.0
+histogram_test_bucket{data="1",le="15.0",type="test_histogram"} 4.0
+histogram_test_bucket{data="1",le="+Inf",type="test_histogram"} 4.0
+histogram_test_count{data="1",type="test_histogram"} 4.0
 histogram_test_sum{data="1",type="test_histogram"} 25.2
 """
 
@@ -321,7 +320,7 @@ histogram_test_sum{data="1",type="test_histogram"} 25.2
         )
 
         histogram_data = (
-            ({"h_sample": "1"}, range(1, 20, 2)),
+            ({"h_sample": "1"}, [3, 14]),
             ({"h_sample": "2"}, range(1, 20, 2)),
             ({"h_sample": "3"}, range(1, 20, 2)),
             ({"h_sample": "1", "h_subsample": "b"}, range(1, 20, 2)),
@@ -346,68 +345,68 @@ histogram_test_sum{data="1",type="test_histogram"} 25.2
         [counter.set(c[0], c[1]) for c in counter_data]
         [gauge.set(g[0], g[1]) for g in gauge_data]
         [summary.add(i[0], s) for i in summary_data for s in i[1]]
-        [histogram.add(i[0], h) for i in histogram_data for h in i[1]]
+        [histogram.observe(i[0], h) for i in histogram_data for h in i[1]]
 
         expected_data = """# HELP counter_test A counter.
 # TYPE counter_test counter
-counter_test{c_sample="1",c_subsample="b",type="counter"} 400
 counter_test{c_sample="1",type="counter"} 100
 counter_test{c_sample="2",type="counter"} 200
 counter_test{c_sample="3",type="counter"} 300
+counter_test{c_sample="1",c_subsample="b",type="counter"} 400
 # HELP gauge_test A gauge.
 # TYPE gauge_test gauge
-gauge_test{g_sample="1",g_subsample="b",type="gauge"} 800
 gauge_test{g_sample="1",type="gauge"} 500
 gauge_test{g_sample="2",type="gauge"} 600
 gauge_test{g_sample="3",type="gauge"} 700
+gauge_test{g_sample="1",g_subsample="b",type="gauge"} 800
 # HELP histogram_test A histogram.
 # TYPE histogram_test histogram
-histogram_test_bucket{h_sample="1",h_subsample="b",le="+Inf",type="histogram"} 2
-histogram_test_bucket{h_sample="1",h_subsample="b",le="10.0",type="histogram"} 2
-histogram_test_bucket{h_sample="1",h_subsample="b",le="15.0",type="histogram"} 3
-histogram_test_bucket{h_sample="1",h_subsample="b",le="5.0",type="histogram"} 3
-histogram_test_bucket{h_sample="1",le="+Inf",type="histogram"} 2
-histogram_test_bucket{h_sample="1",le="10.0",type="histogram"} 2
-histogram_test_bucket{h_sample="1",le="15.0",type="histogram"} 3
-histogram_test_bucket{h_sample="1",le="5.0",type="histogram"} 3
-histogram_test_bucket{h_sample="2",le="+Inf",type="histogram"} 2
-histogram_test_bucket{h_sample="2",le="10.0",type="histogram"} 2
-histogram_test_bucket{h_sample="2",le="15.0",type="histogram"} 3
-histogram_test_bucket{h_sample="2",le="5.0",type="histogram"} 3
-histogram_test_bucket{h_sample="3",le="+Inf",type="histogram"} 2
-histogram_test_bucket{h_sample="3",le="10.0",type="histogram"} 2
-histogram_test_bucket{h_sample="3",le="15.0",type="histogram"} 3
-histogram_test_bucket{h_sample="3",le="5.0",type="histogram"} 3
-histogram_test_count{h_sample="1",h_subsample="b",type="histogram"} 10
-histogram_test_count{h_sample="1",type="histogram"} 10
-histogram_test_count{h_sample="2",type="histogram"} 10
-histogram_test_count{h_sample="3",type="histogram"} 10
-histogram_test_sum{h_sample="1",h_subsample="b",type="histogram"} 100.0
-histogram_test_sum{h_sample="1",type="histogram"} 100.0
+histogram_test_bucket{h_sample="1",le="5.0",type="histogram"} 1.0
+histogram_test_bucket{h_sample="1",le="10.0",type="histogram"} 1.0
+histogram_test_bucket{h_sample="1",le="15.0",type="histogram"} 2.0
+histogram_test_bucket{h_sample="1",le="+Inf",type="histogram"} 2.0
+histogram_test_count{h_sample="1",type="histogram"} 2.0
+histogram_test_sum{h_sample="1",type="histogram"} 17.0
+histogram_test_bucket{h_sample="2",le="5.0",type="histogram"} 3.0
+histogram_test_bucket{h_sample="2",le="10.0",type="histogram"} 5.0
+histogram_test_bucket{h_sample="2",le="15.0",type="histogram"} 8.0
+histogram_test_bucket{h_sample="2",le="+Inf",type="histogram"} 10.0
+histogram_test_count{h_sample="2",type="histogram"} 10.0
 histogram_test_sum{h_sample="2",type="histogram"} 100.0
+histogram_test_bucket{h_sample="3",le="5.0",type="histogram"} 3.0
+histogram_test_bucket{h_sample="3",le="10.0",type="histogram"} 5.0
+histogram_test_bucket{h_sample="3",le="15.0",type="histogram"} 8.0
+histogram_test_bucket{h_sample="3",le="+Inf",type="histogram"} 10.0
+histogram_test_count{h_sample="3",type="histogram"} 10.0
 histogram_test_sum{h_sample="3",type="histogram"} 100.0
+histogram_test_bucket{h_sample="1",h_subsample="b",le="5.0",type="histogram"} 3.0
+histogram_test_bucket{h_sample="1",h_subsample="b",le="10.0",type="histogram"} 5.0
+histogram_test_bucket{h_sample="1",h_subsample="b",le="15.0",type="histogram"} 8.0
+histogram_test_bucket{h_sample="1",h_subsample="b",le="+Inf",type="histogram"} 10.0
+histogram_test_count{h_sample="1",h_subsample="b",type="histogram"} 10.0
+histogram_test_sum{h_sample="1",h_subsample="b",type="histogram"} 100.0
 # HELP summary_test A summary.
 # TYPE summary_test summary
-summary_test_count{s_sample="1",s_subsample="b",type="summary"} 22
+summary_test{quantile="0.5",s_sample="1",type="summary"} 1272.0
+summary_test{quantile="0.9",s_sample="1",type="summary"} 1452.0
+summary_test{quantile="0.99",s_sample="1",type="summary"} 1496.0
 summary_test_count{s_sample="1",type="summary"} 250
-summary_test_count{s_sample="2",type="summary"} 50
-summary_test_count{s_sample="3",type="summary"} 77
-summary_test_sum{s_sample="1",s_subsample="b",type="summary"} 98857.0
 summary_test_sum{s_sample="1",type="summary"} 374500.0
+summary_test{quantile="0.5",s_sample="2",type="summary"} 2260.0
+summary_test{quantile="0.9",s_sample="2",type="summary"} 2440.0
+summary_test{quantile="0.99",s_sample="2",type="summary"} 2500.0
+summary_test_count{s_sample="2",type="summary"} 50
 summary_test_sum{s_sample="2",type="summary"} 124500.0
+summary_test{quantile="0.5",s_sample="3",type="summary"} 3260.0
+summary_test{quantile="0.9",s_sample="3",type="summary"} 3442.0
+summary_test{quantile="0.99",s_sample="3",type="summary"} 3494.0
+summary_test_count{s_sample="3",type="summary"} 77
 summary_test_sum{s_sample="3",type="summary"} 269038.0
 summary_test{quantile="0.5",s_sample="1",s_subsample="b",type="summary"} 4235.0
-summary_test{quantile="0.5",s_sample="1",type="summary"} 1272.0
-summary_test{quantile="0.5",s_sample="2",type="summary"} 2260.0
-summary_test{quantile="0.5",s_sample="3",type="summary"} 3260.0
 summary_test{quantile="0.9",s_sample="1",s_subsample="b",type="summary"} 4470.0
-summary_test{quantile="0.9",s_sample="1",type="summary"} 1452.0
-summary_test{quantile="0.9",s_sample="2",type="summary"} 2440.0
-summary_test{quantile="0.9",s_sample="3",type="summary"} 3442.0
 summary_test{quantile="0.99",s_sample="1",s_subsample="b",type="summary"} 4517.0
-summary_test{quantile="0.99",s_sample="1",type="summary"} 1496.0
-summary_test{quantile="0.99",s_sample="2",type="summary"} 2500.0
-summary_test{quantile="0.99",s_sample="3",type="summary"} 3494.0
+summary_test_count{s_sample="1",s_subsample="b",type="summary"} 22
+summary_test_sum{s_sample="1",s_subsample="b",type="summary"} 98857.0
 """
 
         async with aiohttp.ClientSession() as session:
diff --git a/tests/test_formats_binary.py b/tests/test_formats_binary.py
index d2d0f8f..6d25eb9 100644
--- a/tests/test_formats_binary.py
+++ b/tests/test_formats_binary.py
@@ -11,7 +11,6 @@ TEST_TIMESTAMP = 1515044377268
 
 
 class TestProtobufFormat(unittest.TestCase):
-
     def setUp(self):
 
         self.const_labels = {"app": "my_app"}
@@ -56,7 +55,7 @@ class TestProtobufFormat(unittest.TestCase):
         self.histogram_metric_data = (
             (
                 {"route": "/"},
-                {5.0: 2, 10.0: 1, 15.0: 1, POS_INF: 0, "sum": 25.2, "count": 4},
+                {5.0: 2, 10.0: 3, 15.0: 4, POS_INF: 4, "sum": 25.2, "count": 4},
             ),
         )
         self.histogram_metric_data_values = (({"route": "/"}, (3, 5.2, 13, 4)),)
@@ -599,9 +598,9 @@ class TestProtobufFormat(unittest.TestCase):
             b"\x0bh_subsample\x12\x01b\n\x11\n\x04type\x12\t"
             b"histogram:?\x08\x06\x11\x00\x00\x00\x00\x00\x00G@"
             b"\x1a\x0b\x08\x03\x11\x00\x00\x00\x00\x00\x00\x14@"
-            b"\x1a\x0b\x08\x02\x11\x00\x00\x00\x00\x00\x00$@\x1a"
-            b"\x0b\x08\x01\x11\x00\x00\x00\x00\x00\x00.@\x1a\x0b"
-            b"\x08\x00\x11\x00\x00\x00\x00\x00\x00\xf0\x7f"
+            b"\x1a\x0b\x08\x05\x11\x00\x00\x00\x00\x00\x00$@\x1a"
+            b"\x0b\x08\x06\x11\x00\x00\x00\x00\x00\x00.@\x1a\x0b"
+            b"\x08\x06\x11\x00\x00\x00\x00\x00\x00\xf0\x7f"
         )
 
         f = BinaryFormatter()
diff --git a/tests/test_formats_text.py b/tests/test_formats_text.py
index 1891ba7..3eaf85c 100644
--- a/tests/test_formats_text.py
+++ b/tests/test_formats_text.py
@@ -6,7 +6,6 @@ from aioprometheus.formats import TextFormatter, TEXT_CONTENT_TYPE
 
 
 class TestTextFormat(unittest.TestCase):
-
     def test_headers(self):
         f = TextFormatter()
         expected_result = {"Content-Type": TEXT_CONTENT_TYPE}
@@ -547,11 +546,11 @@ prometheus_local_storage_indexing_queue_capacity 16384"""
 
         valid_result = """# HELP prometheus_target_interval_length_seconds Actual intervals between scrapes.
 # TYPE prometheus_target_interval_length_seconds summary
-prometheus_target_interval_length_seconds_count{interval="5s"} 4
-prometheus_target_interval_length_seconds_sum{interval="5s"} 25.2
 prometheus_target_interval_length_seconds{interval="5s",quantile="0.5"} 4.0
 prometheus_target_interval_length_seconds{interval="5s",quantile="0.9"} 5.2
-prometheus_target_interval_length_seconds{interval="5s",quantile="0.99"} 5.2"""
+prometheus_target_interval_length_seconds{interval="5s",quantile="0.99"} 5.2
+prometheus_target_interval_length_seconds_count{interval="5s"} 4
+prometheus_target_interval_length_seconds_sum{interval="5s"} 25.2"""
 
         s = Summary(**data)
 
@@ -652,11 +651,11 @@ prometheus_target_interval_length_seconds{interval="5s",quantile="0.99"} 5.2"""
 
         result_regex = r"""# HELP prometheus_target_interval_length_seconds Actual intervals between scrapes.
 # TYPE prometheus_target_interval_length_seconds summary
-prometheus_target_interval_length_seconds_count{interval="5s"} 4 \d*(?:.\d*)?
-prometheus_target_interval_length_seconds_sum{interval="5s"} 25.2 \d*(?:.\d*)?
 prometheus_target_interval_length_seconds{interval="5s",quantile="0.5"} 4.0 \d*(?:.\d*)?
 prometheus_target_interval_length_seconds{interval="5s",quantile="0.9"} 5.2 \d*(?:.\d*)?
-prometheus_target_interval_length_seconds{interval="5s",quantile="0.99"} 5.2 \d*(?:.\d*)?$"""
+prometheus_target_interval_length_seconds{interval="5s",quantile="0.99"} 5.2 \d*(?:.\d*)?
+prometheus_target_interval_length_seconds_count{interval="5s"} 4 \d*(?:.\d*)?
+prometheus_target_interval_length_seconds_sum{interval="5s"} 25.2 \d*(?:.\d*)?$"""
 
         s = Summary(**data)
 
@@ -670,7 +669,7 @@ prometheus_target_interval_length_seconds{interval="5s",quantile="0.99"} 5.2 \d*
 
     def test_registry_marshall(self):
 
-        format_times = 10
+        format_times = 3
 
         counter_data = (
             ({"c_sample": "1"}, 100),
@@ -709,41 +708,42 @@ prometheus_target_interval_length_seconds{interval="5s",quantile="0.99"} 5.2 \d*
 
         valid_regex = r"""# HELP counter_test A counter.
 # TYPE counter_test counter
-counter_test{c_sample="1",c_subsample="b",type="counter"} 400
 counter_test{c_sample="1",type="counter"} 100
 counter_test{c_sample="2",type="counter"} 200
 counter_test{c_sample="3",type="counter"} 300
+counter_test{c_sample="1",c_subsample="b",type="counter"} 400
 # HELP gauge_test A gauge.
 # TYPE gauge_test gauge
-gauge_test{g_sample="1",g_subsample="b",type="gauge"} 800
 gauge_test{g_sample="1",type="gauge"} 500
 gauge_test{g_sample="2",type="gauge"} 600
 gauge_test{g_sample="3",type="gauge"} 700
+gauge_test{g_sample="1",g_subsample="b",type="gauge"} 800
 # HELP summary_test A summary.
 # TYPE summary_test summary
-summary_test_count{s_sample="1",s_subsample="b",type="summary"} \d*(?:.\d*)?
+summary_test{quantile="0.5",s_sample="1",type="summary"} \d*(?:.\d*)?
+summary_test{quantile="0.9",s_sample="1",type="summary"} \d*(?:.\d*)?
+summary_test{quantile="0.99",s_sample="1",type="summary"} \d*(?:.\d*)?
 summary_test_count{s_sample="1",type="summary"} \d*(?:.\d*)?
-summary_test_count{s_sample="2",type="summary"} \d*(?:.\d*)?
-summary_test_count{s_sample="3",type="summary"} \d*(?:.\d*)?
-summary_test_sum{s_sample="1",s_subsample="b",type="summary"} \d*(?:.\d*)?
 summary_test_sum{s_sample="1",type="summary"} \d*(?:.\d*)?
+summary_test{quantile="0.5",s_sample="2",type="summary"} \d*(?:.\d*)?
+summary_test{quantile="0.9",s_sample="2",type="summary"} 2\d*(?:.\d*)?
+summary_test{quantile="0.99",s_sample="2",type="summary"} \d*(?:.\d*)?
+summary_test_count{s_sample="2",type="summary"} \d*(?:.\d*)?
 summary_test_sum{s_sample="2",type="summary"} \d*(?:.\d*)?
+summary_test{quantile="0.5",s_sample="3",type="summary"} \d*(?:.\d*)?
+summary_test{quantile="0.9",s_sample="3",type="summary"} \d*(?:.\d*)?
+summary_test{quantile="0.99",s_sample="3",type="summary"} \d*(?:.\d*)?
+summary_test_count{s_sample="3",type="summary"} \d*(?:.\d*)?
 summary_test_sum{s_sample="3",type="summary"} \d*(?:.\d*)?
 summary_test{quantile="0.5",s_sample="1",s_subsample="b",type="summary"} \d*(?:.\d*)?
-summary_test{quantile="0.5",s_sample="1",type="summary"} \d*(?:.\d*)?
-summary_test{quantile="0.5",s_sample="2",type="summary"} \d*(?:.\d*)?
-summary_test{quantile="0.5",s_sample="3",type="summary"} \d*(?:.\d*)?
 summary_test{quantile="0.9",s_sample="1",s_subsample="b",type="summary"} \d*(?:.\d*)?
-summary_test{quantile="0.9",s_sample="1",type="summary"} \d*(?:.\d*)?
-summary_test{quantile="0.9",s_sample="2",type="summary"} 2\d*(?:.\d*)?
-summary_test{quantile="0.9",s_sample="3",type="summary"} \d*(?:.\d*)?
 summary_test{quantile="0.99",s_sample="1",s_subsample="b",type="summary"} \d*(?:.\d*)?
-summary_test{quantile="0.99",s_sample="1",type="summary"} \d*(?:.\d*)?
-summary_test{quantile="0.99",s_sample="2",type="summary"} \d*(?:.\d*)?
-summary_test{quantile="0.99",s_sample="3",type="summary"} \d*(?:.\d*)?
+summary_test_count{s_sample="1",s_subsample="b",type="summary"} \d*(?:.\d*)?
+summary_test_sum{s_sample="1",s_subsample="b",type="summary"} \d*(?:.\d*)?
 """
         f = TextFormatter()
         self.maxDiff = None
-        # Check multiple times to ensure multiple marshalling requests
+        # Check multiple times to ensure multiple calls to marshalling
+        # produce the same results
         for i in range(format_times):
             self.assertTrue(re.match(valid_regex, f.marshall(registry).decode()))
diff --git a/tests/test_metricdict.py b/tests/test_metricdict.py
index 791f5b7..3067819 100644
--- a/tests/test_metricdict.py
+++ b/tests/test_metricdict.py
@@ -7,7 +7,6 @@ from aioprometheus.metricdict import MetricDict
 
 
 class TestMetricDict(unittest.TestCase):
-
     def setUp(self):
         pass
 
diff --git a/tests/test_metrics.py b/tests/test_metrics.py
index 274049e..389d23f 100644
--- a/tests/test_metrics.py
+++ b/tests/test_metrics.py
@@ -3,9 +3,11 @@ import unittest
 
 from aioprometheus import Collector, Counter, Gauge, Histogram, Registry, Summary
 
+POS_INF = float("inf")
+NEG_INF = float("-inf")
 
-class TestCollectorDict(unittest.TestCase):
 
+class TestCollectorDict(unittest.TestCase):
     def setUp(self):
         self.data = {
             "name": "logged_users_total",
@@ -146,7 +148,6 @@ class TestCollectorDict(unittest.TestCase):
 
 
 class TestCounter(unittest.TestCase):
-
     def setUp(self):
         self.data = {
             "name": "logged_users_total",
@@ -223,7 +224,6 @@ class TestCounter(unittest.TestCase):
 
 
 class TestGauge(unittest.TestCase):
-
     def setUp(self):
         self.data = {
             "name": "hdd_disk_used",
@@ -330,7 +330,6 @@ class TestGauge(unittest.TestCase):
 
 
 class TestSummary(unittest.TestCase):
-
     def setUp(self):
         self.data = {
             "name": "http_request_duration_microseconds",
@@ -391,19 +390,64 @@ class TestSummary(unittest.TestCase):
 
 
 class TestHistogram(unittest.TestCase):
-
     def setUp(self):
-        self.data = {
-            "name": "http_request_duration_microseconds",
-            "doc": "Request duration per application",
-            "const_labels": {"app": "my_app"},
+        self.h = Histogram(
+            "h", "doc", const_labels={"app": "my_app"}, buckets=[5.0, 10.0, 15.0]
+        )
+        self.correct_data = {
+            "sum": 25.2,
+            "count": 4,
+            5.0: 2.0,
+            10.0: 3.0,
+            15.0: 4.0,
+            POS_INF: 4.0,
         }
-
-        self.h = Histogram(**self.data)
+        self.input_values = [3, 5.2, 13, 4]
 
     def test_wrong_labels(self):
-
         with self.assertRaises(ValueError) as context:
             self.h.set_value({"le": 2}, 1)
-
         self.assertEqual("Invalid label name: le", str(context.exception))
+
+    def test_expected_values(self):
+        labels = None
+        self.h.observe(labels, 7)
+        results = self.h.get(labels)
+        self.assertEqual(0, results[5.0])
+        self.assertEqual(1, results[10.0])
+        self.assertEqual(1, results[15.0])
+        self.assertEqual(1, results[POS_INF])
+        self.assertEqual(1, results["count"])
+        self.assertEqual(7.0, results["sum"])
+
+        self.h.observe(labels, 7.5)
+        results = self.h.get(labels)
+        self.assertEqual(0, results[5.0])
+        self.assertEqual(2, results[10.0])
+        self.assertEqual(2, results[15.0])
+        self.assertEqual(2, results[POS_INF])
+        self.assertEqual(2, results["count"])
+        self.assertEqual(14.5, results["sum"])
+
+        self.h.observe(labels, POS_INF)
+        results = self.h.get(labels)
+        self.assertEqual(0, results[5.0])
+        self.assertEqual(2, results[10.0])
+        self.assertEqual(2, results[15.0])
+        self.assertEqual(3, results[POS_INF])
+        self.assertEqual(3, results["count"])
+        self.assertEqual(POS_INF, results["sum"])
+
+    def test_get(self):
+        labels = {"path": "/"}
+        for i in self.input_values:
+            self.h.observe(labels, i)
+        data = self.h.get(labels)
+        self.assertEqual(self.correct_data, data)
+
+    def test_add_get_without_labels(self):
+        labels = None
+        for i in self.input_values:
+            self.h.observe(labels, i)
+        self.assertEqual(1, len(self.h.values))
+        self.assertEqual(self.correct_data, self.h.get(labels))
diff --git a/tests/test_negotiate.py b/tests/test_negotiate.py
index ad87360..b55b413 100644
--- a/tests/test_negotiate.py
+++ b/tests/test_negotiate.py
@@ -6,7 +6,6 @@ from aioprometheus.formats import TextFormatter, BinaryFormatter
 
 
 class TestNegotiate(unittest.TestCase):
-
     def test_protobuffer(self):
         """ check that a protobuf formatter is returned """
         headers = (
diff --git a/tests/test_pusher.py b/tests/test_pusher.py
index 7771483..f630340 100644
--- a/tests/test_pusher.py
+++ b/tests/test_pusher.py
@@ -56,7 +56,6 @@ def expected_job_path(job):
 
 
 class TestPusher(asynctest.TestCase):
-
     async def setUp(self):
         self.server = TestPusherServer(loop=self.loop)
         await self.server.start()
diff --git a/tests/test_registry.py b/tests/test_registry.py
index bf32f28..e74ee08 100644
--- a/tests/test_registry.py
+++ b/tests/test_registry.py
@@ -4,7 +4,6 @@ from aioprometheus import CollectorRegistry, Collector, Counter, Gauge, Summary
 
 
 class TestRegistry(unittest.TestCase):
-
     def setUp(self):
         self.data = {
             "name": "logged_users_total",

pip install -e .
parse_log_pytest
requirements.txt
pytest
apt-get update apt-get install -y gcc
3.6
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
aiohttp==3.8.6
-e git+https://github.com/claws/aioprometheus.git@6473101aedc9ace15e781ef896a510134032467c#egg=aioprometheus
aiosignal==1.2.0
async-timeout==4.0.2
asynctest==0.13.0
attrs==22.2.0
certifi==2021.5.30
charset-normalizer==3.0.1
frozenlist==1.2.0
idna==3.10
idna-ssl==1.1.0
importlib-metadata==4.8.3
iniconfig==1.1.1
multidict==5.2.0
packaging==21.3
pluggy==1.0.0
prometheus-metrics-proto==18.1.2
protobuf==3.19.6
py==1.11.0
pyparsing==3.1.4
pytest==7.0.1
quantile-python==1.1
tomli==1.2.3
typing_extensions==4.1.1
yarl==1.7.2
zipp==3.6.0

name: aioprometheus
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - aiohttp==3.8.6
      - aiosignal==1.2.0
      - async-timeout==4.0.2
      - asynctest==0.13.0
      - attrs==22.2.0
      - charset-normalizer==3.0.1
      - frozenlist==1.2.0
      - idna==3.10
      - idna-ssl==1.1.0
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - multidict==5.2.0
      - packaging==21.3
      - pluggy==1.0.0
      - prometheus-metrics-proto==18.1.2
      - protobuf==3.19.6
      - py==1.11.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - quantile-python==1.1
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - yarl==1.7.2
      - zipp==3.6.0
prefix: /opt/conda/envs/aioprometheus

tests/test_export.py::TestTextExporter::test_all tests/test_export.py::TestTextExporter::test_histogram tests/test_export.py::TestTextExporter::test_summary tests/test_formats_binary.py::TestProtobufFormat::test_histogram_format_binary tests/test_formats_binary.py::TestProtobufFormat::test_registry_marshall_histogram tests/test_formats_text.py::TestTextFormat::test_registry_marshall tests/test_formats_text.py::TestTextFormat::test_summary_format_text tests/test_formats_text.py::TestTextFormat::test_summary_format_timestamp tests/test_metrics.py::TestHistogram::test_add_get_without_labels tests/test_metrics.py::TestHistogram::test_expected_values tests/test_metrics.py::TestHistogram::test_get
tests/test_decorators.py::TestDecorators::test_count_exceptions tests/test_decorators.py::TestDecorators::test_inprogress tests/test_decorators.py::TestDecorators::test_timer tests/test_export.py::TestTextExporter::test_counter tests/test_export.py::TestTextExporter::test_fetch_url_before_starting_server tests/test_export.py::TestTextExporter::test_gauge tests/test_export.py::TestTextExporter::test_invalid_registry tests/test_export.py::TestTextExporter::test_no_accept_header tests/test_export.py::TestTextExporter::test_register_deregister tests/test_export.py::TestTextExporter::test_robots_route tests/test_export.py::TestTextExporter::test_root_route tests/test_export.py::TestTextExporter::test_start_started_server tests/test_export.py::TestTextExporter::test_stop_stopped_server tests/test_formats_binary.py::TestProtobufFormat::test_counter_format_binary tests/test_formats_binary.py::TestProtobufFormat::test_gauge_format_binary tests/test_formats_binary.py::TestProtobufFormat::test_headers_binary tests/test_formats_binary.py::TestProtobufFormat::test_no_metric_instances_present_binary tests/test_formats_binary.py::TestProtobufFormat::test_registry_marshall_counter tests/test_formats_binary.py::TestProtobufFormat::test_registry_marshall_gauge tests/test_formats_binary.py::TestProtobufFormat::test_registry_marshall_summary tests/test_formats_binary.py::TestProtobufFormat::test_summary_format_binary tests/test_formats_text.py::TestTextFormat::test_counter_format tests/test_formats_text.py::TestTextFormat::test_counter_format_text tests/test_formats_text.py::TestTextFormat::test_counter_format_with_const_labels tests/test_formats_text.py::TestTextFormat::test_counter_format_with_timestamp tests/test_formats_text.py::TestTextFormat::test_gauge_format tests/test_formats_text.py::TestTextFormat::test_gauge_format_text tests/test_formats_text.py::TestTextFormat::test_gauge_format_with_const_labels tests/test_formats_text.py::TestTextFormat::test_gauge_format_with_timestamp tests/test_formats_text.py::TestTextFormat::test_headers tests/test_formats_text.py::TestTextFormat::test_multiple_summary_format tests/test_formats_text.py::TestTextFormat::test_one_summary_format tests/test_formats_text.py::TestTextFormat::test_single_counter_format_text tests/test_formats_text.py::TestTextFormat::test_single_gauge_format_text tests/test_formats_text.py::TestTextFormat::test_single_summary_format tests/test_formats_text.py::TestTextFormat::test_wrong_format tests/test_metricdict.py::TestMetricDict::test_access_by_str tests/test_metricdict.py::TestMetricDict::test_all tests/test_metricdict.py::TestMetricDict::test_bad_keys tests/test_metricdict.py::TestMetricDict::test_delete tests/test_metricdict.py::TestMetricDict::test_empty_key tests/test_metricdict.py::TestMetricDict::test_get tests/test_metricdict.py::TestMetricDict::test_override tests/test_metricdict.py::TestMetricDict::test_set tests/test_metricdict.py::TestMetricDict::test_similar tests/test_metrics.py::TestCollectorDict::test_get_all tests/test_metrics.py::TestCollectorDict::test_get_value tests/test_metrics.py::TestCollectorDict::test_initialization tests/test_metrics.py::TestCollectorDict::test_not_const_labels tests/test_metrics.py::TestCollectorDict::test_not_help_text tests/test_metrics.py::TestCollectorDict::test_not_name tests/test_metrics.py::TestCollectorDict::test_same_value tests/test_metrics.py::TestCollectorDict::test_set_value tests/test_metrics.py::TestCollectorDict::test_without_labels tests/test_metrics.py::TestCollectorDict::test_wrong_labels tests/test_metrics.py::TestCounter::test_add tests/test_metrics.py::TestCounter::test_get tests/test_metrics.py::TestCounter::test_inc tests/test_metrics.py::TestCounter::test_negative_add tests/test_metrics.py::TestCounter::test_set tests/test_metrics.py::TestCounter::test_set_get_without_labels tests/test_metrics.py::TestGauge::test_add tests/test_metrics.py::TestGauge::test_add_negative tests/test_metrics.py::TestGauge::test_dec tests/test_metrics.py::TestGauge::test_get tests/test_metrics.py::TestGauge::test_inc tests/test_metrics.py::TestGauge::test_set tests/test_metrics.py::TestGauge::test_set_get_without_labels tests/test_metrics.py::TestGauge::test_sub tests/test_metrics.py::TestGauge::test_sub_positive tests/test_metrics.py::TestSummary::test_add tests/test_metrics.py::TestSummary::test_add_get_without_labels tests/test_metrics.py::TestSummary::test_add_wrong_types tests/test_metrics.py::TestSummary::test_get tests/test_metrics.py::TestHistogram::test_wrong_labels tests/test_negotiate.py::TestNegotiate::test_default tests/test_negotiate.py::TestNegotiate::test_no_accept_header tests/test_negotiate.py::TestNegotiate::test_protobuffer tests/test_negotiate.py::TestNegotiate::test_text_004 tests/test_negotiate.py::TestNegotiate::test_text_default tests/test_pusher.py::TestPusher::test_push_add tests/test_pusher.py::TestPusher::test_push_delete tests/test_pusher.py::TestPusher::test_push_job_ping tests/test_pusher.py::TestPusher::test_push_replace tests/test_registry.py::TestRegistry::test_deregister tests/test_registry.py::TestRegistry::test_get tests/test_registry.py::TestRegistry::test_get_all tests/test_registry.py::TestRegistry::test_register tests/test_registry.py::TestRegistry::test_register_counter tests/test_registry.py::TestRegistry::test_register_gauge tests/test_registry.py::TestRegistry::test_register_sames tests/test_registry.py::TestRegistry::test_register_summary tests/test_registry.py::TestRegistry::test_register_wrong_type
src/aioprometheus/__init__.py docs/user/index.rst src/aioprometheus/service.py docs/dev/index.rst src/aioprometheus/formats/text.py src/aioprometheus/collectors.py src/aioprometheus/histogram.py docs/index.rst
src/aioprometheus/__init__.py docs/user/index.rst src/aioprometheus/service.py docs/dev/index.rst src/aioprometheus/formats/text.py src/aioprometheus/collectors.py src/aioprometheus/histogram.py docs/index.rst