001/* 002 * jPOS Project [http://jpos.org] 003 * Copyright (C) 2000-2026 jPOS Software SRL 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package org.jpos.log.render.xml; 020 021import com.fasterxml.jackson.core.JsonProcessingException; 022import com.fasterxml.jackson.databind.SerializationFeature; 023import com.fasterxml.jackson.databind.module.SimpleModule; 024import com.fasterxml.jackson.dataformat.xml.XmlMapper; 025import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 026import org.jpos.log.AuditLogEvent; 027 028import org.jpos.log.LogRenderer; 029import org.jpos.log.evt.LogEvt; 030import org.jpos.log.evt.LogMessage; 031import org.jpos.log.evt.ThrowableAuditLogEvent; 032import org.jpos.log.render.ThrowableSerializer; 033import org.jpos.util.LogEvent; 034import org.jpos.util.Loggeable; 035 036import java.io.ByteArrayOutputStream; 037import java.io.PrintStream; 038import java.time.Duration; 039import java.util.List; 040 041public final class LogEventXmlLogRenderer implements LogRenderer<LogEvent> { 042 private final XmlMapper mapper = new XmlMapper(); 043 044 public LogEventXmlLogRenderer() { 045 mapper.registerModule(new JavaTimeModule()); 046 mapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); 047 048 mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); 049 mapper.enable(SerializationFeature.INDENT_OUTPUT); 050 051 SimpleModule module = new SimpleModule(); 052 module.addSerializer(Throwable.class, new ThrowableSerializer()); 053 mapper.registerModule(module); 054 } 055 056 @Override 057 public void render(LogEvent evt, PrintStream ps, String indent) { 058 List<AuditLogEvent> events = evt.getPayLoad() 059 .stream() 060 .map (obj -> switch (obj) { 061 case AuditLogEvent ale -> ale; 062 case Throwable t -> new ThrowableAuditLogEvent(t); 063 default -> new LogMessage(dump(obj)); 064 }).toList(); 065 long elapsed = Duration.between(evt.getCreatedAt(), evt.getDumpedAt()).toMillis(); 066 LogEvt ev = new LogEvt ( 067 evt.getDumpedAt(), 068 evt.getTraceId(), 069 evt.getRealm(), 070 evt.getTag(), 071 elapsed == 0L ? null : elapsed, 072 events 073 ); 074 try { 075 ps.println (mapper.writeValueAsString(ev)); 076 } catch (JsonProcessingException e) { 077 ps.print (kv("exception", e.toString())); 078 } 079 } 080 public Class<?> clazz() { 081 return LogEvent.class; 082 } 083 public Type type() { 084 return Type.XML; 085 } 086 087 private String kv (String k, String v) { 088 return "{\"%s\":\"%s\"}".formatted(k,v); 089 } 090 091 private String dump (Object obj) { 092 if (obj instanceof Loggeable loggeable) { 093 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 094 PrintStream ps = new PrintStream(baos); 095 loggeable.dump(ps, ""); 096 return baos.toString(); 097 } 098 return obj.toString(); 099 } 100}