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.json; 020 021import com.fasterxml.jackson.annotation.JsonInclude; 022import com.fasterxml.jackson.core.JsonProcessingException; 023import com.fasterxml.jackson.databind.ObjectMapper; 024import com.fasterxml.jackson.databind.module.SimpleModule; 025import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 026import org.jpos.log.AuditLogEvent; 027import org.jpos.log.AuditLogEventRegistry; 028 029import org.jpos.log.LogRenderer; 030import org.jpos.log.evt.LogEvt; 031import org.jpos.log.evt.LogMessage; 032import org.jpos.log.evt.ThrowableAuditLogEvent; 033import org.jpos.log.render.ThrowableSerializer; 034import org.jpos.util.LogEvent; 035import org.jpos.util.Loggeable; 036 037import java.io.ByteArrayOutputStream; 038import java.io.PrintStream; 039import java.time.Duration; 040import java.util.LinkedHashMap; 041import java.util.List; 042import java.util.Map; 043 044/** JSON renderer for {@link LogEvent}, serialising tag/realm/payload via Jackson. */ 045public final class LogEventJsonLogRenderer implements LogRenderer<LogEvent> { 046 private final ObjectMapper mapper = new ObjectMapper(); 047 048 /** Default constructor. */ 049 public LogEventJsonLogRenderer() { 050 mapper.registerModule(new JavaTimeModule()); 051 mapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); 052 // mapper.enable(SerializationFeature.INDENT_OUTPUT); 053 mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); 054 SimpleModule module = new SimpleModule(); 055 module.addSerializer(Throwable.class, new ThrowableSerializer()); 056 mapper.registerModule(module); 057 AuditLogEventRegistry.register(mapper); 058 } 059 060 @Override 061 public void render(LogEvent evt, PrintStream ps, String indent) { 062 List<AuditLogEvent> events = evt.getPayLoad() 063 .stream() 064 .map (obj -> switch (obj) { 065 case AuditLogEvent ale -> ale; 066 case Throwable t -> new ThrowableAuditLogEvent(t); 067 default -> new LogMessage(dump(obj)); 068 }).toList(); 069 070 long elapsed = Duration.between(evt.getCreatedAt(), evt.getDumpedAt()).toMillis(); 071 LogEvt ev = new LogEvt ( 072 evt.getDumpedAt(), 073 evt.getTag(), 074 elapsed == 0L ? null : elapsed, 075 buildTags(evt), 076 events 077 ); 078 try { 079 ps.println (mapper.writeValueAsString(ev)); 080 } catch (JsonProcessingException e) { 081 throw new RuntimeException (e); 082 } 083 } 084 public Class<?> clazz() { 085 return LogEvent.class; 086 } 087 public Type type() { 088 return Type.JSON; 089 } 090 091 private Map<String,String> buildTags(LogEvent evt) { 092 evt.getTraceId(); // ensure trace-id is generated 093 Map<String,String> tags = new LinkedHashMap<>(); 094 String realm = evt.getRealm(); 095 if (realm != null && !realm.isEmpty()) 096 tags.put("realm", realm); 097 tags.putAll(evt.getTags()); 098 return tags; 099 } 100 101 private String dump (Object obj) { 102 if (obj instanceof Loggeable loggeable) { 103 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 104 PrintStream ps = new PrintStream(baos); 105 loggeable.dump(ps, ""); 106 return baos.toString(); 107 } 108 return obj.getClass() + ":" + obj; 109 } 110}