springboot的metrics

codecraft

类结构

clipboard.png

PublicMetricsAutoConfiguration

@Configuration
@AutoConfigureBefore(EndpointAutoConfiguration.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, CacheAutoConfiguration.class,
        MetricRepositoryAutoConfiguration.class, CacheStatisticsAutoConfiguration.class,
        IntegrationAutoConfiguration.class })
public class PublicMetricsAutoConfiguration {

    @Autowired(required = false)
    @ExportMetricReader
    private List<MetricReader> metricReaders = Collections.emptyList();

    @Bean
    public SystemPublicMetrics systemPublicMetrics() {
        return new SystemPublicMetrics();
    }

    @Bean
    public MetricReaderPublicMetrics metricReaderPublicMetrics() {
        return new MetricReaderPublicMetrics(new CompositeMetricReader(
                this.metricReaders.toArray(new MetricReader[0])));
    }

    @Bean
    @ConditionalOnBean(RichGaugeReader.class)
    public RichGaugeReaderPublicMetrics richGaugePublicMetrics(
            RichGaugeReader richGaugeReader) {
        return new RichGaugeReaderPublicMetrics(richGaugeReader);
    }

    @Configuration
    @ConditionalOnClass(DataSource.class)
    @ConditionalOnBean(DataSource.class)
    static class DataSourceMetricsConfiguration {

        @Bean
        @ConditionalOnMissingBean
        @ConditionalOnBean(DataSourcePoolMetadataProvider.class)
        public DataSourcePublicMetrics dataSourcePublicMetrics() {
            return new DataSourcePublicMetrics();
        }

    }

    @Configuration
    @ConditionalOnClass({ Servlet.class, Tomcat.class })
    @ConditionalOnWebApplication
    static class TomcatMetricsConfiguration {

        @Bean
        @ConditionalOnMissingBean
        public TomcatPublicMetrics tomcatPublicMetrics() {
            return new TomcatPublicMetrics();
        }

    }

//......

}

InMemoryMetricRepository

默认如果用户没有给出任何自定义的MetricRepository,spring-boot-starter-actuator会提供一个InMemoryMetricRepository实现。如果我们将Dropwizard的Metrics类库作为依赖加入classpath,那么,Dropwizard Metrics的Metri-cRegistry中所有的度量指标项也会通过Public-Metrics的形式开发暴露出来。

/metrics方法

@ConfigurationProperties(prefix = "endpoints.metrics")
public class MetricsEndpoint extends AbstractEndpoint<Map<String, Object>> {

    private final List<PublicMetrics> publicMetrics;

    /**
     * Create a new {@link MetricsEndpoint} instance.
     * @param publicMetrics the metrics to expose
     */
    public MetricsEndpoint(PublicMetrics publicMetrics) {
        this(Collections.singleton(publicMetrics));
    }

    /**
     * Create a new {@link MetricsEndpoint} instance.
     * @param publicMetrics the metrics to expose. The collection will be sorted using the
     * {@link AnnotationAwareOrderComparator}.
     */
    public MetricsEndpoint(Collection<PublicMetrics> publicMetrics) {
        super("metrics");
        Assert.notNull(publicMetrics, "PublicMetrics must not be null");
        this.publicMetrics = new ArrayList<PublicMetrics>(publicMetrics);
        AnnotationAwareOrderComparator.sort(this.publicMetrics);
    }

    public void registerPublicMetrics(PublicMetrics metrics) {
        this.publicMetrics.add(metrics);
        AnnotationAwareOrderComparator.sort(this.publicMetrics);
    }

    public void unregisterPublicMetrics(PublicMetrics metrics) {
        this.publicMetrics.remove(metrics);
    }

    @Override
    public Map<String, Object> invoke() {
        Map<String, Object> result = new LinkedHashMap<String, Object>();
        List<PublicMetrics> metrics = new ArrayList<PublicMetrics>(this.publicMetrics);
        for (PublicMetrics publicMetric : metrics) {
            try {
                for (Metric<?> metric : publicMetric.metrics()) {
                    result.put(metric.getName(), metric.getValue());
                }
            }
            catch (Exception ex) {
                // Could not evaluate metrics
            }
        }
        return result;
    }

}

相关配置

