diff --git a/pom.xml b/pom.xml
index ea52811..8eb7a22 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/shipping/configuration/PrometheusEndpointContextConfiguration.java b/src/main/java/works/weave/socks/shipping/configuration/PrometheusEndpointContextConfiguration.java
new file mode 100644
index 0000000..109d191
--- /dev/null
+++ b/src/main/java/works/weave/socks/shipping/configuration/PrometheusEndpointContextConfiguration.java
@@ -0,0 +1,40 @@
+package works.weave.socks.shipping.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.shipping.controllers.PrometheusEndpoint;
+import works.weave.socks.shipping.controllers.PrometheusMvcEndpoint;
+import works.weave.socks.shipping.monitoring.PrometheusMetricWriter;
+
+@ManagementContextConfiguration
+public class PrometheusEndpointContextConfiguration {
+
+ @Bean
+ public 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/shipping/controllers/PrometheusEndpoint.java b/src/main/java/works/weave/socks/shipping/controllers/PrometheusEndpoint.java
new file mode 100644
index 0000000..a297934
--- /dev/null
+++ b/src/main/java/works/weave/socks/shipping/controllers/PrometheusEndpoint.java
@@ -0,0 +1,30 @@
+package works.weave.socks.shipping.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/shipping/controllers/PrometheusMvcEndpoint.java b/src/main/java/works/weave/socks/shipping/controllers/PrometheusMvcEndpoint.java
new file mode 100644
index 0000000..9d527a1
--- /dev/null
+++ b/src/main/java/works/weave/socks/shipping/controllers/PrometheusMvcEndpoint.java
@@ -0,0 +1,31 @@
+package works.weave.socks.shipping.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/shipping/monitoring/PrometheusMetricWriter.java b/src/main/java/works/weave/socks/shipping/monitoring/PrometheusMetricWriter.java
new file mode 100644
index 0000000..138472e
--- /dev/null
+++ b/src/main/java/works/weave/socks/shipping/monitoring/PrometheusMetricWriter.java
@@ -0,0 +1,54 @@
+package works.weave.socks.shipping.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_]", "_");
+ }
+
+}