diff --git a/report_coverage_context.sh b/report_coverage_context.sh
index 3a404a62698cdd95f94c9ed7d4c8b4b073778d08..95966ead0bdf84b39be3e3f3063e1b93dfad32f1 100755
--- a/report_coverage_context.sh
+++ b/report_coverage_context.sh
@@ -1,3 +1,3 @@
 #!/bin/bash
 
-./report_coverage_all.sh | grep --color -E -i "^context/.*$|$"
+./report_coverage_all.sh | grep -v -E "^(cent|com|devi|moni|serv|test)" | grep --color -E -i "^context/.*$|$"
diff --git a/run_local_tests.sh b/run_local_tests.sh
index c1fb23861b43a29a82a51e30f9476076552fe631..2b2eb9e39d6a8ed8fbbc0a575eb995e8b43ac078 100755
--- a/run_local_tests.sh
+++ b/run_local_tests.sh
@@ -15,30 +15,32 @@ cat $PROJECTDIR/coverage/.coveragerc.template | sed s+~/teraflow/controller+$PRO
 rm -f $COVERAGEFILE
 
 coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
-    common/database/tests/test_unitary.py \
-    common/database/tests/test_engine_inmemory.py
+    common/metrics/tests/test_unitary.py \
+    common/orm/tests/test_unitary.py
 
-coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
-    centralizedcybersecurity/tests/test_unitary.py
-
-coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
-    context/tests/test_unitary.py
-
-coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
-    device/tests/test_unitary_driverapi.py \
-    device/tests/test_unitary_service.py
-
-coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
-    service/tests/test_unitary.py
-
-coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
-    compute/tests/test_unitary.py
+#coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+#    centralizedcybersecurity/tests/test_unitary.py
 
-# Run integration tests and analyze coverage of code at same time
-export DB_ENGINE='redis'
-export REDIS_SERVICE_HOST='10.1.7.194'
-export REDIS_SERVICE_PORT='31789'
-export REDIS_DATABASE_ID='0'
 coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
