
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