{
      "name": "spring.metrics.export.aggregate.key-pattern",
      "type": "java.lang.String",
      "description": "Pattern that tells the aggregator what to do with the keys from the source\n repository. The keys in the source repository are assumed to be period\n separated, and the pattern is in the same format, e.g. \"d.d.k.d\". Here \"d\"\n means \"discard\" and \"k\" means \"keep\" the key segment in the corresponding\n position in the source.",
      "sourceType": "org.springframework.boot.actuate.metrics.export.MetricExportProperties$Aggregate",
      "defaultValue": ""
    },
    {
      "name": "spring.metrics.export.aggregate.prefix",
      "type": "java.lang.String",
      "description": "Prefix for global repository if active. Should be unique for this JVM, but most\n useful if it also has the form \"a.b\" where \"a\" is unique to this logical\n process (this application) and \"b\" is unique to this physical process. If you\n set spring.application.name elsewhere, then the default will be in the right\n form.",
      "sourceType": "org.springframework.boot.actuate.metrics.export.MetricExportProperties$Aggregate",
      "defaultValue": ""
    },
    {
      "name": "spring.metrics.export.delay-millis",
      "type": "java.lang.Long",
      "description": "Delay in milliseconds between export ticks. Metrics are exported to external\n sources on a schedule with this delay.",
      "sourceType": "org.springframework.boot.actuate.metrics.export.MetricExportProperties"
    },
    {
      "name": "spring.metrics.export.enabled",
      "type": "java.lang.Boolean",
      "description": "Flag to enable metric export (assuming a MetricWriter is available).",
      "sourceType": "org.springframework.boot.actuate.metrics.export.MetricExportProperties"
    },
    {
      "name": "spring.metrics.export.excludes",
      "type": "java.lang.String[]",
      "description": "List of patterns for metric names to exclude. Applied after the includes.",
      "sourceType": "org.springframework.boot.actuate.metrics.export.MetricExportProperties"
    },
    {
      "name": "spring.metrics.export.includes",
      "type": "java.lang.String[]",
      "description": "List of patterns for metric names to include.",
      "sourceType": "org.springframework.boot.actuate.metrics.export.MetricExportProperties"
    },
    {
      "name": "spring.metrics.export.redis.key",
      "type": "java.lang.String",
      "description": "Key for redis repository export (if active). Should be globally unique for a\n system sharing a redis repository across multiple processes.",
      "sourceType": "org.springframework.boot.actuate.metrics.export.MetricExportProperties$Redis",
      "defaultValue": "keys.spring.metrics"
    },
    {
      "name": "spring.metrics.export.redis.prefix",
      "type": "java.lang.String",
      "description": "Prefix for redis repository if active. Should be globally unique across all\n processes sharing the same repository.",
      "sourceType": "org.springframework.boot.actuate.metrics.export.MetricExportProperties$Redis",
      "defaultValue": "spring.metrics"
    },
    {
      "name": "spring.metrics.export.send-latest",
      "type": "java.lang.Boolean",
      "description": "Flag to switch off any available optimizations based on not exporting unchanged\n metric values.",
      "sourceType": "org.springframework.boot.actuate.metrics.export.MetricExportProperties"
    },
    {
      "name": "spring.metrics.export.statsd.host",
      "type": "java.lang.String",
      "description": "Host of a statsd server to receive exported metrics.",
      "sourceType": "org.springframework.boot.actuate.metrics.export.MetricExportProperties$Statsd"
    },
    {
      "name": "spring.metrics.export.statsd.port",
      "type": "java.lang.Integer",
      "description": "Port of a statsd server to receive exported metrics.",
      "sourceType": "org.springframework.boot.actuate.metrics.export.MetricExportProperties$Statsd",
      "defaultValue": 8125
    },
    {
      "name": "spring.metrics.export.statsd.prefix",
      "type": "java.lang.String",
      "description": "Prefix for statsd exported metrics.",
      "sourceType": "org.springframework.boot.actuate.metrics.export.MetricExportProperties$Statsd"
    },
    {
      "name": "spring.metrics.export.triggers",
      "type": "java.util.Map<java.lang.String,org.springframework.boot.actuate.metrics.export.SpecificTriggerProperties>",
      "description": "Specific trigger properties per MetricWriter bean name.",
      "sourceType": "org.springframework.boot.actuate.metrics.export.MetricExportProperties"
    }