-    common/database/tests/test_engine_redis.py \
-    tester_integration/test_context_device_service.py
+    context/tests/test_unitary_orm_context_inmemory.py \
+#    context/tests/test_unitary.py
+
+#coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+#    device/tests/test_unitary_driverapi.py \
+#    device/tests/test_unitary_service.py
+
+#coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+#    service/tests/test_unitary.py
+
+#coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+#    compute/tests/test_unitary.py
+
+## Run integration tests and analyze coverage of code at same time
+#export DB_ENGINE='redis'
+#export REDIS_SERVICE_HOST='10.1.7.194'
+## Find exposed port for Redis service port 6379
+#export REDIS_SERVICE_PORT=$(kubectl --namespace gitlab-ci get service redis-public -o 'jsonpath={.spec.ports[?(@.port==6379)].nodePort}')
+#export REDIS_DATABASE_ID='0'
+#coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
+#    context/tests/test_database_engine_redis.py \
+#    tester_integration/test_context_device_service.py
diff --git a/src/common/metrics/Metrics.py b/src/common/metrics/Metrics.py
new file mode 100644
index 0000000000000000000000000000000000000000..33b52f73b62f240167a9724c102785ebad3332bd
--- /dev/null
+++ b/src/common/metrics/Metrics.py
@@ -0,0 +1,66 @@
+import grpc, logging
+from enum import Enum
+from typing import Dict, List
+from prometheus_client import Counter, Histogram
+from prometheus_client.metrics import MetricWrapperBase
+from common.exceptions.ServiceException import ServiceException
+
+class RequestConditionEnum(Enum):
+    STARTED   = 'started'
+    COMPLETED = 'completed'
+    FAILED    = 'failed'
+
+def get_counter_requests(method_name : str, request_condition : RequestConditionEnum) -> Counter:
+    str_request_condition = request_condition.value
+    name = '{:s}_counter_requests_{:s}'.format(method_name.replace(':', '_'), str_request_condition)
+    description = '{:s} counter of requests {:s}'.format(method_name, str_request_condition)
+    return Counter(name, description)
+
+def get_histogram_duration(method_name : str) -> Histogram:
+    name = '{:s}_histogram_duration'.format(method_name.replace(':', '_'))
+    description = '{:s} histogram of request duration'.format(method_name)
+    return Histogram(name, description)
+
+METRIC_TEMPLATES = {
+    '{:s}_COUNTER_STARTED'   : lambda method_name: get_counter_requests  (method_name, RequestConditionEnum.STARTED),
+    '{:s}_COUNTER_COMPLETED' : lambda method_name: get_counter_requests  (method_name, RequestConditionEnum.COMPLETED),
+    '{:s}_COUNTER_FAILED'    : lambda method_name: get_counter_requests  (method_name, RequestConditionEnum.FAILED),
+    '{:s}_HISTOGRAM_DURATION': lambda method_name: get_histogram_duration(method_name),
+}
+
+def create_metrics(service_name : str, method_names : List[str]) -> Dict[str, MetricWrapperBase]:
+    metrics = {}
+    for method_name in method_names:
+        for template_name, template_generator_function in METRIC_TEMPLATES.items():
+            metric_name = template_name.format(method_name).upper()
+            metrics[metric_name] = template_generator_function('{:s}:{:s}'.format(service_name, method_name))
+    return metrics
+
+def safe_and_metered_rpc_method(metrics : Dict[str, MetricWrapperBase], logger : logging.Logger):
+    def outer_wrapper(func):
+        function_name = func.__name__
+        print('function_name', function_name)
+        HISTOGRAM_DURATION : Histogram = metrics.get('{:s}_HISTOGRAM_DURATION'.format(function_name).upper())
+        COUNTER_STARTED    : Counter   = metrics.get('{:s}_COUNTER_STARTED'   .format(function_name).upper())
+        COUNTER_COMPLETED  : Counter   = metrics.get('{:s}_COUNTER_COMPLETED' .format(function_name).upper())
+        COUNTER_FAILED     : Counter   = metrics.get('{:s}_COUNTER_FAILED'    .format(function_name).upper())
+
+        @HISTOGRAM_DURATION.time()
+        def inner_wrapper(self, request, grpc_context : grpc.ServicerContext):
+            COUNTER_STARTED.inc()
+            try:
+                logger.debug('{:s} request: {:s}'.format(function_name, str(request)))
+                reply = func(self, request, grpc_context)
+                logger.debug('{:s} reply: {:s}'.format(function_name, str(reply)))
+                COUNTER_COMPLETED.inc()
+                return reply
+            except ServiceException as e:                               # pragma: no cover (ServiceException not thrown)
+                logger.exception('{:s} exception'.format(function_name))
+                COUNTER_FAILED.inc()
+                grpc_context.abort(e.code, e.details)
+            except Exception as e:                                      # pragma: no cover
+                logger.exception('{:s} exception'.format(function_name))
+                COUNTER_FAILED.inc()
+                grpc_context.abort(grpc.StatusCode.INTERNAL, str(e))
+        return inner_wrapper
+    return outer_wrapper
diff --git a/src/common/metrics/__init__.py b/src/common/metrics/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/common/metrics/tests/__init__.py b/src/common/metrics/tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/common/metrics/tests/test_unitary.py b/src/common/metrics/tests/test_unitary.py
new file mode 100644
index 0000000000000000000000000000000000000000..cfc755aa995834546bf11ab6b2a59f989de0d1c5
--- /dev/null
+++ b/src/common/metrics/tests/test_unitary.py
@@ -0,0 +1,30 @@
+import grpc, logging, pytest, time
+from common.metrics.Metrics import create_metrics, safe_and_metered_rpc_method
+
+logging.basicConfig(level=logging.DEBUG)
+LOGGER = logging.getLogger(__name__)
+
+def test_database_instantiation():
+    SERVICE_NAME = 'Context'
+    METHOD_NAMES = [
+        'ListContextIds',  'ListContexts',   'GetContext',  'SetContext',  'RemoveContext',  'GetContextEvents',
+        'ListTopologyIds', 'ListTopologies', 'GetTopology', 'SetTopology', 'RemoveTopology', 'GetTopologyEvents',
+        'ListDeviceIds',   'ListDevices',    'GetDevice',   'SetDevice',   'RemoveDevice',   'GetDeviceEvents',
+        'ListLinkIds',     'ListLinks',      'GetLink',     'SetLink',     'RemoveLink',     'GetLinkEvents',
+        'ListServiceIds',  'ListServices',   'GetService',  'SetService',  'RemoveService',  'GetServiceEvents',
+    ]
+    METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES)
+
+    class TestServiceServicerImpl:
+        @safe_and_metered_rpc_method(METRICS, LOGGER)
+        def GetTopology(self, request, grpc_context : grpc.ServicerContext):
+            print('doing funny things')
+            time.sleep(0.1)
+            return 'done'
+
+    tssi = TestServiceServicerImpl()
+    tssi.GetTopology(1, 2)
+
+    for metric_name,metric in METRICS.items():
+        if 'GETTOPOLOGY_' not in metric_name: continue
+        print(metric_name, metric._child_samples())
diff --git a/src/common/orm/tests/test_unitary_orm.py b/src/common/orm/tests/test_unitary.py
similarity index 100%
rename from src/common/orm/tests/test_unitary_orm.py
rename to src/common/orm/tests/test_unitary.py