All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cdc.perfs.io.PerfsXml Maven / Gradle / Ivy

There is a newer version: 0.52.0
Show newest version
package cdc.perfs.io;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import cdc.perfs.Context;
import cdc.perfs.Environment;
import cdc.perfs.Measure;
import cdc.perfs.MeasureStatus;
import cdc.perfs.api.MeasureLevel;
import cdc.perfs.api.RuntimeProbe;
import cdc.perfs.api.Source;
import cdc.perfs.runtime.RuntimeEnvironment;
import cdc.perfs.snapshot.SnapshotContext;
import cdc.perfs.snapshot.SnapshotEnvironment;
import cdc.perfs.snapshot.SnapshotMeasure;
import cdc.util.compress.Compressor;
import cdc.util.data.Element;
import cdc.util.data.util.AbstractResourceLoader;
import cdc.util.lang.Checks;
import cdc.util.lang.FailureReaction;
import cdc.util.lang.IntMasks;
import cdc.util.xml.XmlWriter;

/**
 * Environment XML serialization / deserialization.
 *
 * @author Damien Carbonne
 *
 */
public final class PerfsXml {
    private static final char SEPARATOR = ':';

    private static final String ALIVE = "alive";
    private static final String BEGIN = "begin";
    private static final String CONTEXT = "context";
    private static final String COMPACT_CODE = "c";
    // private static final String COMPACT_DETAILS = "d";
    // private static final String COMPACT_BEGIN = "b";
    // private static final String COMPACT_END = "e";
    private static final String COMPACT_MEASURE = "m";
    // private static final String COMPACT_SOURCE = "s";

    private static final String DETAILS = "details";
    private static final String ELAPSED_NANOS = "elapsed-nanos";
    private static final String END = "end";
    private static final String FORMAT = "format";
    private static final String ID = "id";
    private static final String KIND = "kind";
    private static final String LEVEL = "level";
    private static final String MAX_LEVEL = "max-level";
    private static final String MEASURE = "measure";
    private static final String NAME = "name";
    private static final String PERFS = "perfs";
    private static final String REF_DATE = "ref-date";
    private static final String REF_NANOS = "ref-nanos";
    private static final String SOURCE = "source";
    private static final String STATUS = "status";

    private PerfsXml() {
    }

    /**
     * Enumeration of XML formats.
     *
     * @author Damien Carbonne
     *
     */
    public enum Format {
        /**
         * Standard format is quite verbose and easy to read.
         */
        STANDARD,

