mirror of
https://gitee.com/dcren/initializr.git
synced 2025-07-15 05:13:15 +08:00
Add metrics exporter for initializr service
If a RedisConnectionFactory is detected the system will now export metrics to redis every 5s (initializr.metrics.rateMillis). Metric names are prefixed with <id>.<hex>. where <id> defaults to the application name (initializr.metrics.id) and <hex> is unique. An aggregator app can then pick up the values and (for instance) continue counters across restarts. The code is meant to be easier to upgrade Spring Boot 1.3 There are 2 beans that we can simply remove when we upgrade to 1.3. Meanwhile, the app will run with 1.3 (or 1.2) for testing if you uncomment the @ExportMetricWriter.
This commit is contained in:
parent
7fdde4b0e4
commit
6f4d3f4e03
3
.gitignore
vendored
3
.gitignore
vendored
@ -16,6 +16,7 @@ tmp*
|
||||
initializer-service/spring
|
||||
grapes
|
||||
spring.zip
|
||||
*.jar
|
||||
repository/
|
||||
.idea
|
||||
*.iml
|
||||
*.iml
|
||||
|
@ -3,6 +3,7 @@ package app
|
||||
import io.spring.initializr.web.LegacyStsController
|
||||
|
||||
@Grab('io.spring.initalizr:initializr:1.0.0.BUILD-SNAPSHOT')
|
||||
@Grab('spring-boot-starter-redis')
|
||||
class InitializerService {
|
||||
|
||||
@Bean
|
||||
|
@ -23,6 +23,11 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-redis</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-groovy-templates</artifactId>
|
||||
@ -234,4 +239,4 @@
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.spring.initializr.config
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.actuate.metrics.export.Exporter
|
||||
import org.springframework.boot.actuate.metrics.export.MetricCopyExporter
|
||||
import org.springframework.boot.actuate.metrics.repository.InMemoryMetricRepository
|
||||
import org.springframework.boot.actuate.metrics.repository.MetricRepository
|
||||
import org.springframework.boot.actuate.metrics.repository.redis.RedisMetricRepository
|
||||
import org.springframework.boot.actuate.metrics.writer.MetricWriter
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
import org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||
import org.springframework.context.ApplicationContext
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.context.annotation.Primary
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory
|
||||
import org.springframework.scheduling.annotation.EnableScheduling
|
||||
import org.springframework.scheduling.annotation.Scheduled
|
||||
import org.springframework.util.ObjectUtils
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnBean(RedisConnectionFactory)
|
||||
@ConditionalOnProperty(value='spring.metrics.export.enabled', matchIfMissing=true)
|
||||
@EnableScheduling
|
||||
@EnableConfigurationProperties(MetricsProperties)
|
||||
@AutoConfigureAfter(value=RedisAutoConfiguration, name="org.springframework.boot.actuate.autoconfigure.MetricExportAutoConfiguration")
|
||||
class InitializrMetricsExporterAutoConfiguration {
|
||||
|
||||
@Autowired
|
||||
RedisConnectionFactory connectionFactory
|
||||
|
||||
@Autowired
|
||||
MetricsProperties metrics
|
||||
|
||||
@Autowired
|
||||
ApplicationContext context
|
||||
|
||||
@Bean
|
||||
// @ExportMetricWriter // Add this when upgrading to Boot 1.3
|
||||
MetricWriter writer() {
|
||||
new RedisMetricRepository(connectionFactory,
|
||||
metrics.prefix + metrics.getId(context.getId()) + '.'
|
||||
+ ObjectUtils.getIdentityHexString(context) + '.',
|
||||
metrics.key)
|
||||
}
|
||||
|
||||
// Remove this when upgrading to Boot 1.3
|
||||
@Bean
|
||||
@ConditionalOnMissingClass(name='org.springframework.boot.actuate.autoconfigure.ActuatorMetricWriter')
|
||||
@Primary
|
||||
MetricRepository reader() {
|
||||
new InMemoryMetricRepository()
|
||||
}
|
||||
|
||||
// Remove this when upgrading to Boot 1.3
|
||||
@Bean
|
||||
@ConditionalOnMissingClass(name='org.springframework.boot.actuate.autoconfigure.ActuatorMetricWriter')
|
||||
Exporter exporter(InMemoryMetricRepository reader) {
|
||||
new MetricCopyExporter(reader, writer()) {
|
||||
@Override
|
||||
@Scheduled(fixedRateString = '${spring.metrics.export.default.delayMillis:5000}')
|
||||
void export() {
|
||||
super.export()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.spring.initializr.config
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@ConfigurationProperties('initializr.metrics')
|
||||
class MetricsProperties {
|
||||
|
||||
/**
|
||||
* Prefix for redis keys holding metrics in data store.
|
||||
*/
|
||||
String prefix = 'spring.metrics.collector.'
|
||||
|
||||
/**
|
||||
* Redis key holding index to metrics keys in data store.
|
||||
*/
|
||||
String key = 'keys.spring.metrics.collector'
|
||||
|
||||
/**
|
||||
* Identifier for application in metrics keys. Keys will be exported in the form
|
||||
* '[id].[hex].[name]' (where '[id]' is this value, '[hex]' is unique per application
|
||||
* context, and '[name]' is the "natural" name for the metric.
|
||||
*/
|
||||
@Value('${spring.application.name:${vcap.application.name:application}}')
|
||||
String id
|
||||
|
||||
/**
|
||||
* The rate (in milliseconds) at which metrics are exported to Redis. If the value is
|
||||
* <=0 then the export is disabled.
|
||||
*/
|
||||
@Value('${spring.metrics.export.default.delayMillis:5000}')
|
||||
long rateMillis = 5000L
|
||||
|
||||
boolean isEnabled() {
|
||||
rateMillis > 0
|
||||
}
|
||||
|
||||
String getPrefix() {
|
||||
if (prefix.endsWith('.')) {
|
||||
return prefix
|
||||
}
|
||||
prefix + '.'
|
||||
}
|
||||
|
||||
String getId(String defaultValue) {
|
||||
if (id) return id
|
||||
defaultValue
|
||||
}
|
||||
}
|
@ -30,7 +30,7 @@ class InitializrConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a suitable application mame based on the specified name. If no suitable
|
||||
* Generate a suitable application name based on the specified name. If no suitable
|
||||
* application name can be generated from the specified {@code name}, the
|
||||
* {@link Env#fallbackApplicationName} is used instead.
|
||||
* <p>No suitable application name can be generated if the name is {@code null} or
|
||||
|
@ -20,6 +20,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||
|
||||
/**
|
||||
* Configuration of the initializr service.
|
||||
*
|
||||
|
@ -1 +1,3 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=io.spring.initializr.config.InitializrAutoConfiguration
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
io.spring.initializr.config.InitializrAutoConfiguration,\
|
||||
io.spring.initializr.config.InitializrMetricsExporterAutoConfiguration
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.spring.initializr.metrics;
|
||||
|
||||
import static org.junit.Assert.*
|
||||
import io.spring.initializr.generator.ProjectGenerationMetricsListener
|
||||
import io.spring.initializr.generator.ProjectRequest
|
||||
import io.spring.initializr.metadata.DefaultMetadataElement
|
||||
import io.spring.initializr.metadata.InitializrMetadata
|
||||
import io.spring.initializr.metadata.InitializrMetadataProvider
|
||||
import io.spring.initializr.support.DefaultInitializrMetadataProvider
|
||||
import io.spring.initializr.test.RedisRunning
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.boot.actuate.metrics.repository.redis.RedisMetricRepository
|
||||
import org.springframework.boot.actuate.metrics.writer.MetricWriter
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
import org.springframework.boot.test.IntegrationTest
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner)
|
||||
@SpringApplicationConfiguration(classes = Config)
|
||||
@IntegrationTest(['spring.metrics.export.default.delayMillis:500','initializr.metrics.prefix:test.prefix','initializr.metrics.key:key.test'])
|
||||
public class MetricsExportTests {
|
||||
|
||||
@Rule
|
||||
public RedisRunning running = new RedisRunning()
|
||||
|
||||
@Autowired
|
||||
ProjectGenerationMetricsListener listener
|
||||
|
||||
@Autowired
|
||||
@Qualifier("writer")
|
||||
MetricWriter writer
|
||||
|
||||
RedisMetricRepository repository
|
||||
|
||||
@Before
|
||||
void init() {
|
||||
repository = (RedisMetricRepository) writer
|
||||
repository.findAll().each {
|
||||
repository.reset(it.name)
|
||||
}
|
||||
assertTrue("Metrics not empty", repository.findAll().size()==0)
|
||||
}
|
||||
|
||||
@Test
|
||||
void exportAndCheckMetricsExist() {
|
||||
listener.onGeneratedProject(new ProjectRequest())
|
||||
Thread.sleep(1000L)
|
||||
assertTrue("No metrics exported", repository.findAll().size()>0)
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
InitializrMetadataProvider initializrMetadataProvider(InitializrMetadata metadata) {
|
||||
new DefaultInitializrMetadataProvider(metadata) {
|
||||
@Override
|
||||
protected List<DefaultMetadataElement> fetchBootVersions() {
|
||||
null // Disable metadata fetching from spring.io
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.spring.initializr.test
|
||||
|
||||
import org.junit.Assume
|
||||
import org.junit.rules.TestWatcher
|
||||
import org.junit.runner.Description
|
||||
import org.junit.runners.model.Statement
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
class RedisRunning extends TestWatcher {
|
||||
|
||||
JedisConnectionFactory connectionFactory;
|
||||
|
||||
@Override
|
||||
Statement apply(Statement base, Description description) {
|
||||
if (connectionFactory==null) {
|
||||
connectionFactory = new JedisConnectionFactory()
|
||||
connectionFactory.afterPropertiesSet()
|
||||
}
|
||||
try {
|
||||
connectionFactory.connection
|
||||
} catch (Exception e) {
|
||||
Assume.assumeNoException('Cannot connect to Redis (so skipping tests)', e)
|
||||
}
|
||||
super.apply(base, description)
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user