Skip to content

Commit

Permalink
Make PID and application version available in the environment
Browse files Browse the repository at this point in the history
Adds the following new properties:

- spring.application.pid
- spring.application.version

Refactors the ResourceBanner and the structured logging support to use
the new properties.

Closes gh-41604
  • Loading branch information
mhalbritter committed Aug 1, 2024
1 parent 518bc69 commit f4b4f4f
Show file tree
Hide file tree
Showing 22 changed files with 267 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,11 @@ void propertySourceOrdering() {
.stream()
.map(PropertySource::getName)
.collect(Collectors.toCollection(ArrayList::new));
String last = names.remove(names.size() - 1);
String configResource = names.remove(names.size() - 2);
assertThat(names).containsExactly("configurationProperties", "Inlined Test Properties", "commandLineArgs",
"servletConfigInitParams", "servletContextInitParams", "systemProperties", "systemEnvironment",
"random");
assertThat(last).startsWith("Config resource");
"random", "applicationInfo");
assertThat(configResource).startsWith("Config resource");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2012-2024 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
*
* https://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 org.springframework.boot;

import java.util.HashMap;
import java.util.Map;

import org.springframework.boot.system.ApplicationPid;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.util.StringUtils;

/**
* {@link PropertySource} which provides information about the application, like the
* process ID (PID) or the version.
*
* @author Moritz Halbritter
*/
class ApplicationInfoPropertySource extends MapPropertySource {

static final String NAME = "applicationInfo";

ApplicationInfoPropertySource(Class<?> mainClass) {
super(NAME, getProperties(readVersion(mainClass)));
}

ApplicationInfoPropertySource(String applicationVersion) {
super(NAME, getProperties(applicationVersion));
}

private static Map<String, Object> getProperties(String applicationVersion) {
Map<String, Object> result = new HashMap<>();
if (StringUtils.hasText(applicationVersion)) {
result.put("spring.application.version", applicationVersion);
}
ApplicationPid applicationPid = new ApplicationPid();
if (applicationPid.isAvailable()) {
result.put("spring.application.pid", applicationPid.toLong());
}
return result;
}

private static String readVersion(Class<?> applicationClass) {
Package sourcePackage = (applicationClass != null) ? applicationClass.getPackage() : null;
return (sourcePackage != null) ? sourcePackage.getImplementationVersion() : null;
}

/**
* Moves the {@link ApplicationInfoPropertySource} to the end of the environment's
* property sources.
* @param environment the environment
*/
static void moveToEnd(ConfigurableEnvironment environment) {
MutablePropertySources propertySources = environment.getPropertySources();
PropertySource<?> propertySource = propertySources.remove(NAME);
if (propertySource != null) {
propertySources.addLast(propertySource);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
* @author Vedran Pavic
* @author Toshiaki Maki
* @author Krzysztof Krason
* @author Moritz Halbritter
* @since 1.2.0
*/
public class ResourceBanner implements Banner {
Expand Down Expand Up @@ -91,7 +92,7 @@ protected List<PropertyResolver> getPropertyResolvers(Environment environment, C
}
sources.addLast(getTitleSource(sourceClass));
sources.addLast(getAnsiSource());
sources.addLast(getVersionSource(sourceClass));
sources.addLast(getVersionSource(sourceClass, environment));
List<PropertyResolver> resolvers = new ArrayList<>();
resolvers.add(new PropertySourcesPropertyResolver(sources));
return resolvers;
Expand Down Expand Up @@ -119,12 +120,15 @@ private AnsiPropertySource getAnsiSource() {
return new AnsiPropertySource("ansi", true);
}

private MapPropertySource getVersionSource(Class<?> sourceClass) {
return new MapPropertySource("version", getVersionsMap(sourceClass));
private MapPropertySource getVersionSource(Class<?> sourceClass, Environment environment) {
return new MapPropertySource("version", getVersionsMap(sourceClass, environment));
}

private Map<String, Object> getVersionsMap(Class<?> sourceClass) {
private Map<String, Object> getVersionsMap(Class<?> sourceClass, Environment environment) {
String appVersion = getApplicationVersion(sourceClass);
if (appVersion == null) {
appVersion = getApplicationVersion(environment);
}
String bootVersion = getBootVersion();
Map<String, Object> versions = new HashMap<>();
versions.put("application.version", getVersionString(appVersion, false));
Expand All @@ -134,9 +138,19 @@ private Map<String, Object> getVersionsMap(Class<?> sourceClass) {
return versions;
}

/**
* Returns the application version.
* @param sourceClass the source class
* @return the application version or {@code null} if unknown
* @deprecated since 3.4.0 for removal in 3.6.0
*/
@Deprecated(since = "3.4.0", forRemoval = true)
protected String getApplicationVersion(Class<?> sourceClass) {
Package sourcePackage = (sourceClass != null) ? sourceClass.getPackage() : null;
return (sourcePackage != null) ? sourcePackage.getImplementationVersion() : null;
return null;
}

private String getApplicationVersion(Environment environment) {
return environment.getProperty("spring.application.version");
}

protected String getBootVersion() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(bootstrapContext, environment);
ApplicationInfoPropertySource.moveToEnd(environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
"Environment prefix cannot be set via properties.");
Expand Down Expand Up @@ -539,6 +540,7 @@ protected void configurePropertySources(ConfigurableEnvironment environment, Str
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
environment.getPropertySources().addLast(new ApplicationInfoPropertySource(this.mainApplicationClass));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.springframework.boot.logging.log4j2;

import java.util.Objects;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.impl.ThrowableProxy;
Expand All @@ -28,7 +30,7 @@
import org.springframework.boot.logging.structured.ElasticCommonSchemaService;
import org.springframework.boot.logging.structured.JsonWriterStructuredLogFormatter;
import org.springframework.boot.logging.structured.StructuredLogFormatter;
import org.springframework.boot.system.ApplicationPid;
import org.springframework.core.env.Environment;
import org.springframework.util.ObjectUtils;

/**
Expand All @@ -40,17 +42,17 @@
*/
class ElasticCommonSchemaStructuredLogFormatter extends JsonWriterStructuredLogFormatter<LogEvent> {

ElasticCommonSchemaStructuredLogFormatter(ApplicationPid pid, ElasticCommonSchemaService service) {
super((members) -> jsonMembers(pid, service, members));
ElasticCommonSchemaStructuredLogFormatter(Environment environment) {
super((members) -> jsonMembers(environment, members));
}

private static void jsonMembers(ApplicationPid pid, ElasticCommonSchemaService service,
JsonWriter.Members<LogEvent> members) {
private static void jsonMembers(Environment environment, JsonWriter.Members<LogEvent> members) {
members.add("@timestamp", LogEvent::getInstant).as(ElasticCommonSchemaStructuredLogFormatter::asTimestamp);
members.add("log.level", LogEvent::getLevel).as(Level::name);
members.add("process.pid", pid).when(ApplicationPid::isAvailable).as(ApplicationPid::toLong);
members.add("process.pid", environment.getProperty("spring.application.pid", Long.class))
.when(Objects::nonNull);
members.add("process.thread.name", LogEvent::getThreadName);
service.jsonMembers(members);
ElasticCommonSchemaService.get(environment).jsonMembers(members);
members.add("log.logger", LogEvent::getLoggerName);
members.add("message", LogEvent::getMessage).as(Message::getFormattedMessage);
members.from(LogEvent::getContextData)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,9 @@
import org.apache.logging.log4j.core.layout.AbstractStringLayout;

import org.springframework.boot.logging.structured.CommonStructuredLogFormat;
import org.springframework.boot.logging.structured.ElasticCommonSchemaService;
import org.springframework.boot.logging.structured.StructuredLogFormatter;
import org.springframework.boot.logging.structured.StructuredLogFormatterFactory;
import org.springframework.boot.logging.structured.StructuredLogFormatterFactory.CommonFormatters;
import org.springframework.boot.system.ApplicationPid;
import org.springframework.core.env.Environment;
import org.springframework.util.Assert;

Expand Down Expand Up @@ -106,8 +104,7 @@ public StructuredLogLayout build() {
private void addCommonFormatters(CommonFormatters<LogEvent> commonFormatters) {
commonFormatters.add(CommonStructuredLogFormat.ELASTIC_COMMON_SCHEMA,
(instantiator) -> new ElasticCommonSchemaStructuredLogFormatter(
instantiator.getArg(ApplicationPid.class),
instantiator.getArg(ElasticCommonSchemaService.class)));
instantiator.getArg(Environment.class)));
commonFormatters.add(CommonStructuredLogFormat.LOGSTASH,
(instantiator) -> new LogstashStructuredLogFormatter());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.springframework.boot.logging.logback;

import java.util.Objects;

import ch.qos.logback.classic.pattern.ThrowableProxyConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
Expand All @@ -27,7 +29,7 @@
import org.springframework.boot.logging.structured.ElasticCommonSchemaService;
import org.springframework.boot.logging.structured.JsonWriterStructuredLogFormatter;
import org.springframework.boot.logging.structured.StructuredLogFormatter;
import org.springframework.boot.system.ApplicationPid;
import org.springframework.core.env.Environment;

/**
* Logback {@link StructuredLogFormatter} for
Expand All @@ -41,18 +43,19 @@ class ElasticCommonSchemaStructuredLogFormatter extends JsonWriterStructuredLogF
private static final PairExtractor<KeyValuePair> keyValuePairExtractor = PairExtractor.of((pair) -> pair.key,
(pair) -> pair.value);

ElasticCommonSchemaStructuredLogFormatter(ApplicationPid pid, ElasticCommonSchemaService service,
ElasticCommonSchemaStructuredLogFormatter(Environment environment,
ThrowableProxyConverter throwableProxyConverter) {
super((members) -> jsonMembers(pid, service, throwableProxyConverter, members));
super((members) -> jsonMembers(environment, throwableProxyConverter, members));
}

private static void jsonMembers(ApplicationPid pid, ElasticCommonSchemaService service,
ThrowableProxyConverter throwableProxyConverter, JsonWriter.Members<ILoggingEvent> members) {
private static void jsonMembers(Environment environment, ThrowableProxyConverter throwableProxyConverter,
JsonWriter.Members<ILoggingEvent> members) {
members.add("@timestamp", ILoggingEvent::getInstant);
members.add("log.level", ILoggingEvent::getLevel);
members.add("process.pid", pid).when(ApplicationPid::isAvailable).as(ApplicationPid::toLong);
members.add("process.pid", environment.getProperty("spring.application.pid", Long.class))
.when(Objects::nonNull);
members.add("process.thread.name", ILoggingEvent::getThreadName);
service.jsonMembers(members);
ElasticCommonSchemaService.get(environment).jsonMembers(members);
members.add("log.logger", ILoggingEvent::getLoggerName);
members.add("message", ILoggingEvent::getFormattedMessage);
members.addMapEntries(ILoggingEvent::getMDCPropertyMap);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,9 @@
import ch.qos.logback.core.encoder.EncoderBase;

import org.springframework.boot.logging.structured.CommonStructuredLogFormat;
import org.springframework.boot.logging.structured.ElasticCommonSchemaService;
import org.springframework.boot.logging.structured.StructuredLogFormatter;
import org.springframework.boot.logging.structured.StructuredLogFormatterFactory;
import org.springframework.boot.logging.structured.StructuredLogFormatterFactory.CommonFormatters;
import org.springframework.boot.system.ApplicationPid;
import org.springframework.boot.util.Instantiator.AvailableParameters;
import org.springframework.core.env.Environment;
import org.springframework.util.Assert;
Expand Down Expand Up @@ -82,9 +80,7 @@ private void addAvailableParameters(AvailableParameters availableParameters) {

private void addCommonFormatters(CommonFormatters<ILoggingEvent> commonFormatters) {
commonFormatters.add(CommonStructuredLogFormat.ELASTIC_COMMON_SCHEMA,
(instantiator) -> new ElasticCommonSchemaStructuredLogFormatter(
instantiator.getArg(ApplicationPid.class),
instantiator.getArg(ElasticCommonSchemaService.class),
(instantiator) -> new ElasticCommonSchemaStructuredLogFormatter(instantiator.getArg(Environment.class),
instantiator.getArg(ThrowableProxyConverter.class)));
commonFormatters.add(CommonStructuredLogFormat.LOGSTASH, (instantiator) -> new LogstashStructuredLogFormatter(
instantiator.getArg(ThrowableProxyConverter.class)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public record ElasticCommonSchemaService(String name, String version, String env

private ElasticCommonSchemaService withDefaults(Environment environment) {
String name = withFallbackProperty(environment, this.name, "spring.application.name");
return new ElasticCommonSchemaService(name, this.version, this.environment, this.nodeName);
String version = withFallbackProperty(environment, this.version, "spring.application.version");
return new ElasticCommonSchemaService(name, version, this.environment, this.nodeName);
}

private String withFallbackProperty(Environment environment, String value, String property) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

import ch.qos.logback.classic.pattern.ThrowableProxyConverter;

import org.springframework.boot.system.ApplicationPid;
import org.springframework.core.env.Environment;

/**
Expand All @@ -29,8 +28,6 @@
* Implementing classes can declare the following parameter types in the constructor:
* <ul>
* <li>{@link Environment}</li>
* <li>{@link ApplicationPid}</li>
* <li>{@link ElasticCommonSchemaService}</li>
* </ul>
* When using Logback, implementing classes can also use the following parameter types in
* the constructor:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.util.TreeMap;
import java.util.function.Consumer;

import org.springframework.boot.system.ApplicationPid;
import org.springframework.boot.util.Instantiator;
import org.springframework.boot.util.Instantiator.AvailableParameters;
import org.springframework.boot.util.Instantiator.FailureHandler;
Expand Down Expand Up @@ -68,9 +67,6 @@ public StructuredLogFormatterFactory(Class<E> logEventType, Environment environm
this.logEventType = logEventType;
this.instantiator = new Instantiator<>(StructuredLogFormatter.class, (allAvailableParameters) -> {
allAvailableParameters.add(Environment.class, environment);
allAvailableParameters.add(ApplicationPid.class, (type) -> new ApplicationPid());
allAvailableParameters.add(ElasticCommonSchemaService.class,
(type) -> ElasticCommonSchemaService.get(environment));
if (availableParameters != null) {
availableParameters.accept(allAvailableParameters);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,11 @@
"sourceType": "org.springframework.boot.context.ContextIdApplicationContextInitializer",
"description": "Application name."
},
{
"name": "spring.application.version",
"type": "java.lang.String",
"description": "Application version (defaults to 'Implementation-Version' from the manifest)."
},
{
"name": "spring.banner.charset",
"type": "java.nio.charset.Charset",
Expand Down
Loading

0 comments on commit f4b4f4f

Please sign in to comment.