Home / Class/ StructuredLogFormatterFactory Class — spring-boot Architecture

StructuredLogFormatterFactory Class — spring-boot Architecture

Architecture documentation for the StructuredLogFormatterFactory class in StructuredLogFormatterFactory.java from the spring-boot codebase.

Entity Profile

Relationship Graph

Source Code

core/spring-boot/src/main/java/org/springframework/boot/logging/structured/StructuredLogFormatterFactory.java lines 52–253

public class StructuredLogFormatterFactory<E> {

	private static final FailureHandler failureHandler = (type, implementationName, failure) -> {
		if (!(failure instanceof ClassNotFoundException)) {
			throw new IllegalArgumentException(
					"Unable to instantiate " + implementationName + " [" + type.getName() + "]", failure);
		}
	};

	private final SpringFactoriesLoader factoriesLoader;

	private final Class<E> logEventType;

	private final Instantiator<?> instantiator;

	private final CommonFormatters<E> commonFormatters;

	/**
	 * Create a new {@link StructuredLogFormatterFactory} instance.
	 * @param logEventType the log event type
	 * @param environment the Spring {@link Environment}
	 * @param availableParameters callback used to configure available parameters for the
	 * specific logging system
	 * @param commonFormatters callback used to define supported common formatters
	 */
	public StructuredLogFormatterFactory(Class<E> logEventType, Environment environment,
			@Nullable Consumer<AvailableParameters> availableParameters,
			Consumer<CommonFormatters<E>> commonFormatters) {
		this(SpringFactoriesLoader.forDefaultResourceLocation(), logEventType, environment, availableParameters,
				commonFormatters);
	}

	StructuredLogFormatterFactory(SpringFactoriesLoader factoriesLoader, Class<E> logEventType, Environment environment,
			@Nullable Consumer<AvailableParameters> availableParameters,
			Consumer<CommonFormatters<E>> commonFormatters) {
		StructuredLoggingJsonProperties properties = StructuredLoggingJsonProperties.get(environment);
		this.factoriesLoader = factoriesLoader;
		this.logEventType = logEventType;
		this.instantiator = new Instantiator<>(Object.class, (allAvailableParameters) -> {
			allAvailableParameters.add(Environment.class, environment);
			allAvailableParameters.add(StructuredLoggingJsonMembersCustomizer.class,
					new JsonMembersCustomizerBuilder(properties).build());
			allAvailableParameters.add(StructuredLoggingJsonMembersCustomizer.Builder.class,
					new JsonMembersCustomizerBuilder(properties));
			allAvailableParameters.add(StackTracePrinter.class, (type) -> getStackTracePrinter(properties));
			allAvailableParameters.add(ContextPairs.class, (type) -> getContextPairs(properties));
			if (availableParameters != null) {
				availableParameters.accept(allAvailableParameters);
			}
		}, failureHandler);
		this.commonFormatters = new CommonFormatters<>();
		commonFormatters.accept(this.commonFormatters);
	}

	private @Nullable StackTracePrinter getStackTracePrinter(@Nullable StructuredLoggingJsonProperties properties) {
		return (properties != null && properties.stackTrace() != null) ? properties.stackTrace().createPrinter() : null;
	}

	private ContextPairs getContextPairs(@Nullable StructuredLoggingJsonProperties properties) {
		Context contextProperties = (properties != null) ? properties.context() : null;
		contextProperties = (contextProperties != null) ? contextProperties : new Context(true, null);
		return new ContextPairs(contextProperties.include(), contextProperties.prefix());
	}

	/**
	 * Get a new {@link StructuredLogFormatter} instance for the specified format.
	 * @param format the format requested (either a {@link CommonStructuredLogFormat} ID
	 * or a fully-qualified class name)
	 * @return a new {@link StructuredLogFormatter} instance
	 * @throws IllegalArgumentException if the format is unknown
	 */
	public StructuredLogFormatter<E> get(String format) {
		StructuredLogFormatter<E> formatter = this.commonFormatters.get(this.instantiator, format);
		formatter = (formatter != null) ? formatter : getUsingClassName(format);
		if (formatter != null) {
			return formatter;
		}
		throw new IllegalArgumentException(
				"Unknown format '%s'. Values can be a valid fully-qualified class name or one of the common formats: %s"
					.formatted(format, this.commonFormatters.getCommonNames()));
	}

