Логирование бинарных данных

195
09 августа 2018, 01:40

Хочу прикрутить к log4j возможность логирования бинарных данных. По сути мне нужно принять byte[] и отдать его в appender.

Как-то так

byte[] data = {1, 2, 3};
log.debug(data);

Вопросы:

  1. Есть ли уже стандартный Layout для такой логики или нужно писать своего наследника от MessageLayout
  2. Если писать своего наследника, то как-бы его объявить, чтобы на него можно было ссылаться из конфигурационного XML файла?

Update

Как оказалось, одного Layout мало. Нужны еще манипуляции с интерфейсом Message

Обнаружилась такая проблема

при вызове

byte[] data = {1, 2, 3};
log.debug(data);

я попадаю в метод log класса LoggerConfig и там выполняется такая строчка

LogEvent logEvent = this.logEventFactory.createEvent(loggerName, marker, fqcn, level, data, (List)props, t);

где data объект класса ReusableObjectMessage у которого в поле obj лежит мой массив.

Далее идет вызов

public LogEvent createEvent(String loggerName, Marker marker, String fqcn, Level level, Message message, List<Property> properties, Throwable t) {
    MutableLogEvent result = (MutableLogEvent)mutableLogEventThreadLocal.get();
    ............
    result.setMessage(message);
    ............
}

А метод MutableLogEvent.setMessage реализован так

public void setMessage(Message msg) {
    if (msg instanceof ReusableMessage) {
        ReusableMessage reusable = (ReusableMessage)msg;
        reusable.formatTo(this.getMessageTextForWriting());
        this.messageFormat = msg.getFormat();
        if (this.parameters != null) {
            this.parameters = reusable.swapParameters(this.parameters);
            this.parameterCount = reusable.getParameterCount();
        }
    } else {
        this.message = InternalAsyncUtil.makeMessageImmutable(msg);
    }
}

На строке

reusable.formatTo(this.getMessageTextForWriting());

идет жесткое преобразование объекта в строку а попытка обменяться параметрами

this.parameters = reusable.swapParameters(this.parameters);

приводит к фикции

public Object[] swapParameters(final Object[] emptyReplacement) {
    return emptyReplacement;
}

хотя сами параметры у Message определены

public Object[] getParameters() {
    return new Object[] {obj};
}
Answer 1

Итого получилось:

  1. Реализуем свой Layout

    @Plugin(
        name = "BinaryLayout",
        category = "Core",
        elementType = "layout",
        printObject = true
    )
    public class BinaryLayout extends MessageLayout {
        @Override
        public byte[] toByteArray(LogEvent event) {
            Object[] params = event.getMessage().getParameters();
            int size = 0;
            int count = 0;
            byte[] res = null;
            for (Object obj : params) {
                if (obj instanceof byte[]) {
                    byte[] tmp = (byte[])obj;
                    if (tmp.length > 0) {
                        size += tmp.length;
                        count++;
                        res = tmp;
                    }
                }
            }
            if (count > 1) {
                res = new byte[size];
                size = 0;
                for (Object obj : params) {
                    if (obj instanceof byte[]) {
                        byte[] tmp = (byte[])obj;
                        if (tmp.length > 0) {
                            System.arraycopy(tmp, 0, res, size, tmp.length);
                            size += tmp.length;
                        }
                    }
                }
            }
            return res;
        }
        @PluginFactory
        public static Layout<?> createLayout() {
            return new BinaryLayout();
        }
    }
    • Аннотация @Plugin говорит, что упоминания в конфиге name = "BinaryLayout" это к нам. Категория для layout должна быть Core, а иначе наш layout не находится, elementType и так ясно, а printObject говорит, что метод toString у нас не переопределен и печатать наш объект бессмысленно
    • Аннотация @PluginFactory определяет фабричный метод, который создаст наш layout
  2. Реализуем интерфейс Message При записи в лог используем объект класса ObjectMessage. Т.к. этот класс не является наследником ReusableMessage, то при создании объекта LogEvent не теряются наши данные

  3. Объявляем в конфиге appender с нашим layout

    <Appenders>
      <File name="test" filename="log/test.log" append="false">
        <BinaryLayout/>
      </File>
    </Appenders>
  4. И, собственно, само использование

    Logger log = LogManager.getLogger("test");
    byte[] data = {1, 2, 3, 4};
    log.debug(new ObjectMessage(data));
READ ALSO
Ошибка при запуске сервера

Ошибка при запуске сервера

17:29:10,933 ERROR [orgjboss

200
Java, помогите с security.policy

Java, помогите с security.policy

Мне нужно сделать, чтобы программа могла занимать только один порт, это я сделал с помощью флагов: -Djavasecurity

173
Поэлементное переключение recyclerView

Поэлементное переключение recyclerView

Синий - recyclerViewОранжевый - элементы recyclerView

143
Поддержка браузеров (движки) javascript

Поддержка браузеров (движки) javascript

На занятиях по JS поручили следующее задание:

162