Передача произвольного объекта в ThreadContext.put() в Log4j

#java #log4j #mdc

#java #log4j #mdc

Вопрос:

Я хочу передать произвольный объект в ThreadContext.put() метод следующим образом:

 MyObject originalMessage = new MyObject(...);
ThreadContext.put("originalMessage", originalMessage);
  

Но этот метод допускает только String в качестве своего второго аргумента: docs here .

Есть ли способ поместить произвольный объект в log4j в MDC Log4j? Или альтернатива этому? Я хочу разрешить дальнейшие выполнения log.error() для автоматической регистрации originalMessage без необходимости передачи его в качестве параметра везде.

Кроме того, я не могу вызвать toString() себя, поскольку я бы с нетерпением оценивал этот метод (который довольно тяжелый в этом объекте), поэтому я хочу, чтобы его вызов был отложен до тех пор, пока он не понадобится, точно так же, как при передаче произвольного объекта log.debug("{}", someObject) .

Комментарии:

1. Вы можете выполнить поиск объекта, например, в формате json или создать Map<String,Object> сохранить объект там и поместить ключ в ThreadContext .

2. Не могли бы вы объяснить подробнее? put Метод в ThreadContext принимает только строки, поэтому, если я использую JSONObject или Map, мне все равно нужно преобразовать его в строку, прежде чем я смогу поместить его в ThreadContext. Документы здесь: logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache /…

3. Вы можете преобразовать некоторые объекты в строку с помощью json и отменить этот процесс.

4. Основная проблема заключается в том, что преобразование этого объекта в string слишком тяжелое, будь то с помощью toString или если я сначала преобразовываю его в JSONObject, а затем вызываю в нем toString . В моем случае размер этого объекта составляет несколько МБ, и этот метод выполняется много раз.

Ответ №1:

Я, наконец, нашел (взломанное) решение здесь и здесь .

Вместо этого (который не компилируется):

 ThreadContext.put("originalMessage", originalMessage);
  

Я могу сделать:

 final var threadContextMap = (ObjectThreadContextMap) ThreadContext.getThreadContextMap();
threadContextMap.putValue("originalMessage", originalMessage);
  

Это работает до тех пор, пока вы добавляете эту опцию JVM:

 -Dlog4j2.threadContextMap=org.apache.logging.log4j.spi.CopyOnWriteSortedArrayThreadContextMap
  

Теперь это выглядит хакерски, потому что метод getThreadContextMap() должен возвращать представление внутренней карты только для чтения:

Возвращает доступную только для чтения внутреннюю структуру данных, используемую для хранения пар ключ-значение контекста потока …

Итак, это решение использует эту карту только для чтения и, зная, что реализация не доступна только для чтения, изменяет ее… не самое прозрачное решение, но оно работает.