具体路径 org/springframework/boot/spring-boot-actuator/1.3.5.RELEASE/spring-boot-actuator-1.3.5.RELEASE.jar!/META-INF/spring-configuration-metadata.json

集成statsd实例

    @Bean
    @ExportMetricWriter
    public StatsdMetricWriter statsdMetricWriter(
            @Value("${spring.metrics.export.statsd.host}") String host,
            @Value("${spring.metrics.export.statsd.port}") int port,
            @Value("${spring.metrics.export.statsd.prefix}") String prefix
    ) {
        return new StatsdMetricWriter(prefix, host, port);
    }

其实上面的springboot-acutator已经内置了,配置属性即可激活

spring.metrics.export.statsd.host=localhost
spring.metrics.export.statsd.port=8125
spring.metrics.export.statsd.prefix=metric-demo

同时增加maven依赖

<!-- https://mvnrepository.com/artifact/com.timgroup/java-statsd-client -->
        <dependency>
            <groupId>com.timgroup</groupId>
            <artifactId>java-statsd-client</artifactId>
            <version>3.1.0</version>
        </dependency>

配置类MetricExportProperties

@ConfigurationProperties("spring.metrics.export")
public class MetricExportProperties extends TriggerProperties {

    /**
     * Specific trigger properties per MetricWriter bean name.
     */
    private Map<String, SpecificTriggerProperties> triggers = new LinkedHashMap<String, SpecificTriggerProperties>();

    private Aggregate aggregate = new Aggregate();

    private Redis redis = new Redis();

    private Statsd statsd = new Statsd();

    @PostConstruct
    public void setUpDefaults() {
        TriggerProperties defaults = this;
        for (Entry<String, SpecificTriggerProperties> entry : this.triggers.entrySet()) {
            String key = entry.getKey();
            SpecificTriggerProperties value = entry.getValue();
            if (value.getNames() == null || value.getNames().length == 0) {
                value.setNames(new String[] { key });
            }
        }
        if (defaults.isSendLatest() == null) {
            defaults.setSendLatest(true);
        }
        if (defaults.getDelayMillis() == null) {
            defaults.setDelayMillis(5000);
        }
        for (TriggerProperties value : this.triggers.values()) {
            if (value.isSendLatest() == null) {
                value.setSendLatest(defaults.isSendLatest());
            }
            if (value.getDelayMillis() == null) {
                value.setDelayMillis(defaults.getDelayMillis());
            }
        }
    }

    /**
     * Configuration for triggers on individual named writers. Each value can individually
     * specify a name pattern explicitly, or else the map key will be used if the name is
     * not set.
     * @return the writers
     */
    public Map<String, SpecificTriggerProperties> getTriggers() {
        return this.triggers;
    }

    public Aggregate getAggregate() {
        return this.aggregate;
    }

    public void setAggregate(Aggregate aggregate) {
        this.aggregate = aggregate;
    }

    public Redis getRedis() {
        return this.redis;
    }

    public void setRedis(Redis redis) {
        this.redis = redis;
    }

    public Statsd getStatsd() {
        return this.statsd;
    }

    public void setStatsd(Statsd statsd) {
        this.statsd = statsd;
    }

    /**
     * Find a matching trigger configuration.
     * @param name the bean name to match
     * @return a matching configuration if there is one
     */
    public TriggerProperties findTrigger(String name) {
        for (SpecificTriggerProperties value : this.triggers.values()) {
            if (PatternMatchUtils.simpleMatch(value.getNames(), name)) {
                return value;
            }
        }
        return this;
    }

    /**
     * Aggregate properties.
     */
    public static class Aggregate {

        /**
         * Prefix for global repository if active. Should be unique for this JVM, but most
         * useful if it also has the form "a.b" where "a" is unique to this logical
         * process (this application) and "b" is unique to this physical process. If you
         * set spring.application.name elsewhere, then the default will be in the right
         * form.
         */
        private String prefix = "";

        /**
         * Pattern that tells the aggregator what to do with the keys from the source
         * repository. The keys in the source repository are assumed to be period
         * separated, and the pattern is in the same format, e.g. "d.d.k.d". Here "d"
         * means "discard" and "k" means "keep" the key segment in the corresponding
         * position in the source.
         */
        private String keyPattern = "";

        public String getPrefix() {
            return this.prefix;
        }

