#java #logging #serialization #jackson #jackson-databind
#java #ведение журнала #сериализация #джексон #jackson-databind
Вопрос:
в моем недавнем проекте есть много инструкций журнала, как показано ниже:
Bar bar = foo();
logger.info("something happened: param:{}", JSON.toJSONString(bar));
как вы можете видеть, просто json сериализует объект в журнал. иногда bar очень большой, и сериализация объекта bar занимает слишком много времени, а файл журнала сильно расширяется.
платформа сериализации json — это fasterxml-jackson
Итак, мой вопрос: есть ли способ, например, настроить JsonSerializer для реализации:
- подсчитайте символы / байты, которые уже были сериализованы (путем вызова метода #toJSONString())
- если количество превысило значение MAX_SIZE, скажем, 2000, затем остановитесь
- верните только строку 2000 символов и распечатайте ее в файле журнала
Ответ №1:
Если мы не хотим попадать в Jackson
и как это работает, мы можем просто ограничить Writer
, куда сериализуются объекты. Это простой подход, который не требует реализации пользовательских сериализаторов с проверенными внутри ограничениями. Недостатком этого подхода является то, что мы не пропускаем процесс сериализации, мы просто игнорируем его результат.
Вы можете написать свой собственный Writer
с максимальным размером буфера. Простая реализация, основанная на StringBuilder
:
class LimitedStringBuilderWriter extends Writer {
private final StringBuilder buffer;
private int remaining;
public LimitedStringBuilderWriter(int limit) {
if (limit <= 0) {
throw new IllegalArgumentException("Limit must be positive number!");
}
this.remaining = limit;
this.buffer = new StringBuilder(limit);
}
@Override
public void write(char[] cbuf, int off, int len) {
if (len == 0 || this.remaining <= 0) {
return;
}
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off len) > cbuf.length) || ((off len) < 0)) {
throw new IndexOutOfBoundsException();
}
final int size = len - off;
if (this.remaining >= size) {
this.remaining -= size;
buffer.append(cbuf, off, len);
return;
}
buffer.append(cbuf, off, this.remaining);
this.remaining = 0;
}
@Override
public void write(int c) {
if (this.remaining <= 0) {
return;
}
this.remaining--;
this.buffer.append(c);
}
@Override
public void flush() {
}
@Override
public void close() {
}
@Override
public String toString() {
return this.buffer.toString();
}
}
Использование:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Supplier;
public class LimitJsonLogApp {
public static void main(String[] args) {
Map<String, Object> value = new LinkedHashMap<>();
value.put("array", Arrays.asList(1, 2, 3));
value.put("string", "Value");
value.put("int", 23);
for (int limit = 11; limit < 30; limit = 3) {
System.out.println(limit " => " JSON.toJSONString(value, limit));
}
}
}
class JSON {
private static final ObjectMapper mapper = JsonMapper.builder().build();
public static String toJSONString(Object value) {
return toJSONStringSupplier(value).get();
}
public static String toJSONString(Object value, int limit) {
return toJSONStringSupplier(value, limit).get();
}
public static Supplier<String> toJSONStringSupplier(Object value) {
return toJSONStringSupplier(value, 1000);
}
public static Supplier<String> toJSONStringSupplier(Object value, int limit) {
if (value == null) {
return () -> "null";
}
return () -> {
try {
LimitedStringBuilderWriter writer = new LimitedStringBuilderWriter(limit);
mapper.writeValue(writer, value);
return writer.toString();
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
};
}
}
Приведенный выше код печатает:
11 => {"array":[1
14 => {"array":[1,2,
17 => {"array":[1,2,3],
20 => {"array":[1,2,3],"st
23 => {"array":[1,2,3],"strin
26 => {"array":[1,2,3],"string":
29 => {"array":[1,2,3],"string":"Va
Обратите внимание, некоторые методы в JSON
возвратах класса Supplier<String>
. Вы должны использовать их, если ваш регистратор позволяет предоставлять поставщиков. Это позволяет отложить сериализацию до момента, когда это действительно необходимо. И в случае, если вы отключите INFO
вход в конфигурацию, это действительно отключит выполнение этого кода.
Комментарии:
1. да, это очень кратко. И здесь я тоже публикую не такое элегантное решение.
Ответ №2:
после проведения некоторых исследований возникает следующая мысль. это не так элегантно. жаль, что кто-то не может это улучшить!
import java.io.IOException;
import java.io.Writer;
import java.util.stream.IntStream;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.json.WriterBasedJsonGenerator;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.MappingJsonFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.databind.ser.SerializerFactory;
import com.fasterxml.jackson.databind.ser.Serializers;
import org.jetbrains.annotations.NotNull;
public class JSON {
static int MAX_SIZE = 10;
final static ObjectMapper OBJECT_MAPPER;
static {
OBJECT_MAPPER = new ObjectMapper(new WrappedJsonFactory(new MappingJsonFactory()), null, null);
OBJECT_MAPPER.setSerializerFactory(new WrapperSerialFactory(OBJECT_MAPPER.getSerializerFactory()));
}
static String toJSONString(Object value) {
try {
return OBJECT_MAPPER.writeValueAsString(value);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
public static void main(String... args) throws Exception {
int[] array = IntStream.range(1, 100).toArray();
System.out.println(JSON.toJSONString(array));
MAX_SIZE = 20;
System.out.println(JSON.toJSONString(array));
MAX_SIZE = 30;
System.out.println(JSON.toJSONString(array));
}
static class WrapperSerialFactory extends SerializerFactory {
private SerializerFactory wrapped;
public WrapperSerialFactory(SerializerFactory wrapped) {
this.wrapped = wrapped;
}
@Override
public SerializerFactory withAdditionalSerializers(Serializers additional) {
return wrapped.withAdditionalSerializers(additional);
}
@Override
public SerializerFactory withAdditionalKeySerializers(Serializers additional) {
return wrapped.withAdditionalKeySerializers(additional);
}
@Override
public SerializerFactory withSerializerModifier(BeanSerializerModifier modifier) {
return wrapped.withSerializerModifier(modifier);
}
@Override
public JsonSerializer<Object> createSerializer(SerializerProvider prov, JavaType baseType)
throws JsonMappingException {
JsonSerializer<Object> serializer = wrapped.createSerializer(prov, baseType);
return new WrappedTypeSerializer(serializer);
}
@Override
public TypeSerializer createTypeSerializer(SerializationConfig config, JavaType baseType)
throws JsonMappingException {
return wrapped.createTypeSerializer(config, baseType);
}
@Override
public JsonSerializer<Object> createKeySerializer(SerializationConfig config, JavaType type,
JsonSerializer<Object> defaultImpl) throws JsonMappingException {
return wrapped.createKeySerializer(config, type, defaultImpl);
}
}
static class WrappedJsonFactory extends JsonFactory {
private JsonFactory wrapped;
public WrappedJsonFactory(JsonFactory wrapped) {
this.wrapped = wrapped;
}
@Override
public JsonGenerator createGenerator(Writer w) throws IOException {
w = new LimitingWriter(w);
return wrapped.createGenerator(w);
}
}
static class LimitingWriter extends Writer {
private int count;
private boolean finished = false;
private Writer delegate;
public LimitingWriter(Writer delegate) {
this.delegate = delegate;
}
private void finish() throws IOException {
if (!finished) {
delegate.write("...");
finished = true;
}
}
@Override
public void write(int c) throws IOException {
if (!finished) {
if (count >= MAX_SIZE) {
finish();
} else {
delegate.write(c);
count;
}
}
}
@Override
public void write(@NotNull char[] cbuf) throws IOException {
if (!finished) {
if ((count cbuf.length) > MAX_SIZE) {
delegate.write(cbuf, 0, MAX_SIZE - count);
count = MAX_SIZE;
finish();
} else {
delegate.write(cbuf);
count = cbuf.length;
}
}
}
@Override
public void write(@NotNull String str) throws IOException {
if (!finished) {
if (count str.length() > MAX_SIZE) {
delegate.write(str, 0, MAX_SIZE - count);
count = MAX_SIZE;
finish();
} else {
delegate.write(str);
count = str.length();
}
}
}
@Override
public void write(@NotNull String str, int off, int len) throws IOException {
if (!finished) {
if (count len > MAX_SIZE) {
delegate.write(str, 0, MAX_SIZE - count);
count = MAX_SIZE;
finish();
} else {
delegate.write(str, off, len);
count = len;
}
}
}
@Override
public Writer append(CharSequence csq) throws IOException {
if (!finished) {
if (count csq.length() > MAX_SIZE) {
count = MAX_SIZE;
delegate.append(csq, 0, MAX_SIZE - count);
finish();
} else {
count = csq.length();
delegate.append(csq);
}
}
return this;
}
@Override
public Writer append(CharSequence csq, int start, int end) throws IOException {
if (!finished) {
if (count end - start > MAX_SIZE) {
delegate.append(csq, start, start MAX_SIZE - count);
count = MAX_SIZE;
finish();
} else {
count = (end - start);
delegate.append(csq, start, end);
}
}
return this;
}
@Override
public Writer append(char c) throws IOException {
if (!finished) {
if (count >= MAX_SIZE) {
finish();
} else {
count;
delegate.append(c);
}
}
return this;
}
@Override
public void write(@NotNull char[] cbuf, int off, int len) throws IOException {
if (!finished) {
if (count len > MAX_SIZE) {
delegate.write(cbuf, off, MAX_SIZE - count);
count = MAX_SIZE;
finish();
} else {
delegate.write(cbuf, off, len);
count = len;
}
}
}
@Override
public void flush() throws IOException {
delegate.flush();
}
@Override
public void close() throws IOException {
delegate.close();
}
}
static class WrappedTypeSerializer extends JsonSerializer {
private JsonSerializer wrapped;
public WrappedTypeSerializer(JsonSerializer wrapped) {
this.wrapped = wrapped;
}
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
WriterBasedJsonGenerator writerBasedJsonGenerator = (WriterBasedJsonGenerator)gen;
LimitingWriter writer = (LimitingWriter)writerBasedJsonGenerator.getOutputTarget();
if (writer.finished) {
return;
}
wrapped.serialize(value, gen, serializers);
}
}
}
вывод:
[1,2,3,4,5...
[1,2,3,4,5,6,7,8,9,1...
[1,2,3,4,5,6,7,8,9,10,11,12,13...