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;
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 LogEventJsonLogRenderer implements LogRenderer<LogEvent> {
042    private final ObjectMapper mapper = new ObjectMapper();
043
044    public LogEventJsonLogRenderer() {
045        mapper.registerModule(new JavaTimeModule());
046        mapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
047        // mapper.enable(SerializationFeature.INDENT_OUTPUT);
048        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
049        SimpleModule module = new SimpleModule();
050        module.addSerializer(Throwable.class, new ThrowableSerializer());
051        mapper.registerModule(module);
052    }
053
054    @Override
055    public void render(LogEvent evt, PrintStream ps, String indent) {
056        List<AuditLogEvent> events = evt.getPayLoad()
057          .stream()
058          .map (obj -> switch (obj) {
059              case AuditLogEvent ale -> ale;
060              case Throwable t -> new ThrowableAuditLogEvent(t);
061              default -> new LogMessage(dump(obj));
062          }).toList();
063
064        long elapsed = Duration.between(evt.getCreatedAt(), evt.getDumpedAt()).toMillis();
065        LogEvt ev = new LogEvt (
066          evt.getDumpedAt(),
067          evt.getTraceId(),
068          evt.getRealm(),
069          evt.getTag(),
070          elapsed == 0L ? null : elapsed,
071          events
072        );
073        try {
074             ps.println (mapper.writeValueAsString(ev));
075        } catch (JsonProcessingException e) {
076            throw new RuntimeException (e);
077        }
078    }
079    public Class<?> clazz() {
080        return LogEvent.class;
081    }
082    public Type type() {
083        return Type.JSON;
084    }
085
086    private String dump (Object obj) {
087        if (obj instanceof Loggeable loggeable) {
088            ByteArrayOutputStream baos = new ByteArrayOutputStream();
089            PrintStream ps = new PrintStream(baos);
090            loggeable.dump(ps, "");
091            return baos.toString();
092        }
093        return obj.getClass() + ":" + obj;
094    }
095}