简体   繁体   English

当对象足够大时,如何告诉杰克逊序列化器停止继续序列化和写入

[英]how to tell jackson serializer to stop continuing to serialize and write when object is big enough

in my recent project, there are a lot of log statement like below:在我最近的项目中,有很多日志语句如下:

Bar bar = foo();
logger.info("something happened: param:{}", JSON.toJSONString(bar));

as you can see, just json serialize the object into log.如您所见,只需 json 将对象序列化为日志。 sometimes bar is very big, and serializing the bar object consumes too much time, and log file expands a lot.有时 bar 很大,序列化 bar 对象消耗太多时间,日志文件扩展很多。 the json serializing framework is fasterxml-jackson json 序列化框架是 fastxml-jackson

So my question is: is there a way ,like customizing JsonSerializer to implement:所以我的问题是:有没有办法,比如自定义 JsonSerializer 来实现:

  1. count the chars/bytes that has already been serialized(by invoking method #toJSONString())计算已经序列化的字符/字节(通过调用方法#toJSONString())
  2. if the count has exceed the MAX_SIZE, say 2000, then stop如果计数已超过 MAX_SIZE,例如 2000,则停止
  3. return just the 2000 char String and print it in log file仅返回 2000 个字符字符串并将其打印在日志文件中

If we do not want to fall into Jackson and how it works we can just limit Writer where objects are serialised into.如果我们不想陷入Jackson及其工作原理,我们可以限制Writer对象序列化到的位置。 It is simple approach which does not require to implement custom serialisers with limits checked inside.这是一种简单的方法,不需要在内部检查限制的情况下实现自定义序列化程序。 Downside of this approach is we do not skip serialisation process we just ignore it's result.这种方法的缺点是我们不会跳过序列化过程,我们只是忽略它的结果。

You can write your own Writer with a maximum buffer size.您可以使用最大缓冲区大小编写自己的Writer Simple implementation based on StringBuilder :基于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();
    }
}

Usage:用法:

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);
            }
        };
    }
}

Above code prints:上面的代码打印:

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

Notice, some methods in JSON class returns Supplier<String> .请注意, JSON类中的某些方法返回Supplier<String> You should use them if your logger allows to provide suppliers.如果您的记录器允许提供供应商,您应该使用它们。 It allows to postpone serialisation to the moment when it is really needed.它允许将序列化推迟到真正需要的时候。 And in case you disable INFO log in configuration it would really disable execution of this code.如果您禁用INFO登录配置,它将真正禁用此代码的执行。

after doing some research, the below thought comes up.在做了一些研究之后,下面的想法出现了。 this is not that elegant.这不是那么优雅。 wish someone could improve this!希望有人能改进这一点!

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);
        }
    }
}

output:输出:

[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...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM