Skip to content

Commit

Permalink
Merge pull request #930 from logfellow/fix_non_json_generators
Browse files Browse the repository at this point in the history
Fix non-JSON generators
  • Loading branch information
philsttr authored Feb 6, 2023
2 parents 489498b + 20779d6 commit c9ab293
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ public abstract class AbstractCompositeJsonFormatter<Event extends DeferredProce
private volatile boolean started;

private ThreadLocalHolder<JsonFormatter> threadLocalJsonFormatter;


public AbstractCompositeJsonFormatter(ContextAware declaredOrigin) {
super(declaredOrigin);
}
Expand Down Expand Up @@ -254,7 +254,25 @@ private JsonFactory createJsonFactory() {
}

private JsonFactory decorateFactory(JsonFactory factory) {
return this.jsonFactoryDecorator.decorate(factory)
JsonFactory factoryToDecorate = factory
/*
* When generators are flushed, don't flush the underlying outputStream.
*
* This allows some streaming optimizations when using an encoder.
*
* The encoder generally determines when the stream should be flushed
* by an 'immediateFlush' property.
*
* The 'immediateFlush' property of the encoder can be set to false
* when the appender performs the flushes at appropriate times
* (such as the end of a batch in the AbstractLogstashTcpSocketAppender).
*
* Set this prior to decorating, because some generators require
* FLUSH_PASSED_TO_STREAM to work properly (e.g. YAML)
*/
.disable(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM);

return this.jsonFactoryDecorator.decorate(factoryToDecorate)
/*
* Jackson buffer recycling works by maintaining a pool of buffers per thread. This
* feature works best when one JsonGenerator is created per thread, typically in J2EE
Expand Down Expand Up @@ -284,32 +302,28 @@ private JsonGenerator createGenerator(OutputStream outputStream) throws IOExcept
}

private JsonGenerator decorateGenerator(JsonGenerator generator) {
return new SimpleObjectJsonGeneratorDelegate(jsonGeneratorDecorator.decorate(generator)
/*
* When generators are flushed, don't flush the underlying outputStream.
*
* This allows some streaming optimizations when using an encoder.
*
* The encoder generally determines when the stream should be flushed
* by an 'immediateFlush' property.
*
* The 'immediateFlush' property of the encoder can be set to false
* when the appender performs the flushes at appropriate times
* (such as the end of a batch in the AbstractLogstashTcpSocketAppender).
*/
.disable(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM)

/*
* Don't let the json generator close the underlying outputStream and let the
* encoder managed it.
*/
.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET)

/*
* JsonGenerator are reused to serialize multiple log events.
* Change the default root value separator to an empty string instead of a single space.
*/
.setRootValueSeparator(new SerializedString(CoreConstants.EMPTY_STRING)));
JsonGenerator decorated = jsonGeneratorDecorator.decorate(generator)
/*
* Don't let the json generator close the underlying outputStream and let the
* encoder managed it.
*/
.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);

try {
decorated = decorated
/*
* JsonGenerator are reused to serialize multiple log events.
* Change the default root value separator to an empty string instead of a single space.
*/
.setRootValueSeparator(new SerializedString(CoreConstants.EMPTY_STRING));
} catch (UnsupportedOperationException e) {
/*
* Ignore.
* Some generators do not support setting the rootValueSeparator.
*/
}

return new SimpleObjectJsonGeneratorDelegate(decorated);
}

public JsonFactory getJsonFactory() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import net.logstash.logback.decorate.JsonFactoryDecorator;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
Expand All @@ -35,6 +36,12 @@ public JsonFactory decorate(JsonFactory factory) {
YAMLFactory yamlFactory = new YAMLFactory();
ObjectMapper mapper = new ObjectMapper(yamlFactory);
yamlFactory.setCodec(mapper);
/*
* YAMLGenerator needs to pass the flush to the stream.
* It doesn't maintain an internal buffer like the other generators.
* To see this, look at the .flush() implementations of each of the generator classes.
*/
yamlFactory.enable(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM);
return yamlFactory;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 2013-2022 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
*
* 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.
*/
package net.logstash.logback.decorate.cbor;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import net.logstash.logback.composite.loggingevent.LoggingEventCompositeJsonFormatter;
import net.logstash.logback.composite.loggingevent.LoggingEventJsonProviders;
import net.logstash.logback.composite.loggingevent.MessageJsonProvider;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.spi.ContextAware;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
class CborJsonFactoryDecoratorTest {

@Mock
private ContextAware contextAware;

@Mock
private ILoggingEvent event;

@Test
void test() throws IOException {
CborJsonFactoryDecorator decorator = new CborJsonFactoryDecorator();

LoggingEventJsonProviders providers = new LoggingEventJsonProviders();
providers.addMessage(new MessageJsonProvider());

LoggingEventCompositeJsonFormatter formatter = new LoggingEventCompositeJsonFormatter(contextAware);
formatter.setProviders(providers);
formatter.setJsonFactoryDecorator(decorator);
formatter.start();

when(event.getFormattedMessage()).thenReturn("a message");

ByteArrayOutputStream baos = new ByteArrayOutputStream();
formatter.writeEvent(event, baos);

assertThat(baos.toByteArray()).asBase64Encoded().isEqualTo("v2dtZXNzYWdlaWEgbWVzc2FnZf8=");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 2013-2022 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
*
* 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.
*/
package net.logstash.logback.decorate.smile;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import net.logstash.logback.composite.loggingevent.LoggingEventCompositeJsonFormatter;
import net.logstash.logback.composite.loggingevent.LoggingEventJsonProviders;
import net.logstash.logback.composite.loggingevent.MessageJsonProvider;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.spi.ContextAware;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
class SmileJsonFactoryDecoratorTest {

@Mock
private ContextAware contextAware;

@Mock
private ILoggingEvent event;

@Test
void test() throws IOException {
SmileJsonFactoryDecorator decorator = new SmileJsonFactoryDecorator();

LoggingEventJsonProviders providers = new LoggingEventJsonProviders();
providers.addMessage(new MessageJsonProvider());

LoggingEventCompositeJsonFormatter formatter = new LoggingEventCompositeJsonFormatter(contextAware);
formatter.setProviders(providers);
formatter.setJsonFactoryDecorator(decorator);
formatter.start();

when(event.getFormattedMessage()).thenReturn("a message");

ByteArrayOutputStream baos = new ByteArrayOutputStream();
formatter.writeEvent(event, baos);

assertThat(baos.toByteArray()).asBase64Encoded().isEqualTo("OikKAfqGbWVzc2FnZUhhIG1lc3NhZ2X7");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2013-2022 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
*
* 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.
*/
package net.logstash.logback.decorate.yaml;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

import net.logstash.logback.composite.loggingevent.LoggingEventCompositeJsonFormatter;
import net.logstash.logback.composite.loggingevent.LoggingEventJsonProviders;
import net.logstash.logback.composite.loggingevent.MessageJsonProvider;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.spi.ContextAware;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
class YamlJsonFactoryDecoratorTest {

@Mock
private ContextAware contextAware;

@Mock
private ILoggingEvent event;

@Test
void test() throws IOException {
YamlJsonFactoryDecorator decorator = new YamlJsonFactoryDecorator();

LoggingEventJsonProviders providers = new LoggingEventJsonProviders();
providers.addMessage(new MessageJsonProvider());

LoggingEventCompositeJsonFormatter formatter = new LoggingEventCompositeJsonFormatter(contextAware);
formatter.setProviders(providers);
formatter.setJsonFactoryDecorator(decorator);
formatter.start();

when(event.getFormattedMessage()).thenReturn("a message");

ByteArrayOutputStream baos = new ByteArrayOutputStream();
formatter.writeEvent(event, baos);

assertThat(new String(baos.toByteArray(), StandardCharsets.UTF_8)).isEqualTo("---\nmessage: \"a message\"");
}

}

0 comments on commit c9ab293

Please sign in to comment.