├── .editorconfig ├── .gitignore ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── example │ │ ├── DemoApplication.java │ │ ├── PrometheusAutoConfiguration.java │ │ ├── PrometheusExporter.java │ │ ├── PrometheusMetricServices.java │ │ └── PrometheusRegistryMetricReader.java └── resources │ └── application.properties └── test └── java └── com └── example └── DemoApplicationTests.java /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | indent_style = space 7 | indent_size = 2 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Maven template 3 | target/ 4 | pom.xml.tag 5 | pom.xml.releaseBackup 6 | pom.xml.versionsBackup 7 | pom.xml.next 8 | release.properties 9 | dependency-reduced-pom.xml 10 | buildNumber.properties 11 | .mvn/timing.properties 12 | ### JetBrains template 13 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 14 | 15 | *.iml 16 | 17 | ## Directory-based project format: 18 | .idea/ 19 | # if you remove the above rule, at least ignore the following: 20 | 21 | # User-specific stuff: 22 | # .idea/workspace.xml 23 | # .idea/tasks.xml 24 | # .idea/dictionaries 25 | 26 | # Sensitive or high-churn files: 27 | # .idea/dataSources.ids 28 | # .idea/dataSources.xml 29 | # .idea/sqlDataSources.xml 30 | # .idea/dynamic.xml 31 | # .idea/uiDesigner.xml 32 | 33 | # Gradle: 34 | # .idea/gradle.xml 35 | # .idea/libraries 36 | 37 | # Mongo Explorer plugin: 38 | # .idea/mongoSettings.xml 39 | 40 | ## File-based project format: 41 | *.ipr 42 | *.iws 43 | 44 | ## Plugin-specific files: 45 | 46 | # IntelliJ 47 | /out/ 48 | 49 | # mpeltonen/sbt-idea plugin 50 | .idea_modules/ 51 | 52 | # JIRA plugin 53 | atlassian-ide-plugin.xml 54 | 55 | # Crashlytics plugin (for Android Studio and IntelliJ) 56 | com_crashlytics_export_strings.xml 57 | crashlytics.properties 58 | crashlytics-build.properties 59 | 60 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | io.fabric8.springboot 7 | prometheus 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | Spring Boot Prometheus Exporter 12 | Spring Boot Prometheus Exporter 13 | 14 | 15 | UTF-8 16 | 1.8 17 | UTF-8 18 | UTF-8 19 | ${java.version} 20 | ${java.version} 21 | 22 | 23 | 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-parent 28 | 1.3.0.RC1 29 | pom 30 | import 31 | 32 | 33 | 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-actuator 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-web 43 | 44 | 45 | 46 | io.prometheus 47 | simpleclient_servlet 48 | 0.0.11 49 | 50 | 51 | io.prometheus 52 | simpleclient_hotspot 53 | 0.0.11 54 | 55 | 56 | 57 | org.springframework.boot 58 | spring-boot-starter-test 59 | test 60 | 61 | 62 | 63 | 64 | 65 | 66 | org.springframework.boot 67 | spring-boot-maven-plugin 68 | 69 | 70 | 71 | 72 | 73 | 74 | spring-snapshots 75 | Spring Snapshots 76 | https://repo.spring.io/snapshot 77 | 78 | true 79 | 80 | 81 | 82 | spring-milestones 83 | Spring Milestones 84 | https://repo.spring.io/milestone 85 | 86 | false 87 | 88 | 89 | 90 | 91 | 92 | spring-snapshots 93 | Spring Snapshots 94 | https://repo.spring.io/snapshot 95 | 96 | true 97 | 98 | 99 | 100 | spring-milestones 101 | Spring Milestones 102 | https://repo.spring.io/milestone 103 | 104 | false 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /src/main/java/com/example/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class DemoApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(DemoApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/example/PrometheusAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import io.prometheus.client.CollectorRegistry; 4 | import io.prometheus.client.exporter.MetricsServlet; 5 | import org.springframework.boot.actuate.autoconfigure.MetricRepositoryAutoConfiguration; 6 | import org.springframework.boot.actuate.endpoint.MetricReaderPublicMetrics; 7 | import org.springframework.boot.actuate.metrics.CounterService; 8 | import org.springframework.boot.actuate.metrics.GaugeService; 9 | import org.springframework.boot.autoconfigure.AutoConfigureBefore; 10 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 11 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 12 | import org.springframework.boot.context.embedded.ServletRegistrationBean; 13 | import org.springframework.context.annotation.Bean; 14 | import org.springframework.context.annotation.Configuration; 15 | 16 | @Configuration 17 | @ConditionalOnClass(CollectorRegistry.class) 18 | @AutoConfigureBefore(MetricRepositoryAutoConfiguration.class) 19 | public class PrometheusAutoConfiguration { 20 | 21 | @Bean 22 | @ConditionalOnMissingBean 23 | public CollectorRegistry metricRegistry() { 24 | return new CollectorRegistry(); 25 | } 26 | 27 | @Bean 28 | @ConditionalOnMissingBean({PrometheusMetricServices.class, CounterService.class, GaugeService.class}) 29 | public PrometheusMetricServices prometheusMetricServices(CollectorRegistry metricRegistry) { 30 | return new PrometheusMetricServices(metricRegistry); 31 | } 32 | 33 | @Bean 34 | public MetricReaderPublicMetrics prometheusPublicMetrics(CollectorRegistry metricRegistry) { 35 | PrometheusRegistryMetricReader reader = new PrometheusRegistryMetricReader(metricRegistry); 36 | return new MetricReaderPublicMetrics(reader); 37 | } 38 | 39 | @Bean 40 | public ServletRegistrationBean registerPrometheusExporterServlet(CollectorRegistry metricRegistry) { 41 | return new ServletRegistrationBean(new MetricsServlet(metricRegistry), "/prometheus"); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/example/PrometheusExporter.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | public class PrometheusExporter { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/com/example/PrometheusMetricServices.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import io.prometheus.client.CollectorRegistry; 4 | import io.prometheus.client.Gauge; 5 | import io.prometheus.client.hotspot.GarbageCollectorExports; 6 | import io.prometheus.client.hotspot.MemoryPoolsExports; 7 | import io.prometheus.client.hotspot.StandardExports; 8 | import org.springframework.boot.actuate.metrics.CounterService; 9 | import org.springframework.boot.actuate.metrics.GaugeService; 10 | 11 | import java.util.concurrent.ConcurrentHashMap; 12 | import java.util.concurrent.ConcurrentMap; 13 | 14 | public class PrometheusMetricServices implements CounterService, GaugeService { 15 | 16 | private final CollectorRegistry registry; 17 | 18 | private final ConcurrentMap gauges = new ConcurrentHashMap<>(); 19 | 20 | public PrometheusMetricServices(CollectorRegistry registry) { 21 | this.registry = registry; 22 | new StandardExports().register(registry); 23 | new MemoryPoolsExports().register(registry); 24 | new GarbageCollectorExports().register(registry); 25 | } 26 | 27 | @Override 28 | public void increment(String name) { 29 | getOrRegisterGauge(name).inc(); 30 | } 31 | 32 | @Override 33 | public void decrement(String name) { 34 | getOrRegisterGauge(name).dec(); 35 | } 36 | 37 | private Gauge getOrRegisterGauge(String name) { 38 | return gauges.computeIfAbsent(name, k -> { 39 | Gauge gauge = Gauge.build().name(sanitizeName(k)).help(k).create(); 40 | registry.register(gauge); 41 | return gauge; 42 | }); 43 | } 44 | 45 | @Override 46 | public void submit(String name, double value) { 47 | getOrRegisterGauge(name).set(value); 48 | } 49 | 50 | @Override 51 | public void reset(String name) { 52 | gauges.computeIfPresent(name, (k, v) -> { 53 | v.set(0); 54 | return v; 55 | }); 56 | } 57 | 58 | private String sanitizeName(String name) { 59 | return name.replaceAll("[^a-zA-Z0-9_]", "_"); 60 | } 61 | 62 | } 63 | 64 | -------------------------------------------------------------------------------- /src/main/java/com/example/PrometheusRegistryMetricReader.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import io.prometheus.client.Collector; 4 | import io.prometheus.client.CollectorRegistry; 5 | import org.springframework.boot.actuate.metrics.Metric; 6 | import org.springframework.boot.actuate.metrics.reader.MetricReader; 7 | 8 | import java.util.Collections; 9 | import java.util.HashSet; 10 | import java.util.Iterator; 11 | import java.util.Set; 12 | 13 | public class PrometheusRegistryMetricReader implements MetricReader { 14 | 15 | private final CollectorRegistry registry; 16 | 17 | public PrometheusRegistryMetricReader(CollectorRegistry registry) { 18 | this.registry = registry; 19 | } 20 | 21 | 22 | @Override 23 | public Metric findOne(String metricName) { 24 | for (Collector.MetricFamilySamples metricFamilySamples : Collections.list(registry.metricFamilySamples())) { 25 | for (Collector.MetricFamilySamples.Sample sample : metricFamilySamples.samples) { 26 | if (sample.name.equals(metricName)) { 27 | return new Metric(metricName, sample.value); 28 | } 29 | } 30 | } 31 | return null; 32 | } 33 | 34 | @Override 35 | public Iterable> findAll() { 36 | return new Iterable>() { 37 | @Override 38 | public Iterator> iterator() { 39 | Set> metrics = new HashSet>(); 40 | for (Collector.MetricFamilySamples metricFamilySamples : Collections.list(registry.metricFamilySamples())) { 41 | for (Collector.MetricFamilySamples.Sample sample : metricFamilySamples.samples) { 42 | metrics.add(new Metric(sample.name, sample.value)); 43 | } 44 | } 45 | return metrics.iterator(); 46 | } 47 | }; 48 | } 49 | 50 | @Override 51 | public long count() { 52 | return Collections.list(registry.metricFamilySamples()).size(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmidyson/spring-boot-prometheus/b12edf22e73abe720434953890cd696c3b67049b/src/main/resources/application.properties -------------------------------------------------------------------------------- /src/test/java/com/example/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.SpringApplicationConfiguration; 6 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 7 | import org.springframework.test.context.web.WebAppConfiguration; 8 | 9 | @RunWith(SpringJUnit4ClassRunner.class) 10 | @SpringApplicationConfiguration(classes = DemoApplication.class) 11 | @WebAppConfiguration 12 | public class DemoApplicationTests { 13 | 14 | @Test 15 | public void contextLoads() { 16 | } 17 | 18 | } 19 | --------------------------------------------------------------------------------