diff --git a/synapse/federation/transaction_queue.py b/synapse/federation/transaction_queue.py
index ae62c69fc33e07afe7689044c89796fd428cc558..ca5bcf21cf3543fa2c45fcaf77d845c5ee079d21 100644
--- a/synapse/federation/transaction_queue.py
+++ b/synapse/federation/transaction_queue.py
@@ -69,11 +69,11 @@ class TransactionQueue(object):
         # HACK to get unique tx id
         self._next_txn_id = int(self._clock.time_msec())
 
-        metrics.register_callback("pending_pdus",
+        metrics.register_callback("pendingPdus",
             lambda: {(dest,): len(pdus[dest]) for dest in pdus.keys()},
             labels=["dest"],
         )
-        metrics.register_callback("pending_edus",
+        metrics.register_callback("pendingEdus",
             lambda: {(dest,): len(edus[dest]) for dest in edus.keys()},
             labels=["dest"],
         )
diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py
index 698946a48beaa75f67f766e2170b177b16bcf285..c6d6aef53bd5b6c1caa53a225c9bacacd211c53a 100644
--- a/synapse/handlers/presence.py
+++ b/synapse/handlers/presence.py
@@ -136,7 +136,7 @@ class PresenceHandler(BaseHandler):
         self._user_cachemap = {}
         self._user_cachemap_latest_serial = 0
 
-        metrics.register_callback("user_cachemap:size",
+        metrics.register_callback("userCachemap:size",
             lambda: len(self._user_cachemap)
         )
 
diff --git a/synapse/http/client.py b/synapse/http/client.py
index ad2c9c05ecaa87188593ced119f21d166ed6ec86..01737a71889834a36af9f398beeb32c89680fe88 100644
--- a/synapse/http/client.py
+++ b/synapse/http/client.py
@@ -34,10 +34,10 @@ logger = logging.getLogger(__name__)
 
 metrics = synapse.metrics.get_metrics_for(__name__)
 
-outgoing_requests_counter = metrics.register_counter("outgoing_requests",
+outgoing_requests_counter = metrics.register_counter("requests",
     labels=["method"],
 )
-incoming_responses_counter = metrics.register_counter("incoming_responses",
+incoming_responses_counter = metrics.register_counter("responses",
     labels=["method","code"],
 )
 
diff --git a/synapse/http/matrixfederationclient.py b/synapse/http/matrixfederationclient.py
index 6b6d79a04495b1e0bda12a27a8fad6bf4e6ec7e6..11883d3852227a6db4c157e459143239469b3368 100644
--- a/synapse/http/matrixfederationclient.py
+++ b/synapse/http/matrixfederationclient.py
@@ -43,10 +43,10 @@ logger = logging.getLogger(__name__)
 
 metrics = synapse.metrics.get_metrics_for(__name__)
 
-outgoing_requests_counter = metrics.register_counter("outgoing_requests",
+outgoing_requests_counter = metrics.register_counter("requests",
     labels=["method"],
 )
-incoming_responses_counter = metrics.register_counter("incoming_responses",
+incoming_responses_counter = metrics.register_counter("responses",
     labels=["method","code"],
 )
 
diff --git a/synapse/http/server.py b/synapse/http/server.py
index 35bd3a00ba0977fd90ddb166446fde4c3644a63c..23708c08c94f80a66645f95946876173eac29342 100644
--- a/synapse/http/server.py
+++ b/synapse/http/server.py
@@ -37,10 +37,10 @@ logger = logging.getLogger(__name__)
 
 metrics = synapse.metrics.get_metrics_for(__name__)
 
-incoming_requests_counter = metrics.register_counter("incoming_requests",
+incoming_requests_counter = metrics.register_counter("requests",
     labels=["method"],
 )
-outgoing_responses_counter = metrics.register_counter("outgoing_responses",
+outgoing_responses_counter = metrics.register_counter("responses",
     labels=["method","code"],
 )
 
diff --git a/synapse/metrics/__init__.py b/synapse/metrics/__init__.py
index 443d67f41c0d5312845b9300b6c27a4f85e642c2..47e475acd23c357a648b8afaa297c50eb0748a81 100644
--- a/synapse/metrics/__init__.py
+++ b/synapse/metrics/__init__.py
@@ -41,7 +41,12 @@ class Metrics(object):
         self.name_prefix = name
 
     def _register(self, metric_class, name, *args, **kwargs):
-        full_name = "%s.%s" % (self.name_prefix, name)
+        if "_" in name:
+            raise ValueError("Metric names %s is invalid as it cannot contain an underscore"
+                % (name)
+            )
+
+        full_name = "%s_%s" % (self.name_prefix, name)
 
         metric = metric_class(full_name, *args, **kwargs)
 
@@ -78,10 +83,13 @@ class Metrics(object):
         return wrapped
 
 
-def get_metrics_for(name):
+def get_metrics_for(pkg_name):
     """ Returns a Metrics instance for conveniently creating metrics
     namespaced with the given name prefix. """
-    return Metrics(name)
+
+    # Convert a "package.name" to "package_name" because Prometheus doesn't
+    # let us use . in metric names
+    return Metrics(pkg_name.replace(".", "_"))
 
 
 def render_all():
diff --git a/synapse/notifier.py b/synapse/notifier.py
index 1f7cad624ed7de78e6f78bfe4ae63cda7e206c29..75e8152d03686bce467714c7ed256081e1dc1cf5 100644
--- a/synapse/notifier.py
+++ b/synapse/notifier.py
@@ -122,7 +122,7 @@ class Notifier(object):
                 all_listeners |= x
 
             return len(all_listeners)
-        metrics.register_callback("all_listeners", count_listeners)
+        metrics.register_callback("listeners", count_listeners)
 
         metrics.register_callback("rooms",
             lambda: count(bool, self.room_to_listeners.values())
diff --git a/synapse/storage/_base.py b/synapse/storage/_base.py
index a38b603584f55aa062e3b4aa6995e283c911be0c..35d118c586db220ea529eeb611930c8eedba741b 100644
--- a/synapse/storage/_base.py
+++ b/synapse/storage/_base.py
@@ -40,7 +40,14 @@ metrics = synapse.metrics.get_metrics_for("synapse.storage")
 
 sql_query_timer = metrics.register_timer("queries", labels=["verb"])
 sql_txn_timer = metrics.register_timer("transactions", labels=["desc"])
-sql_getevents_timer = metrics.register_timer("get_events", labels=["desc"])
+sql_getevents_timer = metrics.register_timer("getEvents", labels=["desc"])
+
+caches_by_name = {}
+cache_counter = metrics.register_cache(
+    "cache",
+    lambda: {(name,): len(caches_by_name[name]) for name in caches_by_name.keys()},
+    labels=["name"],
+)
 
 
 # TODO(paul):
@@ -62,8 +69,9 @@ def cached(max_entries=1000):
     """
     def wrap(orig):
         cache = OrderedDict()
+        name = orig.__name__
 
-        counter = metrics.register_cache(orig.__name__, lambda: len(cache))
+        caches_by_name[name] = cache
 
         def prefill(key, value):
             while len(cache) > max_entries:
@@ -74,10 +82,10 @@ def cached(max_entries=1000):
         @defer.inlineCallbacks
         def wrapped(self, key):
             if key in cache:
-                counter.inc_hits()
+                cache_counter.inc_hits(name)
                 defer.returnValue(cache[key])
 
-            counter.inc_misses()
+            cache_counter.inc_misses(name)
             ret = yield orig(self, key)
             prefill(key, ret)
             defer.returnValue(ret)
@@ -195,7 +203,7 @@ class SQLBaseStore(object):
 
         self._get_event_cache = LruCache(hs.config.event_cache_size)
         self._get_event_cache_counter = metrics.register_cache(
-            "get_event_cache",
+            "getEventCache",
             size_callback=lambda: len(self._get_event_cache),
         )