mirror of
https://github.com/elastic/logstash.git
synced 2025-04-24 06:37:19 -04:00
Moves the application of jackson defaults overrides into pure java, and
applies them statically _before_ the `org.logstash.ObjectMappers` has a chance
to start initializing object mappers that rely on the defaults.
We replace the runner's invocation (which was too late to be fully applied) with
a _verification_ that the configured defaults have been applied.
(cherry picked from commit 202d07cbbf
)
Co-authored-by: Ry Biesemeyer <yaauie@users.noreply.github.com>
This commit is contained in:
parent
1d82e377ff
commit
5b3e62e52d
10 changed files with 389 additions and 186 deletions
|
@ -353,8 +353,8 @@ class LogStash::Runner < Clamp::StrictCommand
|
|||
# Add local modules to the registry before everything else
|
||||
LogStash::Modules::Util.register_local_modules(LogStash::Environment::LOGSTASH_HOME)
|
||||
|
||||
# Set up the Jackson defaults
|
||||
LogStash::Util::Jackson.set_jackson_defaults(logger)
|
||||
# Verify the Jackson defaults
|
||||
LogStash::Util::Jackson.verify_jackson_overrides
|
||||
|
||||
@dispatcher = LogStash::EventDispatcher.new(self)
|
||||
LogStash::PLUGIN_REGISTRY.hooks.register_emitter(self.class, @dispatcher)
|
||||
|
|
|
@ -18,76 +18,13 @@
|
|||
module LogStash
|
||||
module Util
|
||||
module Jackson
|
||||
def self.set_jackson_defaults(logger)
|
||||
JacksonStreamReadConstraintsDefaults.new(logger).configure
|
||||
|
||||
def self.verify_jackson_overrides
|
||||
java_import org.logstash.ObjectMappers
|
||||
|
||||
ObjectMappers::getConfiguredStreamReadConstraints().validateIsGlobalDefault()
|
||||
end
|
||||
|
||||
class JacksonStreamReadConstraintsDefaults
|
||||
|
||||
java_import com.fasterxml.jackson.core.StreamReadConstraints
|
||||
|
||||
PROPERTY_MAX_STRING_LENGTH = 'logstash.jackson.stream-read-constraints.max-string-length'.freeze
|
||||
PROPERTY_MAX_NUMBER_LENGTH = 'logstash.jackson.stream-read-constraints.max-number-length'.freeze
|
||||
PROPERTY_MAX_NESTING_DEPTH = 'logstash.jackson.stream-read-constraints.max-nesting-depth'.freeze
|
||||
|
||||
def initialize(logger)
|
||||
@logger = logger
|
||||
end
|
||||
|
||||
public
|
||||
|
||||
def configure
|
||||
max_string_len = get_default_value_override!(PROPERTY_MAX_STRING_LENGTH)
|
||||
max_num_len = get_default_value_override!(PROPERTY_MAX_NUMBER_LENGTH)
|
||||
max_nesting_depth = get_default_value_override!(PROPERTY_MAX_NESTING_DEPTH)
|
||||
|
||||
if max_string_len || max_num_len || max_nesting_depth
|
||||
begin
|
||||
override_default_stream_read_constraints(max_string_len, max_num_len, max_nesting_depth)
|
||||
rescue java.lang.IllegalArgumentException => e
|
||||
raise LogStash::ConfigurationError, "Invalid `logstash.jackson.*` system properties configuration: #{e.message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_default_value_override!(property)
|
||||
value = get_property_value(property)
|
||||
return if value.nil?
|
||||
|
||||
begin
|
||||
int_value = java.lang.Integer.parseInt(value)
|
||||
|
||||
if int_value < 1
|
||||
raise LogStash::ConfigurationError, "System property '#{property}' must be bigger than zero. Received: #{int_value}"
|
||||
end
|
||||
|
||||
@logger.info("Jackson default value override `#{property}` configured to `#{int_value}`")
|
||||
|
||||
int_value
|
||||
rescue java.lang.NumberFormatException => _e
|
||||
raise LogStash::ConfigurationError, "System property '#{property}' must be a positive integer value. Received: #{value}"
|
||||
end
|
||||
end
|
||||
|
||||
def get_property_value(name)
|
||||
java.lang.System.getProperty(name)
|
||||
end
|
||||
|
||||
def override_default_stream_read_constraints(max_string_len, max_num_len, max_nesting_depth)
|
||||
builder = new_stream_read_constraints_builder
|
||||
builder.maxStringLength(max_string_len) if max_string_len
|
||||
builder.maxNumberLength(max_num_len) if max_num_len
|
||||
builder.maxNestingDepth(max_nesting_depth) if max_nesting_depth
|
||||
|
||||
StreamReadConstraints.overrideDefaultStreamReadConstraints(builder.build)
|
||||
end
|
||||
|
||||
def new_stream_read_constraints_builder
|
||||
StreamReadConstraints::builder
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -571,8 +571,8 @@ describe LogStash::Runner do
|
|||
subject { LogStash::Runner.new("") }
|
||||
let(:args) { ["-e", "input {} output {}"] }
|
||||
|
||||
it 'should be set' do
|
||||
expect(LogStash::Util::Jackson).to receive(:set_jackson_defaults)
|
||||
it 'should be verified' do
|
||||
expect(LogStash::Util::Jackson).to receive(:verify_jackson_overrides)
|
||||
subject.run(args)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,113 +0,0 @@
|
|||
# Licensed to Elasticsearch B.V. under one or more contributor
|
||||
# license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright
|
||||
# ownership. Elasticsearch B.V. licenses this file to you 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.
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe LogStash::Util::Jackson do
|
||||
it 'configures the read constraints defaults' do
|
||||
read_constraints_defaults = double('read_constraints_defaults')
|
||||
expect(read_constraints_defaults).to receive(:configure)
|
||||
|
||||
expect(LogStash::Util::Jackson::JacksonStreamReadConstraintsDefaults).to receive(:new).and_return(read_constraints_defaults)
|
||||
|
||||
LogStash::Util::Jackson.set_jackson_defaults(double('logger').as_null_object)
|
||||
end
|
||||
end
|
||||
|
||||
describe LogStash::Util::Jackson::JacksonStreamReadConstraintsDefaults do
|
||||
let(:logger) { double('logger') }
|
||||
|
||||
subject { described_class.new(logger) }
|
||||
|
||||
shared_examples 'stream read constraint property' do |property|
|
||||
let(:property) { property }
|
||||
let(:value) { nil }
|
||||
let(:builder) { double('builder') }
|
||||
let(:builder_set_value_method) { expected_builder_set_value_method(property) }
|
||||
|
||||
before(:each) do
|
||||
allow(logger).to receive(:info)
|
||||
|
||||
allow(builder).to receive(:build).and_return(com.fasterxml.jackson.core.StreamReadConstraints::builder.build)
|
||||
allow(builder).to receive(builder_set_value_method).with(value.to_i)
|
||||
|
||||
allow(subject).to receive(:new_stream_read_constraints_builder).and_return(builder)
|
||||
allow(subject).to receive(:get_property_value) do |name|
|
||||
if name == property
|
||||
value.to_s
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with valid number' do
|
||||
let(:value) { '10' }
|
||||
it 'does not raises an error and set value' do
|
||||
expect { subject.configure }.to_not raise_error
|
||||
expect(builder).to have_received(builder_set_value_method).with(value.to_i)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with non-number value' do
|
||||
let(:value) { 'foo' }
|
||||
it 'raises an error and does not set value' do
|
||||
expect { subject.configure }.to raise_error(LogStash::ConfigurationError, /System property '#{property}' must be a positive integer value. Received: #{value}/)
|
||||
expect(builder).to_not have_received(builder_set_value_method)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with zeroed value' do
|
||||
let(:value) { '0' }
|
||||
it 'raises an error and does not set value' do
|
||||
expect { subject.configure }.to raise_error(LogStash::ConfigurationError, /System property '#{property}' must be bigger than zero. Received: #{value}/)
|
||||
expect(builder).to_not have_received(builder_set_value_method)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with zeroed value' do
|
||||
let(:value) { '-1' }
|
||||
it 'raises an error and does not set value' do
|
||||
expect { subject.configure }.to raise_error(LogStash::ConfigurationError, /System property '#{property}' must be bigger than zero. Received: #{value}/)
|
||||
expect(builder).to_not have_received(builder_set_value_method)
|
||||
end
|
||||
end
|
||||
|
||||
def expected_builder_set_value_method(property)
|
||||
case property
|
||||
when LogStash::Util::Jackson::JacksonStreamReadConstraintsDefaults::PROPERTY_MAX_STRING_LENGTH
|
||||
return :maxStringLength
|
||||
when LogStash::Util::Jackson::JacksonStreamReadConstraintsDefaults::PROPERTY_MAX_NUMBER_LENGTH
|
||||
return :maxNumberLength
|
||||
when LogStash::Util::Jackson::JacksonStreamReadConstraintsDefaults::PROPERTY_MAX_NESTING_DEPTH
|
||||
return :maxNestingDepth
|
||||
else
|
||||
raise 'Invalid system property value'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
[
|
||||
LogStash::Util::Jackson::JacksonStreamReadConstraintsDefaults::PROPERTY_MAX_STRING_LENGTH,
|
||||
LogStash::Util::Jackson::JacksonStreamReadConstraintsDefaults::PROPERTY_MAX_NUMBER_LENGTH,
|
||||
LogStash::Util::Jackson::JacksonStreamReadConstraintsDefaults::PROPERTY_MAX_NESTING_DEPTH,
|
||||
].each { |system_property|
|
||||
context "#{system_property}" do
|
||||
it_behaves_like "stream read constraint property", system_property
|
||||
end
|
||||
}
|
||||
end
|
|
@ -42,6 +42,7 @@ import java.io.IOException;
|
|||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.apache.logging.log4j.core.jackson.Log4jJsonObjectMapper;
|
||||
import org.jruby.RubyBignum;
|
||||
import org.jruby.RubyBoolean;
|
||||
|
@ -52,12 +53,27 @@ import org.jruby.RubyString;
|
|||
import org.jruby.RubySymbol;
|
||||
import org.jruby.ext.bigdecimal.RubyBigDecimal;
|
||||
import org.logstash.ext.JrubyTimestampExtLibrary;
|
||||
import org.logstash.jackson.StreamReadConstraintsUtil;
|
||||
import org.logstash.log.RubyBasicObjectSerializer;
|
||||
|
||||
public final class ObjectMappers {
|
||||
|
||||
static final String RUBY_SERIALIZERS_MODULE_ID = "RubySerializers";
|
||||
|
||||
static final StreamReadConstraintsUtil CONFIGURED_STREAM_READ_CONSTRAINTS;
|
||||
|
||||
static {
|
||||
// The StreamReadConstraintsUtil needs to load the configured constraints from system
|
||||
// properties and apply them _statically_, before any object mappers are initialized.
|
||||
CONFIGURED_STREAM_READ_CONSTRAINTS = StreamReadConstraintsUtil.fromSystemProperties();
|
||||
CONFIGURED_STREAM_READ_CONSTRAINTS.applyAsGlobalDefault();
|
||||
}
|
||||
|
||||
public static StreamReadConstraintsUtil getConfiguredStreamReadConstraints() {
|
||||
return CONFIGURED_STREAM_READ_CONSTRAINTS;
|
||||
}
|
||||
|
||||
|
||||
private static final SimpleModule RUBY_SERIALIZERS =
|
||||
new SimpleModule(RUBY_SERIALIZERS_MODULE_ID)
|
||||
.addSerializer(RubyString.class, new RubyStringSerializer())
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
package org.logstash.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.StreamReadConstraints;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class StreamReadConstraintsUtil {
|
||||
|
||||
private final Map<String,String> propertyOverrides;
|
||||
private final Logger logger;
|
||||
|
||||
private StreamReadConstraints configuredStreamReadConstraints;
|
||||
|
||||
enum Override {
|
||||
MAX_STRING_LENGTH(StreamReadConstraints.Builder::maxStringLength, StreamReadConstraints::getMaxStringLength),
|
||||
MAX_NUMBER_LENGTH(StreamReadConstraints.Builder::maxNumberLength, StreamReadConstraints::getMaxNumberLength),
|
||||
MAX_NESTING_DEPTH(StreamReadConstraints.Builder::maxNestingDepth, StreamReadConstraints::getMaxNestingDepth),
|
||||
;
|
||||
|
||||
static final String PROP_PREFIX = "logstash.jackson.stream-read-constraints.";
|
||||
|
||||
final String propertyName;
|
||||
private final IntValueApplicator applicator;
|
||||
private final IntValueObserver observer;
|
||||
|
||||
Override(final IntValueApplicator applicator,
|
||||
final IntValueObserver observer) {
|
||||
this.propertyName = PROP_PREFIX + this.name().toLowerCase().replace('_', '-');
|
||||
this.applicator = applicator;
|
||||
this.observer = observer;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface IntValueObserver extends Function<StreamReadConstraints, Integer> {}
|
||||
|
||||
@FunctionalInterface
|
||||
interface IntValueApplicator extends BiFunction<StreamReadConstraints.Builder, Integer, StreamReadConstraints.Builder> {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an instance configured by {@code System.getProperties()}
|
||||
*/
|
||||
public static StreamReadConstraintsUtil fromSystemProperties() {
|
||||
return new StreamReadConstraintsUtil(System.getProperties());
|
||||
}
|
||||
|
||||
StreamReadConstraintsUtil(final Properties properties) {
|
||||
this(properties, null);
|
||||
}
|
||||
|
||||
StreamReadConstraintsUtil(final Properties properties,
|
||||
final Logger logger) {
|
||||
this(extractProperties(properties), logger);
|
||||
}
|
||||
|
||||
static private Map<String,String> extractProperties(final Properties properties) {
|
||||
return properties.stringPropertyNames().stream()
|
||||
.filter(propName -> propName.startsWith(Override.PROP_PREFIX))
|
||||
.map(propName -> Map.entry(propName, properties.getProperty(propName)))
|
||||
.filter(entry -> entry.getValue() != null)
|
||||
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
}
|
||||
|
||||
private StreamReadConstraintsUtil(final Map<String,String> propertyOverrides,
|
||||
final Logger logger) {
|
||||
this.propertyOverrides = Map.copyOf(propertyOverrides);
|
||||
this.logger = Objects.requireNonNullElseGet(logger, () -> LogManager.getLogger(StreamReadConstraintsUtil.class));
|
||||
}
|
||||
|
||||
StreamReadConstraints get() {
|
||||
if (configuredStreamReadConstraints == null) {
|
||||
final StreamReadConstraints.Builder builder = StreamReadConstraints.defaults().rebuild();
|
||||
|
||||
eachOverride((override, value) -> override.applicator.apply(builder, value));
|
||||
|
||||
this.configuredStreamReadConstraints = builder.build();
|
||||
}
|
||||
return configuredStreamReadConstraints;
|
||||
}
|
||||
|
||||
public void applyAsGlobalDefault() {
|
||||
StreamReadConstraints.overrideDefaultStreamReadConstraints(get());
|
||||
}
|
||||
|
||||
public void validateIsGlobalDefault() {
|
||||
validate(StreamReadConstraints.defaults());
|
||||
}
|
||||
|
||||
private void validate(final StreamReadConstraints streamReadConstraints) {
|
||||
final List<String> fatalIssues = new ArrayList<>();
|
||||
eachOverride((override, specifiedValue) -> {
|
||||
final Integer effectiveValue = override.observer.apply(streamReadConstraints);
|
||||
if (Objects.equals(specifiedValue, effectiveValue)) {
|
||||
logger.info("Jackson default value override `{}` configured to `{}`", override.propertyName, specifiedValue);
|
||||
} else {
|
||||
fatalIssues.add(String.format("`%s` (expected: `%s`, actual: `%s`)", override.propertyName, specifiedValue, effectiveValue));
|
||||
}
|
||||
});
|
||||
for (String unsupportedProperty : getUnsupportedProperties()) {
|
||||
logger.warn("Jackson default value override `{}` is unknown and has been ignored", unsupportedProperty);
|
||||
}
|
||||
if (!fatalIssues.isEmpty()) {
|
||||
throw new IllegalStateException(String.format("Jackson default values not applied: %s", String.join(",", fatalIssues)));
|
||||
}
|
||||
}
|
||||
|
||||
void eachOverride(BiConsumer<Override,Integer> overrideIntegerBiConsumer) {
|
||||
for (Override override : Override.values()) {
|
||||
final String propValue = this.propertyOverrides.get(override.propertyName);
|
||||
if (propValue != null) {
|
||||
try {
|
||||
int intValue = Integer.parseInt(propValue);
|
||||
overrideIntegerBiConsumer.accept(override, intValue);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException(String.format("System property `%s` must be positive integer value. Received: `%s`", override.propertyName, propValue), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> getUnsupportedProperties() {
|
||||
Set<String> supportedProps = Arrays.stream(Override.values()).map(p -> p.propertyName).collect(Collectors.toSet());
|
||||
Set<String> providedProps = this.propertyOverrides.keySet();
|
||||
|
||||
return Sets.difference(providedProps, supportedProps);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,213 @@
|
|||
package org.logstash.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.StreamReadConstraints;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.core.LogEvent;
|
||||
import org.apache.logging.log4j.junit.LoggerContextRule;
|
||||
import org.apache.logging.log4j.test.appender.ListAppender;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.function.BiConsumer;
|
||||
import org.apache.logging.log4j.Level;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.logstash.jackson.StreamReadConstraintsUtil.Override.*;
|
||||
|
||||
public class StreamReadConstraintsUtilTest {
|
||||
|
||||
@ClassRule
|
||||
public static final LoggerContextRule LOGGER_CONTEXT_RULE = new LoggerContextRule("log4j2-log-stream-read-constraints.xml");
|
||||
|
||||
private ListAppender listAppender;
|
||||
private Logger observedLogger;
|
||||
|
||||
@Before
|
||||
public void setUpLoggingListAppender() {
|
||||
int i = 1+16;
|
||||
this.observedLogger = LOGGER_CONTEXT_RULE.getLogger(StreamReadConstraintsUtil.class);
|
||||
this.listAppender = LOGGER_CONTEXT_RULE.getListAppender("EventListAppender").clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configuresDefaultsByDefault() {
|
||||
StreamReadConstraintsUtil.fromSystemProperties().validateIsGlobalDefault();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configuresMaxStringLength() {
|
||||
final Properties properties = new Properties();
|
||||
properties.setProperty(MAX_STRING_LENGTH.propertyName, "10101");
|
||||
|
||||
fromProperties(properties, (configuredUtil, defaults) -> {
|
||||
final StreamReadConstraints configuredConstraints = configuredUtil.get();
|
||||
|
||||
assertThat(configuredConstraints).returns(10101, from(StreamReadConstraints::getMaxStringLength));
|
||||
|
||||
assertThat(configuredConstraints).as("inherited defaults")
|
||||
.returns(defaults.getMaxDocumentLength(), from(StreamReadConstraints::getMaxDocumentLength))
|
||||
.returns(defaults.getMaxNameLength(), from(StreamReadConstraints::getMaxNameLength))
|
||||
.returns(defaults.getMaxNestingDepth(), from(StreamReadConstraints::getMaxNestingDepth))
|
||||
.returns(defaults.getMaxNumberLength(), from(StreamReadConstraints::getMaxNumberLength));
|
||||
|
||||
assertThatThrownBy(configuredUtil::validateIsGlobalDefault).isInstanceOf(IllegalStateException.class).hasMessageContaining(MAX_STRING_LENGTH.propertyName);
|
||||
|
||||
configuredUtil.applyAsGlobalDefault();
|
||||
assertThatCode(configuredUtil::validateIsGlobalDefault).doesNotThrowAnyException();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configuresMaxStringLengthInvalid() {
|
||||
final Properties properties = new Properties();
|
||||
properties.setProperty(MAX_STRING_LENGTH.propertyName, "NaN");
|
||||
|
||||
fromProperties(properties, (configuredUtil, defaults) -> assertThatThrownBy(configuredUtil::get)
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessageContaining(MAX_STRING_LENGTH.propertyName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configuresMaxStringLengthNegative() {
|
||||
final Properties properties = new Properties();
|
||||
properties.setProperty(MAX_STRING_LENGTH.propertyName, "-1000");
|
||||
|
||||
fromProperties(properties, (configuredUtil, defaults) -> assertThatThrownBy(configuredUtil::get)
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessageContaining(MAX_STRING_LENGTH.propertyName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configuresMaxNumberLength() {
|
||||
final Properties properties = new Properties();
|
||||
properties.setProperty(MAX_NUMBER_LENGTH.propertyName, "101");
|
||||
|
||||
fromProperties(properties, (configuredUtil, defaults) -> {
|
||||
final StreamReadConstraints configuredConstraints = configuredUtil.get();
|
||||
|
||||
assertThat(configuredConstraints).returns(101, from(StreamReadConstraints::getMaxNumberLength));
|
||||
|
||||
assertThat(configuredConstraints).as("inherited defaults")
|
||||
.returns(defaults.getMaxDocumentLength(), from(StreamReadConstraints::getMaxDocumentLength))
|
||||
.returns(defaults.getMaxNameLength(), from(StreamReadConstraints::getMaxNameLength))
|
||||
.returns(defaults.getMaxNestingDepth(), from(StreamReadConstraints::getMaxNestingDepth))
|
||||
.returns(defaults.getMaxStringLength(), from(StreamReadConstraints::getMaxStringLength));
|
||||
|
||||
assertThatThrownBy(configuredUtil::validateIsGlobalDefault).isInstanceOf(IllegalStateException.class).hasMessageContaining(MAX_NUMBER_LENGTH.propertyName);
|
||||
|
||||
configuredUtil.applyAsGlobalDefault();
|
||||
assertThatCode(configuredUtil::validateIsGlobalDefault).doesNotThrowAnyException();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configuresMaxNumberLengthInvalid() {
|
||||
final Properties properties = new Properties();
|
||||
properties.setProperty(MAX_NUMBER_LENGTH.propertyName, "NaN");
|
||||
|
||||
fromProperties(properties, (configuredUtil, defaults) -> assertThatThrownBy(configuredUtil::get)
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessageContaining(MAX_NUMBER_LENGTH.propertyName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configuresMaxNumberLengthNegative() {
|
||||
final Properties properties = new Properties();
|
||||
properties.setProperty(MAX_NUMBER_LENGTH.propertyName, "-1000");
|
||||
|
||||
fromProperties(properties, (configuredUtil, defaults) -> assertThatThrownBy(configuredUtil::get)
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessageContaining(MAX_NUMBER_LENGTH.propertyName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configuresMaxNestingDepth() {
|
||||
final Properties properties = new Properties();
|
||||
properties.setProperty(MAX_NESTING_DEPTH.propertyName, "101");
|
||||
|
||||
fromProperties(properties, (configuredUtil, defaults) -> {
|
||||
final StreamReadConstraints configuredConstraints = configuredUtil.get();
|
||||
|
||||
assertThat(configuredConstraints).returns(101, from(StreamReadConstraints::getMaxNestingDepth));
|
||||
|
||||
assertThat(configuredConstraints).as("inherited defaults")
|
||||
.returns(defaults.getMaxDocumentLength(), from(StreamReadConstraints::getMaxDocumentLength))
|
||||
.returns(defaults.getMaxNameLength(), from(StreamReadConstraints::getMaxNameLength))
|
||||
.returns(defaults.getMaxStringLength(), from(StreamReadConstraints::getMaxStringLength))
|
||||
.returns(defaults.getMaxNumberLength(), from(StreamReadConstraints::getMaxNumberLength));
|
||||
|
||||
assertThatThrownBy(configuredUtil::validateIsGlobalDefault).isInstanceOf(IllegalStateException.class).hasMessageContaining(MAX_NESTING_DEPTH.propertyName);
|
||||
|
||||
configuredUtil.applyAsGlobalDefault();
|
||||
assertThatCode(configuredUtil::validateIsGlobalDefault).doesNotThrowAnyException();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configuresMaxNestingDepthInvalid() {
|
||||
final Properties properties = new Properties();
|
||||
properties.setProperty(MAX_NESTING_DEPTH.propertyName, "NaN");
|
||||
|
||||
fromProperties(properties, (configuredUtil, defaults) -> assertThatThrownBy(configuredUtil::get)
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessageContaining(MAX_NESTING_DEPTH.propertyName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configuresMaxNestingDepthNegative() {
|
||||
final Properties properties = new Properties();
|
||||
properties.setProperty(MAX_NESTING_DEPTH.propertyName, "-1000");
|
||||
|
||||
fromProperties(properties, (configuredUtil, defaults) -> assertThatThrownBy(configuredUtil::get)
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessageContaining(MAX_NESTING_DEPTH.propertyName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validatesApplication() {
|
||||
final Properties properties = new Properties();
|
||||
properties.setProperty(PROP_PREFIX + "unsupported-option1", "100");
|
||||
properties.setProperty(PROP_PREFIX + "unsupported-option2", "100");
|
||||
properties.setProperty(MAX_NESTING_DEPTH.propertyName, "1010");
|
||||
properties.setProperty(MAX_STRING_LENGTH.propertyName, "1011");
|
||||
properties.setProperty(MAX_NUMBER_LENGTH.propertyName, "1110");
|
||||
|
||||
System.out.format("START%n");
|
||||
|
||||
fromProperties(properties, (configuredUtil, defaults) -> {
|
||||
configuredUtil.applyAsGlobalDefault();
|
||||
configuredUtil.validateIsGlobalDefault();
|
||||
});
|
||||
|
||||
System.out.format("OK%n");
|
||||
|
||||
assertLogObserved(Level.INFO, "override `" + MAX_NESTING_DEPTH.propertyName + "` configured to `1010`");
|
||||
assertLogObserved(Level.INFO, "override `" + MAX_STRING_LENGTH.propertyName + "` configured to `1011`");
|
||||
assertLogObserved(Level.INFO, "override `" + MAX_NUMBER_LENGTH.propertyName + "` configured to `1110`");
|
||||
|
||||
assertLogObserved(Level.WARN, "override `" + PROP_PREFIX + "unsupported-option1` is unknown and has been ignored");
|
||||
assertLogObserved(Level.WARN, "override `" + PROP_PREFIX + "unsupported-option1` is unknown and has been ignored");
|
||||
}
|
||||
|
||||
private void assertLogObserved(final Level level, final String... messageFragments) {
|
||||
List<LogEvent> logEvents = listAppender.getEvents();
|
||||
assertThat(logEvents)
|
||||
.withFailMessage("Expected %s to contain a %s-level log event containing %s", logEvents, level, Arrays.toString(messageFragments))
|
||||
.filteredOn(logEvent -> logEvent.getLevel().equals(level))
|
||||
.anySatisfy(logEvent -> assertThat(logEvent.getMessage().getFormattedMessage()).contains(messageFragments));
|
||||
}
|
||||
|
||||
private synchronized void fromProperties(final Properties properties, BiConsumer<StreamReadConstraintsUtil, StreamReadConstraints> defaultsConsumer) {
|
||||
final StreamReadConstraints defaults = StreamReadConstraints.defaults();
|
||||
try {
|
||||
final StreamReadConstraintsUtil util = new StreamReadConstraintsUtil(properties, this.observedLogger);
|
||||
defaultsConsumer.accept(util, defaults);
|
||||
} finally {
|
||||
StreamReadConstraints.overrideDefaultStreamReadConstraints(defaults);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -59,6 +59,7 @@ public class LogstashConfigurationFactoryTest {
|
|||
ThreadContext.putAll(dumpedLog4jThreadContext);
|
||||
snapshotHelper.restoreSnapshot("log4j.configurationFile", "ls.log.format", "ls.logs",
|
||||
LogstashConfigurationFactory.PIPELINE_SEPARATE_LOGS);
|
||||
forceLog4JContextRefresh();
|
||||
}
|
||||
|
||||
@Before
|
||||
|
@ -123,7 +124,7 @@ public class LogstashConfigurationFactoryTest {
|
|||
assertNull("No routing appender should be present", routingApp);
|
||||
}
|
||||
|
||||
private void forceLog4JContextRefresh() {
|
||||
private static void forceLog4JContextRefresh() {
|
||||
LoggerContext context = LoggerContext.getContext(false);
|
||||
context.reconfigure();
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ public class PluginDeprecationLoggerTest {
|
|||
public static void afterClass() {
|
||||
snapshotHelper.restoreSnapshot("log4j.configurationFile", "ls.log.format", "ls.logs",
|
||||
LogstashConfigurationFactory.PIPELINE_SEPARATE_LOGS);
|
||||
LogTestUtils.reloadLogConfiguration();
|
||||
}
|
||||
|
||||
@Before
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="ALL" name="LoggerTest">
|
||||
|
||||
<Appenders>
|
||||
<List name="EventListAppender" /><!-- No pattern layout enables whole event capture-->
|
||||
</Appenders>
|
||||
|
||||
<Loggers>
|
||||
<Root level="all">
|
||||
<AppenderRef ref="EventListAppender"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
|
||||
</Configuration>
|
Loading…
Add table
Add a link
Reference in a new issue