Spring Boot starter that auto-configures a client application to register with Spring Boot Admin server for centralized monitoring and management
—
Metadata management allows you to attach custom information to your application when registering with Spring Boot Admin. This metadata is displayed in the admin UI and can be used for filtering, organization, and monitoring purposes.
@FunctionalInterface
public interface MetadataContributor {
/**
* Contribute metadata as key-value pairs
* @return Map of metadata entries
*/
Map<String, String> getMetadata();
}public class StartupDateMetadataContributor implements MetadataContributor {
@Override
public Map<String, String> getMetadata();
}Automatically contributes the application startup timestamp.
public class CompositeMetadataContributor implements MetadataContributor {
public CompositeMetadataContributor(List<MetadataContributor> contributors);
@Override
public Map<String, String> getMetadata();
}Combines metadata from multiple contributors into a single map.
@Component
public class EnvironmentMetadataContributor implements MetadataContributor {
private final Environment environment;
public EnvironmentMetadataContributor(Environment environment) {
this.environment = environment;
}
@Override
public Map<String, String> getMetadata() {
Map<String, String> metadata = new HashMap<>();
// Add active profiles
String[] profiles = environment.getActiveProfiles();
metadata.put("profiles", String.join(",", profiles));
// Add environment-specific info
metadata.put("environment", environment.getProperty("app.environment", "unknown"));
metadata.put("version", environment.getProperty("app.version", "unknown"));
return metadata;
}
}@Component
public class BuildMetadataContributor implements MetadataContributor {
private final BuildProperties buildProperties;
public BuildMetadataContributor(BuildProperties buildProperties) {
this.buildProperties = buildProperties;
}
@Override
public Map<String, String> getMetadata() {
Map<String, String> metadata = new HashMap<>();
if (buildProperties != null) {
metadata.put("build.version", buildProperties.getVersion());
metadata.put("build.time", buildProperties.getTime().toString());
metadata.put("build.artifact", buildProperties.getArtifact());
metadata.put("build.group", buildProperties.getGroup());
}
return metadata;
}
}@Component
public class GitMetadataContributor implements MetadataContributor {
private final GitProperties gitProperties;
public GitMetadataContributor(GitProperties gitProperties) {
this.gitProperties = gitProperties;
}
@Override
public Map<String, String> getMetadata() {
Map<String, String> metadata = new HashMap<>();
if (gitProperties != null) {
metadata.put("git.branch", gitProperties.getBranch());
metadata.put("git.commit.id", gitProperties.getShortCommitId());
metadata.put("git.commit.time", gitProperties.getCommitTime().toString());
}
return metadata;
}
}@Component
public class SystemMetadataContributor implements MetadataContributor {
@Override
public Map<String, String> getMetadata() {
Map<String, String> metadata = new HashMap<>();
// JVM information
metadata.put("java.version", System.getProperty("java.version"));
metadata.put("java.vendor", System.getProperty("java.vendor"));
// System information
metadata.put("os.name", System.getProperty("os.name"));
metadata.put("os.arch", System.getProperty("os.arch"));
// Runtime information
Runtime runtime = Runtime.getRuntime();
metadata.put("jvm.max.memory", String.valueOf(runtime.maxMemory()));
metadata.put("jvm.processors", String.valueOf(runtime.availableProcessors()));
// Container/hostname info
try {
metadata.put("hostname", InetAddress.getLocalHost().getHostName());
} catch (UnknownHostException e) {
metadata.put("hostname", "unknown");
}
return metadata;
}
}# Static metadata via properties
spring.boot.admin.client.instance.metadata.environment=production
spring.boot.admin.client.instance.metadata.team=backend
spring.boot.admin.client.instance.metadata.region=us-east-1
spring.boot.admin.client.instance.metadata.version=1.2.3
spring.boot.admin.client.instance.metadata.deployed-by=jenkinsspring:
boot:
admin:
client:
instance:
metadata:
environment: production
team: backend
region: us-east-1
version: 1.2.3
deployed-by: jenkins
custom-tag: custom-value@Component
@Profile("production")
public class ProductionMetadataContributor implements MetadataContributor {
@Override
public Map<String, String> getMetadata() {
Map<String, String> metadata = new HashMap<>();
metadata.put("environment", "production");
metadata.put("monitoring.level", "enhanced");
return metadata;
}
}@Component
@ConditionalOnProperty(value = "app.metadata.kubernetes.enabled", havingValue = "true")
public class KubernetesMetadataContributor implements MetadataContributor {
@Override
public Map<String, String> getMetadata() {
Map<String, String> metadata = new HashMap<>();
// Read from Kubernetes downward API
metadata.put("k8s.namespace", System.getenv("POD_NAMESPACE"));
metadata.put("k8s.pod.name", System.getenv("POD_NAME"));
metadata.put("k8s.node.name", System.getenv("NODE_NAME"));
return metadata;
}
}While metadata is typically static at registration time, you can implement dynamic updates:
@Component
@Scheduled(fixedRate = 60000) // Update every minute
public class DynamicMetadataService {
private final ApplicationRegistrator registrator;
public DynamicMetadataService(ApplicationRegistrator registrator) {
this.registrator = registrator;
}
@Scheduled(fixedRate = 300000) // Re-register every 5 minutes with updated metadata
public void updateMetadata() {
// This will trigger re-registration with current metadata
registrator.register();
}
}environment=production, stage=stagingversion=1.2.3, build=456team=backend, owner=user-service-teamregion=us-east-1, datacenter=dc1framework=spring-boot, database=postgresqldeployed-by=jenkins, deployed-at=2023-12-01T10:00:00ZInstall with Tessl CLI
npx tessl i tessl/maven-de-codecentric--spring-boot-admin-starter-client