        public void setPrefix(String prefix) {
            this.prefix = prefix;
        }

        public String getKeyPattern() {
            return this.keyPattern;
        }

        public void setKeyPattern(String keyPattern) {
            this.keyPattern = keyPattern;
        }

    }

    /**
     * Redis properties.
     */
    public static class Redis {

        /**
         * Prefix for redis repository if active. Should be globally unique across all
         * processes sharing the same repository.
         */
        private String prefix = "spring.metrics";

        /**
         * Key for redis repository export (if active). Should be globally unique for a
         * system sharing a redis repository across multiple processes.
         */
        private String key = "keys.spring.metrics";

        public String getPrefix() {
            return this.prefix;
        }

        public void setPrefix(String prefix) {
            this.prefix = prefix;
        }

        public String getKey() {
            return this.key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public String getAggregatePrefix() {
            // The common case including a standalone aggregator would have a prefix that
            // starts with the end of the key, so strip that bit off and call it the
            // aggregate prefix.
            if (this.key.startsWith("keys.")) {
                String candidate = this.key.substring("keys.".length());
                if (this.prefix.startsWith(candidate)) {
                    return candidate;
                }
                return candidate;
            }
            // If the user went off piste, choose something that is safe (not empty) but
            // not the whole prefix (on the assumption that it contains dimension keys)
            if (this.prefix.contains(".")
                    && this.prefix.indexOf(".") < this.prefix.length() - 1) {
                return this.prefix.substring(this.prefix.indexOf(".") + 1);
            }
            return this.prefix;
        }

    }

    /**
     * Statsd properties.
     */
    public static class Statsd {

        /**
         * Host of a statsd server to receive exported metrics.
         */
        private String host;

        /**
         * Port of a statsd server to receive exported metrics.
         */
        private int port = 8125;

        /**
         * Prefix for statsd exported metrics.
         */
        private String prefix;

        public String getHost() {
            return this.host;
        }

        public void setHost(String host) {
            this.host = host;
        }

        public int getPort() {
            return this.port;
        }

        public void setPort(int port) {
            this.port = port;
        }

        public String getPrefix() {
            return this.prefix;
        }

        public void setPrefix(String prefix) {
            this.prefix = prefix;
        }

    }

}

MetricExportAutoConfiguration

@Configuration
@EnableScheduling
@ConditionalOnProperty(value = "spring.metrics.export.enabled", matchIfMissing = true)
@EnableConfigurationProperties
public class MetricExportAutoConfiguration {

    @Autowired
    private MetricExportProperties properties;

    @Autowired(required = false)
    private MetricsEndpointMetricReader endpointReader;

    @Autowired(required = false)
    @ExportMetricReader
    private List<MetricReader> readers;

    @Autowired(required = false)
    @ExportMetricWriter
    private Map<String, GaugeWriter> writers = Collections.emptyMap();

    @Autowired(required = false)
    private Map<String, Exporter> exporters = Collections.emptyMap();

    @Bean
    @ConditionalOnMissingBean(name = "metricWritersMetricExporter")
    public SchedulingConfigurer metricWritersMetricExporter() {
        Map<String, GaugeWriter> writers = new HashMap<String, GaugeWriter>();
        MetricReader reader = this.endpointReader;
        if (reader == null && !CollectionUtils.isEmpty(this.readers)) {
            reader = new CompositeMetricReader(
                    this.readers.toArray(new MetricReader[this.readers.size()]));
        }
        if (reader == null && this.exporters.isEmpty()) {
            return new NoOpSchedulingConfigurer();
        }
        MetricExporters exporters = new MetricExporters(this.properties);
        if (reader != null) {
            writers.putAll(this.writers);
            exporters.setReader(reader);
            exporters.setWriters(writers);
        }
        exporters.setExporters(this.exporters);
        return exporters;
    }

    @Bean
    @ExportMetricWriter
    @ConditionalOnMissingBean
    @ConditionalOnProperty(prefix = "spring.metrics.export.statsd", name = "host")
    public StatsdMetricWriter statsdMetricWriter() {
        MetricExportProperties.Statsd statsdProperties = this.properties.getStatsd();
        return new StatsdMetricWriter(statsdProperties.getPrefix(),
                statsdProperties.getHost(), statsdProperties.getPort());
    }