        /**
         * Compact mode is, ... compact.
         * 

* It can, use 3 to 4 times less space that STANDARD mode.
* It is more cryptic, but easier to read than binary format. */ COMPACT; boolean isCompact() { return this == COMPACT; } } protected static SimpleDateFormat getDateFormat() { return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); } protected static SimpleDateFormat getAlternateDateFormat() { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); } /** * Environment XML serialization. * * @author Damien Carbonne * */ public static class Printer { private static final Logger LOGGER = LogManager.getLogger(PerfsXml.Printer.class); private int features = 0; private final Format format; private static final Source SRC = RuntimeEnvironment.getInstance().getSource(Printer.class); private final Map sources = new HashMap<>(); public enum Feature { DEBUG } public Printer(Format format) { Checks.isNotNull(format, "format"); this.format = format; } public Printer() { this(Format.STANDARD); } public Format getFormat() { return format; } public void setEnabled(Feature feature, boolean enabled) { features = IntMasks.setEnabled(features, feature, enabled); } public boolean isEnabled(Feature feature) { return IntMasks.isEnabled(features, feature); } public void save(Environment environment, String filename, Compressor compressor) throws IOException { final RuntimeProbe probe = RuntimeEnvironment.getInstance().createProbe(SRC, MeasureLevel.INFO); probe.start("save(" + filename + ", " + compressor + ")"); try (final XmlWriter writer = new XmlWriter(filename, compressor)) { if (format.isCompact()) { writer.setIndentString(""); writer.setEnabled(XmlWriter.Feature.PRETTY_PRINT, false); } else { writer.setIndentString(" "); writer.setEnabled(XmlWriter.Feature.PRETTY_PRINT, true); } printDocument(environment, writer); } finally { probe.stop(); } } public void printDocument(Environment environment, XmlWriter writer) throws IOException { LOGGER.trace("printDocument(...)"); writer.reset(); writer.beginDocument(); print(environment, writer); writer.endDocument(); } public void print(Environment environment, XmlWriter writer) throws IOException { LOGGER.trace("print'Environment(...) " + getFormat()); writer.beginElement(PERFS); if (format.isCompact()) { writer.addAttribute(FORMAT, getFormat().name()); } writer.addAttribute(KIND, environment.getKind().name()); writer.addAttribute(REF_DATE, PerfsXml.getDateFormat().format(Date.from(environment.getReferenceInstant()))); writer.addAttribute(REF_NANOS, environment.getReferenceNanos()); writer.addAttribute(ELAPSED_NANOS, environment.getElapsedNanos()); sources.clear(); for (final Source source : environment.getSources()) { print(source, writer); } for (final Context context : environment.getContexts()) { print(context, writer); } writer.endElement(); } private void print(Source source, XmlWriter writer) throws IOException { LOGGER.trace("print'Source(...)"); final short id = (short) (sources.size() + 1); sources.put(source, id); writer.beginElement(SOURCE); if (format.isCompact()) { writer.addAttribute(ID, sources.get(source)); } writer.addAttribute(NAME, source.getName()); writer.addAttribute(MAX_LEVEL, source.getMaxLevel().name()); writer.endElement(); } private void print(Context context, XmlWriter writer) throws IOException { LOGGER.trace("print'Context(...)"); writer.beginElement(CONTEXT); writer.addAttribute(ID, context.getId()); writer.addAttribute(NAME, context.getName()); writer.addAttribute(ALIVE, context.isAlive()); if (isEnabled(Feature.DEBUG)) { writer.addAttribute("height", context.getHeight()); } for (int index = 0; index < context.getRootMeasuresCount(); index++) { print(context.getRootMeasure(index), writer); } writer.endElement(); } private void print(Measure measure, XmlWriter writer) throws IOException { LOGGER.trace("print'Measure(...)"); if (getFormat() == Format.STANDARD) { writer.beginElement(MEASURE); writer.addAttribute(SOURCE, measure.getSource().getName()); writer.addAttribute(STATUS, measure.getStatus().name()); writer.addAttribute(LEVEL, measure.getLevel().name()); if (isEnabled(Feature.DEBUG)) { writer.addAttribute("height", measure.getHeight()); } if (measure.getDetails() != null && !measure.getDetails().isEmpty()) { writer.addAttribute(DETAILS, measure.getDetails()); } writer.addAttribute(BEGIN, measure.getAbsoluteBeginNanos()); if (measure.getStatus() != MeasureStatus.RUNNING) { writer.addAttribute(END, measure.getAbsoluteEndNanos()); } } else { // COMPACT writer.beginElement(COMPACT_MEASURE); final StringBuilder builder = new StringBuilder(); builder.append(sources.get(measure.getSource())); builder.append(measure.getStatus().getCode()); builder.append(measure.getRelativeBeginNanos()); builder.append(measure.getLevel().getCode()); if (measure.getStatus() != MeasureStatus.RUNNING) { builder.append(measure.getElapsedNanos()); } builder.append(SEPARATOR); if (measure.getDetails() != null && !measure.getDetails().isEmpty()) { builder.append(measure.getDetails()); } writer.addAttribute(COMPACT_CODE, builder.toString()); // writer.addAttribute(COMPACT_SOURCE, sources.get(measure.getSource())); // writer.addAttribute(STATUS, measure.getStatus().getCode()); // writer.addAttribute(LEVEL, measure.getLevel().getCode()); // if (measure.getDetails() != null && !measure.getDetails().isEmpty()) { // writer.addAttribute(COMPACT_DETAILS, measure.getDetails()); // } // writer.addAttribute(COMPACT_BEGIN, measure.getRelativeBeginNanos()); // if (measure.getStatus() != MeasureStatus.RUNNING) { // writer.addAttribute(COMPACT_END, measure.getElapsedNanos()); // } } Measure child = measure.getFirstChild(); while (child != null) { print(child, writer); child = child.getNextSibling(); } writer.endElement(); } } /** * Environment XML deserialization. * * @author Damien Carbonne * */ public static class Loader extends AbstractResourceLoader { private static final Source SRC = RuntimeEnvironment.getInstance().getSource(Loader.class); private final Map sources = new HashMap<>(); private Format format; public Loader(FailureReaction reaction) { super(reaction); } private Date toDate(String s) { try { return getDateFormat().parse(s); } catch (final ParseException e) { try { return getAlternateDateFormat().parse(s); } catch (final ParseException ex) { getLogger().error("Failed to parse date: \"" + s + "\""); return null; } } } @Override protected SnapshotEnvironment loadRoot(Element root) { final RuntimeProbe probe = RuntimeEnvironment.getInstance().createProbe(SRC, MeasureLevel.INFO); probe.start("loadRoot(...)"); try { final long refNanos = root.getAttributeAsLong(REF_NANOS, 0); final Date refDate = toDate(root.getAttributeValue(REF_DATE, "1970-01-01T00:00:00")); final long elapsedNanos = root.getAttributeAsLong(ELAPSED_NANOS, 0); final Instant refInstant = refDate == null ? null : refDate.toInstant(); this.format = root.getAttributeAsEnum(FORMAT, Format.class, Format.STANDARD); final SnapshotEnvironment environment = new SnapshotEnvironment(refNanos, refInstant, elapsedNanos); for (final Element child : root.getChildren(Element.class)) { switch (child.getName()) { case SOURCE: loadSource(environment, child); break; case CONTEXT: loadContext(environment, child); break; default: unexpectedElement(child, SOURCE, CONTEXT); break; } } return environment; } finally { probe.stop(); } } private void loadSource(SnapshotEnvironment environment, Element element) { final String name = element.getAttributeValue(NAME, null); final MeasureLevel maxLevel = element.getAttributeAsEnum(MAX_LEVEL, MeasureLevel.class, null); final Source source = environment.getSource(name); source.setMaxLevel(maxLevel); if (format.isCompact()) { final short id = element.getAttributeAsShort(ID, (short) -1); sources.put(id, source); } } private void loadContext(SnapshotEnvironment environment, Element element) { final int id = element.getAttributeAsInt(ID, 0); final String name = element.getAttributeValue(NAME, null); final boolean alive = element.getAttributeAsBoolean(ALIVE, false); final SnapshotContext context = environment.createContext(id, name, alive); SnapshotMeasure previous = null; if (format == Format.STANDARD) { for (final Element child : element.getChildren(Element.class)) { if (MEASURE.equals(child.getName())) { previous = loadMeasure(context, null, 0, previous, child); } } } else { for (final Element child : element.getChildren(Element.class)) { if (COMPACT_MEASURE.equals(child.getName())) { previous = loadMeasure(context, null, 0, previous, child); } } } } private static int indexOfFirstNonDigit(String s, int from) { for (int index = from; index < s.length(); index++) { final char c = s.charAt(index); if (c < '0' || c > '9') { return index; } } return -1; } private SnapshotMeasure loadMeasure(SnapshotContext context, SnapshotMeasure parent, int parentDepth, SnapshotMeasure previous, Element element) { if (format == Format.STANDARD) { final String sourceName = element.getAttributeValue(SOURCE, null); final MeasureStatus status = element.getAttributeAsEnum(STATUS, MeasureStatus.class, null); final MeasureLevel level = element.getAttributeAsEnum(LEVEL, MeasureLevel.class, null); final String details = element.getAttributeValue(DETAILS, null); final long begin = element.getAttributeAsLong(BEGIN, 0); final long end = element.getAttributeAsLong(END, 0); final SnapshotMeasure measure = context.createMeasure(parent, parentDepth, previous, context.getEnvironment().getSource(sourceName), details, begin, end, status, level); SnapshotMeasure prev = null; for (final Element child : element.getChildren(Element.class)) { if (MEASURE.equals(child.getName())) { prev = loadMeasure(context, measure, parentDepth + 1, prev, child); } } return measure; } else { final String code = element.getAttributeValue(COMPACT_CODE); // source id + status code + relative begin + level code (+ elapsed nanos) + separator (+ details) final int statusPos = indexOfFirstNonDigit(code, 0); final int levelPos = indexOfFirstNonDigit(code, statusPos + 1); final int separatorPos = code.indexOf(SEPARATOR, levelPos + 1); final short sourceId = Short.parseShort(code.substring(0, statusPos)); final char statusCode = code.charAt(statusPos); final MeasureStatus status = MeasureStatus.ENCODER.decode(statusCode); final char levelCode = code.charAt(levelPos); final MeasureLevel level = MeasureLevel.ENCODER.decode(levelCode); final long relativeBegin = Long.parseLong(code.substring(statusPos + 1, levelPos)); final long begin = relativeBegin + context.getEnvironment().getReferenceNanos(); final long elapsed = status == MeasureStatus.RUNNING ? 0L : Long.parseLong(code.substring(levelPos + 1, separatorPos)); final long end = begin + elapsed; final String details = separatorPos == code.length() - 1 ? null : code.substring(separatorPos + 1); final SnapshotMeasure measure = context.createMeasure(parent, parentDepth, previous, sources.get(sourceId), details, begin, end, status, level); SnapshotMeasure prev = null; for (final Element child : element.getChildren(Element.class)) { if (COMPACT_MEASURE.equals(child.getName())) { prev = loadMeasure(context, measure, parentDepth + 1, prev, child); } } return measure; } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy