diff --git a/pom.xml b/pom.xml
index 1c0b28a44a3ea61b842699e45076bd41093e128a..465a3ea0a2278d69d5406f401699c4be27e88d8f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -31,6 +31,20 @@
org.springframework.boot
spring-boot-starter-data-mongodb
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ io.prometheus
+ simpleclient
+ RELEASE
+
+
+ io.prometheus
+ simpleclient_common
+ RELEASE
+
org.springframework.data
spring-data-rest-hal-browser
diff --git a/src/main/java/works/weave/socks/cart/configuration/PrometheusEndpointContextConfiguration.java b/src/main/java/works/weave/socks/cart/configuration/PrometheusEndpointContextConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..9844b2fbc14e2e94674c67c3aec394b66ffd0e39
--- /dev/null
+++ b/src/main/java/works/weave/socks/cart/configuration/PrometheusEndpointContextConfiguration.java
@@ -0,0 +1,40 @@
+package works.weave.socks.cart.configuration;
+
+import io.prometheus.client.CollectorRegistry;
+import org.springframework.boot.actuate.autoconfigure.ExportMetricWriter;
+import org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration;
+import org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint;
+import org.springframework.boot.actuate.metrics.writer.MetricWriter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.context.annotation.Bean;
+import works.weave.socks.cart.controllers.PrometheusEndpoint;
+import works.weave.socks.cart.controllers.PrometheusMvcEndpoint;
+import works.weave.socks.cart.monitoring.PrometheusMetricWriter;
+
+@ManagementContextConfiguration
+public class PrometheusEndpointContextConfiguration {
+
+ @Bean
+ public works.weave.socks.cart.controllers.PrometheusEndpoint prometheusEndpoint(CollectorRegistry registry) {
+ return new PrometheusEndpoint(registry);
+ }
+
+ @Bean
+ @ConditionalOnBean(PrometheusEndpoint.class)
+ @ConditionalOnEnabledEndpoint("prometheus")
+ PrometheusMvcEndpoint prometheusMvcEndpoint(PrometheusEndpoint prometheusEndpoint) {
+ return new PrometheusMvcEndpoint(prometheusEndpoint);
+ }
+
+ @Bean
+ CollectorRegistry collectorRegistry() {
+ return new CollectorRegistry();
+ }
+
+ @Bean
+ @ExportMetricWriter
+ MetricWriter prometheusMetricWriter(CollectorRegistry registry) {
+ return new PrometheusMetricWriter(registry);
+ }
+
+}
diff --git a/src/main/java/works/weave/socks/cart/controllers/PrometheusEndpoint.java b/src/main/java/works/weave/socks/cart/controllers/PrometheusEndpoint.java
new file mode 100644
index 0000000000000000000000000000000000000000..9960871d2abb883ca1edbef6593924af491b61ee
--- /dev/null
+++ b/src/main/java/works/weave/socks/cart/controllers/PrometheusEndpoint.java
@@ -0,0 +1,30 @@
+package works.weave.socks.cart.controllers;
+
+import io.prometheus.client.CollectorRegistry;
+import io.prometheus.client.exporter.common.TextFormat;
+import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+
+public class PrometheusEndpoint extends AbstractEndpoint {
+
+ private CollectorRegistry registry;
+
+ public PrometheusEndpoint(CollectorRegistry registry) {
+ super("prometheus", false, true);
+ this.registry = registry;
+ }
+
+ @Override
+ public String invoke() {
+ Writer writer = new StringWriter();
+ try {
+ TextFormat.write004(writer, registry.metricFamilySamples());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return writer.toString();
+ }
+}
diff --git a/src/main/java/works/weave/socks/cart/controllers/PrometheusMvcEndpoint.java b/src/main/java/works/weave/socks/cart/controllers/PrometheusMvcEndpoint.java
new file mode 100644
index 0000000000000000000000000000000000000000..71f8b0a6c7cae2c0c8b920dd16766c19030024fd
--- /dev/null
+++ b/src/main/java/works/weave/socks/cart/controllers/PrometheusMvcEndpoint.java
@@ -0,0 +1,31 @@
+package works.weave.socks.cart.controllers;
+
+import io.prometheus.client.exporter.common.TextFormat;
+import org.springframework.boot.actuate.endpoint.mvc.AbstractEndpointMvcAdapter;
+import org.springframework.boot.actuate.endpoint.mvc.HypermediaDisabled;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.util.Collections;
+
+public class PrometheusMvcEndpoint extends AbstractEndpointMvcAdapter {
+
+ public PrometheusMvcEndpoint(PrometheusEndpoint delegate) {
+ super(delegate);
+ }
+
+ @RequestMapping(method = RequestMethod.GET, produces = TextFormat.CONTENT_TYPE_004)
+ @ResponseBody
+ @HypermediaDisabled
+ protected Object invoke() {
+ if (!getDelegate().isEnabled()) {
+ return new ResponseEntity<>(
+ Collections.singletonMap("message", "This endpoint is disabled"),
+ HttpStatus.NOT_FOUND);
+ }
+ return super.invoke();
+ }
+}
diff --git a/src/main/java/works/weave/socks/cart/monitoring/PrometheusMetricWriter.java b/src/main/java/works/weave/socks/cart/monitoring/PrometheusMetricWriter.java
new file mode 100644
index 0000000000000000000000000000000000000000..2fc17e117f5eb294fb93bf79f61343924935be14
--- /dev/null
+++ b/src/main/java/works/weave/socks/cart/monitoring/PrometheusMetricWriter.java
@@ -0,0 +1,54 @@
+package works.weave.socks.cart.monitoring;
+
+import io.prometheus.client.CollectorRegistry;
+import io.prometheus.client.Counter;
+import io.prometheus.client.Gauge;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.actuate.metrics.Metric;
+import org.springframework.boot.actuate.metrics.writer.Delta;
+import org.springframework.boot.actuate.metrics.writer.MetricWriter;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public class PrometheusMetricWriter implements MetricWriter {
+
+ private final ConcurrentMap counters = new ConcurrentHashMap<>();
+ private final ConcurrentMap gauges = new ConcurrentHashMap<>();
+ private CollectorRegistry registry;
+
+ @Autowired
+ public PrometheusMetricWriter(CollectorRegistry registry) {
+ this.registry = registry;
+ }
+
+ @Override
+ public void increment(Delta> delta) {
+ counter(delta.getName()).inc(delta.getValue().doubleValue());
+ }
+
+ @Override
+ public void reset(String metricName) {
+ counter(metricName).clear();
+ }
+
+ @Override
+ public void set(Metric> value) {
+ gauge(value.getName()).set(value.getValue().doubleValue());
+ }
+
+ private Counter counter(String name) {
+ String key = sanitizeName(name);
+ return counters.computeIfAbsent(key, k -> Counter.build().name(k).help(k).register(registry));
+ }
+
+ private Gauge gauge(String name) {
+ String key = sanitizeName(name);
+ return gauges.computeIfAbsent(key, k -> Gauge.build().name(k).help(k).register(registry));
+ }
+
+ private String sanitizeName(String name) {
+ return name.replaceAll("[^a-zA-Z0-9_]", "_");
+ }
+
+}