	@SuppressWarnings("unchecked")
	private @Nullable StructuredLogFormatter<E> getUsingClassName(String className) {
		Object formatter = this.instantiator.instantiate(className);
		if (formatter != null) {
			Assert.state(formatter instanceof StructuredLogFormatter,
					() -> "'%s' is not a StructuredLogFormatter".formatted(className));
			checkTypeArgument(formatter);
		}
		return (StructuredLogFormatter<E>) formatter;
	}

	private void checkTypeArgument(Object formatter) {
		Class<?> typeArgument = GenericTypeResolver.resolveTypeArgument(formatter.getClass(),
				StructuredLogFormatter.class);
		Assert.state(this.logEventType.equals(typeArgument),
				() -> "Type argument of %s must be %s but was %s".formatted(formatter.getClass().getName(),
						this.logEventType.getName(), (typeArgument != null) ? typeArgument.getName() : "null"));

	}

	/**
	 * Callback used for configure the {@link CommonFormatterFactory} to use for a given
	 * {@link CommonStructuredLogFormat}.
	 *
	 * @param <E> the log event type
	 */
	public static class CommonFormatters<E> {

		private final Map<CommonStructuredLogFormat, CommonFormatterFactory<E>> factories = new TreeMap<>();

		/**
		 * Add the factory that should be used for the given
		 * {@link CommonStructuredLogFormat}.
		 * @param format the common structured log format
		 * @param factory the factory to use
		 */
		public void add(CommonStructuredLogFormat format, CommonFormatterFactory<E> factory) {
			this.factories.put(format, factory);
		}

		Collection<String> getCommonNames() {
			return this.factories.keySet().stream().map(CommonStructuredLogFormat::getId).toList();
		}

		@Nullable StructuredLogFormatter<E> get(Instantiator<?> instantiator, String format) {
			CommonStructuredLogFormat commonFormat = CommonStructuredLogFormat.forId(format);
			CommonFormatterFactory<E> factory = (commonFormat != null) ? this.factories.get(commonFormat) : null;
			return (factory != null) ? factory.createFormatter(instantiator) : null;
		}

	}

	/**
	 * Factory used to create a {@link StructuredLogFormatter} for a given
	 * {@link CommonStructuredLogFormat}.
	 *
	 * @param <E> the log event type
	 */
	@FunctionalInterface
	public interface CommonFormatterFactory<E> {

		/**
		 * Create the {@link StructuredLogFormatter} instance.
		 * @param instantiator instantiator that can be used to obtain arguments
		 * @return a new {@link StructuredLogFormatter} instance
		 */
		StructuredLogFormatter<E> createFormatter(Instantiator<?> instantiator);

	}

	/**
	 * {@link StructuredLoggingJsonMembersCustomizer.Builder} implementation.
	 */
	class JsonMembersCustomizerBuilder implements StructuredLoggingJsonMembersCustomizer.Builder<E> {

		private final @Nullable StructuredLoggingJsonProperties properties;

		private boolean nested;

		JsonMembersCustomizerBuilder(@Nullable StructuredLoggingJsonProperties properties) {
			this.properties = properties;
		}

		@Override
		public JsonMembersCustomizerBuilder nested(boolean nested) {
			this.nested = nested;
			return this;
		}

		@Override
		public StructuredLoggingJsonMembersCustomizer<E> build() {
			return (members) -> {
				List<StructuredLoggingJsonMembersCustomizer<?>> customizers = new ArrayList<>();
				if (this.properties != null) {
					customizers.add(new StructuredLoggingJsonPropertiesJsonMembersCustomizer(
							StructuredLogFormatterFactory.this.instantiator, this.properties, this.nested));
				}
				customizers.addAll(loadStructuredLoggingJsonMembersCustomizers());
				invokeCustomizers(members, customizers);
			};
		}

		@SuppressWarnings({ "unchecked", "rawtypes" })
		private List<StructuredLoggingJsonMembersCustomizer<?>> loadStructuredLoggingJsonMembersCustomizers() {
			return (List) StructuredLogFormatterFactory.this.factoriesLoader.load(
					StructuredLoggingJsonMembersCustomizer.class,
					ArgumentResolver.from(StructuredLogFormatterFactory.this.instantiator::getArg));
		}

		@SuppressWarnings("unchecked")
		private void invokeCustomizers(Members<E> members,
				List<StructuredLoggingJsonMembersCustomizer<?>> customizers) {
			LambdaSafe.callbacks(StructuredLoggingJsonMembersCustomizer.class, customizers, members)
				.withFilter(LambdaSafe.Filter.allowAll())
				.invoke((customizer) -> customizer.customize(members));
		}

	}

}

Domain

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free