    @Configuration
    protected static class MetricExportPropertiesConfiguration {

        @Value("${spring.application.name:application}.${random.value:0000}")
        private String prefix = "";

        private String aggregateKeyPattern = "k.d";

        @Bean(name = "spring.metrics.export.CONFIGURATION_PROPERTIES")
        @ConditionalOnMissingBean
        public MetricExportProperties metricExportProperties() {
            MetricExportProperties export = new MetricExportProperties();
            export.getRedis().setPrefix("spring.metrics"
                    + (this.prefix.length() > 0 ? "." : "") + this.prefix);
            export.getAggregate().setPrefix(this.prefix);
            export.getAggregate().setKeyPattern(this.aggregateKeyPattern);
            return export;
        }

    }

    private static class NoOpSchedulingConfigurer implements SchedulingConfigurer {

        @Override
        public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        }

    }

}

MetricExporters定时export数据

public class MetricExporters implements SchedulingConfigurer, Closeable {

    private MetricReader reader;

    private Map<String, GaugeWriter> writers = new HashMap<String, GaugeWriter>();

    private final MetricExportProperties properties;

    private final Map<String, Exporter> exporters = new HashMap<String, Exporter>();

    private final Set<String> closeables = new HashSet<String>();

    public MetricExporters(MetricExportProperties properties) {
        this.properties = properties;
    }

    public void setReader(MetricReader reader) {
        this.reader = reader;
    }

    public void setWriters(Map<String, GaugeWriter> writers) {
        this.writers.putAll(writers);
    }

    public void setExporters(Map<String, Exporter> exporters) {
        this.exporters.putAll(exporters);
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        for (Entry<String, Exporter> entry : this.exporters.entrySet()) {
            String name = entry.getKey();
            Exporter exporter = entry.getValue();
            TriggerProperties trigger = this.properties.findTrigger(name);
            if (trigger != null) {
                ExportRunner runner = new ExportRunner(exporter);
                IntervalTask task = new IntervalTask(runner, trigger.getDelayMillis(),
                        trigger.getDelayMillis());
                taskRegistrar.addFixedDelayTask(task);
            }
        }
        for (Entry<String, GaugeWriter> entry : this.writers.entrySet()) {
            String name = entry.getKey();
            GaugeWriter writer = entry.getValue();
            TriggerProperties trigger = this.properties.findTrigger(name);
            if (trigger != null) {
                MetricCopyExporter exporter = getExporter(writer, trigger);
                this.exporters.put(name, exporter);
                this.closeables.add(name);
                ExportRunner runner = new ExportRunner(exporter);
                IntervalTask task = new IntervalTask(runner, trigger.getDelayMillis(),
                        trigger.getDelayMillis());
                taskRegistrar.addFixedDelayTask(task);
            }
        }
    }

    private MetricCopyExporter getExporter(GaugeWriter writer,
            TriggerProperties trigger) {
        MetricCopyExporter exporter = new MetricCopyExporter(this.reader, writer);
        exporter.setIncludes(trigger.getIncludes());
        exporter.setExcludes(trigger.getExcludes());
        exporter.setSendLatest(trigger.isSendLatest());
        return exporter;
    }

    public Map<String, Exporter> getExporters() {
        return this.exporters;
    }

    @Override
    public void close() throws IOException {
        for (String name : this.closeables) {
            Exporter exporter = this.exporters.get(name);
            if (exporter instanceof Closeable) {
                ((Closeable) exporter).close();
            }
        }
    }

    private static class ExportRunner implements Runnable {

        private final Exporter exporter;

        ExportRunner(Exporter exporter) {
            this.exporter = exporter;
        }

        @Override
        public void run() {
            this.exporter.export();
        }

    }

}

配置参数实例

spring.metrics.export.enabled=true
spring.metrics.export.send-latest=true
spring.metrics.export.delay-millis=10000
spring.metrics.export.statsd.host=localhost
spring.metrics.export.statsd.port=8125
spring.metrics.export.statsd.prefix=metric-demo

docs

阅读 7.8k

code-craft
spring boot , docker and so on 欢迎关注微信公众号: geek_luandun

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很...

11.6k 声望
1.9k 粉丝
0 条评论
你知道吗?

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很...

11.6k 声望
1.9k 粉丝
文章目录
宣传栏