Skip to content

Commit

Permalink
Make PID, application version and title 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
- spring.application.title

Refactors the ResourceBanner and the structured logging support to use
the new properties.
  • Loading branch information
mhalbritter committed Jul 31, 2024
1 parent 67bae52 commit 99b5b7e
Show file tree
Hide file tree
Showing 22 changed files with 283 additions and 131 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,8 @@ void propertySourceOrdering() {
.collect(Collectors.toCollection(ArrayList::new));
String last = names.remove(names.size() - 1);
assertThat(names).containsExactly("configurationProperties", "Inlined Test Properties", "commandLineArgs",
"servletConfigInitParams", "servletContextInitParams", "systemProperties", "systemEnvironment",
"random");
"applicationInfo", "servletConfigInitParams", "servletContextInitParams", "systemProperties",
"systemEnvironment", "random");
assertThat(last).startsWith("Config resource");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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.boot.system.ApplicationTitle;
import org.springframework.boot.system.ApplicationVersion;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;

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

static final String NAME = "applicationInfo";

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

private static Map<String, Object> getProperties(Class<?> mainClass) {
Map<String, Object> result = new HashMap<>();
ApplicationVersion applicationVersion = new ApplicationVersion(mainClass);
if (applicationVersion.isAvailable()) {
result.put("spring.application.version", applicationVersion.asString());
}
ApplicationTitle applicationTitle = new ApplicationTitle(mainClass);
if (applicationTitle.isAvailable()) {
result.put("spring.application.title", applicationTitle.asString());
}
ApplicationPid applicationPid = new ApplicationPid();
if (applicationPid.isAvailable()) {
result.put("spring.application.pid", applicationPid.asLong());
}
return result;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -47,6 +46,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 @@ -89,42 +89,29 @@ protected List<PropertyResolver> getPropertyResolvers(Environment environment, C
if (environment instanceof ConfigurableEnvironment configurableEnvironment) {
configurableEnvironment.getPropertySources().forEach(sources::addLast);
}
sources.addLast(getTitleSource(sourceClass));
sources.addLast(getTitleSource(environment));
sources.addLast(getAnsiSource());
sources.addLast(getVersionSource(sourceClass));
sources.addLast(getVersionSource(environment));
List<PropertyResolver> resolvers = new ArrayList<>();
resolvers.add(new PropertySourcesPropertyResolver(sources));
return resolvers;
}

private MapPropertySource getTitleSource(Class<?> sourceClass) {
String applicationTitle = getApplicationTitle(sourceClass);
Map<String, Object> titleMap = Collections.singletonMap("application.title",
(applicationTitle != null) ? applicationTitle : "");
return new MapPropertySource("title", titleMap);
}

/**
* Return the application title that should be used for the source class. By default
* will use {@link Package#getImplementationTitle()}.
* @param sourceClass the source class
* @return the application title
*/
protected String getApplicationTitle(Class<?> sourceClass) {
Package sourcePackage = (sourceClass != null) ? sourceClass.getPackage() : null;
return (sourcePackage != null) ? sourcePackage.getImplementationTitle() : null;
private MapPropertySource getTitleSource(Environment environment) {
String title = environment.getProperty("spring.application.title");
return new MapPropertySource("title", Map.of("application.title", (title != null) ? title : ""));
}

private AnsiPropertySource getAnsiSource() {
return new AnsiPropertySource("ansi", true);
}

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

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

protected String getApplicationVersion(Class<?> sourceClass) {
Package sourcePackage = (sourceClass != null) ? sourceClass.getPackage() : null;
return (sourcePackage != null) ? sourcePackage.getImplementationVersion() : null;
}

protected String getBootVersion() {
return SpringBootVersion.getVersion();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,7 @@ protected void configurePropertySources(ConfigurableEnvironment environment, Str
if (!CollectionUtils.isEmpty(this.defaultProperties)) {
DefaultPropertiesPropertySource.addOrMerge(this.defaultProperties, sources);
}
sources.addFirst(new ApplicationInfoPropertySource(this.mainApplicationClass));
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
if (sources.contains(name)) {
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 @@ -71,7 +71,7 @@ public boolean isAvailable() {
* @return the application PID or {@code null}
* @since 3.4.0
*/
public Long toLong() {
public Long asLong() {
return this.pid;
}

Expand Down
Loading

0 comments on commit 99b5b7e

Please sign in to comment.