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

com.yahoo.container.logging.TraceRenderer Maven / Gradle / Ivy

// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.logging;

import com.yahoo.data.access.Inspectable;
import com.yahoo.data.access.Inspector;
import com.yahoo.data.access.simple.JsonRender;
import com.yahoo.yolean.trace.TraceNode;
import com.yahoo.yolean.trace.TraceVisitor;
import com.fasterxml.jackson.core.JsonGenerator;

import java.io.IOException;

public class TraceRenderer extends TraceVisitor {
    private static final String TRACE_CHILDREN = "children";
    private static final String TRACE_MESSAGE = "message";
    private static final String TRACE_TIMESTAMP = "timestamp";
    private static final String TRACE = "trace";

    private final long basetime;
    private final JsonGenerator generator;
    private final FieldConsumer fieldConsumer;
    private boolean hasFieldName = false;
    int emittedChildNesting = 0;
    int currentChildNesting = 0;
    private boolean insideOpenObject = false;

    public interface FieldConsumer {
        void accept(Object object) throws IOException;
    }

    private static class Consumer implements FieldConsumer {
        private final JsonGenerator generator;

        Consumer(JsonGenerator generator) {
            this.generator = generator;
        }

        @Override
        public void accept(Object object) throws IOException {
            if (object instanceof Inspectable) {
                renderInspectorDirect(((Inspectable) object).inspect());
            } else {
                generator.writeObject(object);
            }
        }
        private void renderInspectorDirect(Inspector data) throws IOException {
            StringBuilder intermediate = new StringBuilder();
            JsonRender.render(data, intermediate, true);
            generator.writeRawValue(intermediate.toString());
        }
    }

    TraceRenderer(JsonGenerator generator, long basetime) {
        this(generator, new Consumer(generator), basetime);
    }
    public TraceRenderer(JsonGenerator generator, FieldConsumer consumer, long basetime) {
        this.generator = generator;
        this.fieldConsumer = consumer;
        this.basetime = basetime;
    }

    @Override
    public void entering(TraceNode node) {
        ++currentChildNesting;
    }

    @Override
    public void leaving(TraceNode node) {
        conditionalEndObject();
        if (currentChildNesting == emittedChildNesting) {
            try {
                generator.writeEndArray();
                generator.writeEndObject();
            } catch (IOException e) {
                throw new TraceRenderWrapper(e);
            }
            --emittedChildNesting;
        }
        --currentChildNesting;
    }

    @Override
    public void visit(TraceNode node) {
        try {
            doVisit(node.timestamp(), node.payload(), node.children().iterator().hasNext());
        } catch (IOException e) {
            throw new TraceRenderWrapper(e);
        }
    }

    private void doVisit(long timestamp, Object payload, boolean hasChildren) throws IOException {
        boolean dirty = false;
        if (timestamp != 0L) {
            header();
            generator.writeStartObject();
            generator.writeNumberField(TRACE_TIMESTAMP, timestamp - basetime);
            dirty = true;
        }
        if (payload != null) {
            if (!dirty) {
                header();
                generator.writeStartObject();
            }
            generator.writeFieldName(TRACE_MESSAGE);
            fieldConsumer.accept(payload);
            dirty = true;
        }
        if (dirty) {
            if (!hasChildren) {
                generator.writeEndObject();
            } else {
                setInsideOpenObject(true);
            }
        }
    }
    private void header() {
        fieldName();
        for (int i = 0; i < (currentChildNesting - emittedChildNesting); ++i) {
            startChildArray();
        }
        emittedChildNesting = currentChildNesting;
    }

    private void startChildArray() {
        try {
            conditionalStartObject();
            generator.writeArrayFieldStart(TRACE_CHILDREN);
        } catch (IOException e) {
            throw new TraceRenderWrapper(e);
        }
    }

    private void conditionalStartObject() throws IOException {
        if (!isInsideOpenObject()) {
            generator.writeStartObject();
        } else {
            setInsideOpenObject(false);
        }
    }

    private void conditionalEndObject() {
        if (isInsideOpenObject()) {
            // This triggers if we were inside a data node with payload and
            // subnodes, but none of the subnodes contained data
            try {
                generator.writeEndObject();
                setInsideOpenObject(false);
            } catch (IOException e) {
                throw new TraceRenderWrapper(e);
            }
        }
    }

    private void fieldName() {
        if (hasFieldName) {
            return;
        }

        try {
            generator.writeFieldName(TRACE);
        } catch (IOException e) {
            throw new TraceRenderWrapper(e);
        }
        hasFieldName = true;
    }

    boolean isInsideOpenObject() {
        return insideOpenObject;
    }

    void setInsideOpenObject(boolean insideOpenObject) {
        this.insideOpenObject = insideOpenObject;
    }
    public static final class TraceRenderWrapper extends RuntimeException {

        /**
         * Should never be serialized, but this is still needed.
         */
        private static final long serialVersionUID = 2L;

        TraceRenderWrapper(IOException wrapped) {
            super(wrapped);
        }

    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy