org.spf4j.log.LogPrinter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spf4j-core Show documentation
Show all versions of spf4j-core Show documentation
A continuously growing collection of utilities to measure performance, get better diagnostics,
improve performance, or do things more reliably, faster that other open source libraries...
The newest version!
/*
* Copyright 2018 SPF4J.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.spf4j.log;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.UncheckedIOException;
import java.io.Writer;
import java.nio.charset.Charset;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import javax.annotation.concurrent.ThreadSafe;
import org.slf4j.Marker;
import org.slf4j.event.KeyValuePair;
import org.spf4j.base.CoreTextMediaType;
import org.spf4j.base.EscapeJsonStringAppendableWrapper;
import org.spf4j.base.Slf4jMessageFormatter;
import org.spf4j.base.Throwables;
import org.spf4j.base.avro.AThrowables;
import org.spf4j.base.avro.LogRecord;
import org.spf4j.io.ByteArrayBuilder;
import org.spf4j.io.ConfigurableAppenderSupplier;
import org.spf4j.io.ObjectAppender;
import org.spf4j.recyclable.impl.ArraySuppliers;
import org.spf4j.recyclable.impl.ThreadLocalRecyclingSupplier;
/**
* A log printer. The format is not configurable, and this is intentional.
* create One instance of this printer and re-use it.
* @author Zoltan Farkas
*/
@ParametersAreNonnullByDefault
@ThreadSafe
public final class LogPrinter {
private static final ConcurrentMap>
BUFFERS = new ConcurrentHashMap<>();
private final ThreadLocalRecyclingSupplier tlBuffer;
private final ConfigurableAppenderSupplier toStringer;
private final DateTimeFormatter fmt;
interface BufferedAppendable {
Appendable getAppendable();
Appendable getJsonStringEscapingAppendable();
int getCurrentPos();
void resetPos(int pos);
static BufferedAppendable from(final StringBuilder sb) {
return new BufferedAppendable() {
private Appendable escaper = null;
@Override
public Appendable getAppendable() {
return sb;
}
@Override
public Appendable getJsonStringEscapingAppendable() {
if (escaper == null) {
escaper = new EscapeJsonStringAppendableWrapper(sb);
}
return escaper;
}
@Override
public int getCurrentPos() {
return sb.length();
}
@Override
public void resetPos(final int pos) {
sb.setLength(pos);
}
};
}
}
private static final class Buffer implements BufferedAppendable {
private static final int MAX_BUFFER_SIZE = Integer.getInteger("spf4j.logPrinter", 1024 * 32);
private final ByteArrayBuilder bab;
private final Writer writer;
private final EscapeJsonStringAppendableWrapper writerEscaper;
Buffer(final Charset charset) {
bab = new ByteArrayBuilder(512, ArraySuppliers.Bytes.JAVA_NEW);
writer = new BufferedWriter(new OutputStreamWriter(bab, charset));
writerEscaper = new EscapeJsonStringAppendableWrapper(writer);
}
private void clear() {
try {
writer.flush();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
bab.reset();
}
public Appendable getAppendable() {
return writer;
}
public Appendable getJsonStringEscapingAppendable() {
return writerEscaper;
}
private void flush() {
try {
writer.flush();
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
private byte[] getBytes() {
return bab.getBuffer();
}
private int size() {
return bab.size();
}
@Override
public int getCurrentPos() {
flush();
return bab.size();
}
@Override
@SuppressFBWarnings("EXS_EXCEPTION_SOFTENING_NO_CHECKED") //on purpose.
public void resetPos(final int pos) {
flush();
bab.resetCountTo(pos);
}
}
@SuppressFBWarnings("EI_EXPOSE_REP")
public ConfigurableAppenderSupplier getAppenderSupplier() {
return toStringer;
}
public LogPrinter() {
this(DateTimeFormatter.ISO_INSTANT, Charset.defaultCharset());
}
public LogPrinter(final Charset charset) {
this(DateTimeFormatter.ISO_INSTANT, charset);
}
public LogPrinter(final DateTimeFormatter fmt, final Charset charset) {
this.fmt = fmt;
this.toStringer = new ConfigurableAppenderSupplier();
tlBuffer = BUFFERS.computeIfAbsent(charset,
(cs) -> new ThreadLocalRecyclingSupplier(() -> new Buffer(cs)));
}
public OutputStream print(final Slf4jLogRecord record, final OutputStream os, final OutputStream errStream) {
if (record.getLevel() == Level.ERROR) {
print(record, errStream);
return errStream;
} else {
print(record, os);
return os;
}
}
public void print(final Slf4jLogRecord record, final OutputStream os) {
Buffer buff = tlBuffer.get();
boolean recycle = true;
try {
buff.clear();
print(record, buff, "");
buff.flush();
int len = buff.size();
os.write(buff.getBytes(), 0, len);
if (len > Buffer.MAX_BUFFER_SIZE) {
recycle = false;
}
} catch (IOException ex) {
throw new UncheckedIOException(ex);
} finally {
if (recycle) {
tlBuffer.recycle(buff);
}
}
}
public byte[] printToBytes(final Slf4jLogRecord record) {
Buffer buff = tlBuffer.get();
boolean recycle = true;
try {
buff.clear();
print(record, buff, "");
buff.flush();
int size = buff.size();
if (size > Buffer.MAX_BUFFER_SIZE) {
recycle = false;
}
return Arrays.copyOf(buff.getBytes(), size);
} catch (IOException ex) {
throw new UncheckedIOException(ex);
} finally {
if (recycle) {
tlBuffer.recycle(buff);
}
}
}
public void print(final LogRecord record, final OutputStream os) throws IOException {
printTo(os, record, "");
os.flush();
}
public void printTo(final StringBuilder sb, final Slf4jLogRecord record, final String annotate) {
try {
print(record, BufferedAppendable.from(sb), annotate);
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
public void printTo(final OutputStream stream, final LogRecord record, final String annotate) {
Buffer buff = tlBuffer.get();
buff.clear();
try {
print(record, buff, annotate);
buff.flush();
stream.write(buff.getBytes(), 0, buff.size());
stream.flush();
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
public void printTo(final PrintStream stream, final Slf4jLogRecord record, final String annotate) {
Buffer buff = tlBuffer.get();
buff.clear();
try {
print(record, buff, annotate);
buff.flush();
stream.write(buff.getBytes(), 0, buff.size());
stream.flush();
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
static void printMarker(final Marker marker, final Appendable wr,
final Appendable wrapper)
throws IOException {
if (marker.hasReferences()) {
wr.append('{');
wr.append('"');
wrapper.append(marker.getName());
wr.append("\":[");
Iterator it = marker.iterator();
if (it.hasNext()) {
printMarker(it.next(), wr, wrapper);
while (it.hasNext()) {
wr.append(',');
printMarker(it.next(), wr, wrapper);
}
}
wr.append("]}");
} else {
wr.append('"');
wrapper.append(marker.getName());
wr.append('"');
}
}
private void print(final Slf4jLogRecord record, final BufferedAppendable app, final String annotate)
throws IOException {
Appendable wr = app.getAppendable();
Appendable wrapper = app.getJsonStringEscapingAppendable();
wr.append(annotate);
wr.append(record.getLevel().toCharRepresentation());
wr.append(' ');
fmt.formatTo(Instant.ofEpochMilli(record.getTimeStamp()), wr);
wr.append(' ');
Marker marker = record.getMarker();
if (marker != null) {
printMarker(marker, wr, wrapper);
wr.append(' ');
}
Throwables.writeAbreviatedClassName(record.getLoggerName(), wr);
wr.append(" \"");
wrapper.append(record.getThreadName());
wr.append("\" \"");
Object[] arguments = record.getArguments();
int i = Slf4jMessageFormatter.format(LogPrinter::exHandle, 0, wrapper, record.getMessageFormat(),
toStringer, arguments);
wr.append("\" ");
Throwable t = null;
if (i < arguments.length) {
boolean first = true;
for (; i < arguments.length; i++) {
Object arg = arguments[i];
if (arg instanceof Throwable) {
if (t == null) {
t = (Throwable) arg;
} else {
t.addSuppressed((Throwable) arg); // not ideal
}
} else {
if (!first) {
wr.append(", ");
} else {
wr.append('[');
first = false;
}
printJsonObject(arg, app);
}
}
if (!first) {
wr.append(']');
}
}
List kvs = record.getKVPayload();
Iterator iterator = kvs.iterator();
if (iterator.hasNext()) {
wr.append(" {");
KeyValuePair next = iterator.next();
printJsonObject(next.key, app);
wr.append(':');
printJsonObject(next.value, app);
while (iterator.hasNext()) {
next = iterator.next();
wr.append(',');
printJsonObject(next.key, app);
wr.append(':');
printJsonObject(next.value, app);
}
wr.append('}');
}
if (t != null) {
wr.append('\n');
Throwables.writeTo(t, wr, Throwables.PackageDetail.SHORT);
} else {
wr.append('\n');
}
}
private void print(final LogRecord record, final BufferedAppendable ba, final String annotate)
throws IOException {
Appendable wr = ba.getAppendable();
Appendable wrapper = ba.getJsonStringEscapingAppendable();
wr.append(annotate);
wr.append(Level.fromAvroLevel(record.getLevel()).toCharRepresentation());
wr.append(' ');
wr.append('"');
wrapper.append(record.getOrigin());
wr.append("\" ");
fmt.formatTo(record.getTs(), wr);
wr.append(' ');
wr.append(record.getLogger());
wr.append(" \"");
wrapper.append(record.getThr());
wrapper.append(':');
wrapper.append(record.getTrId());
wr.append("\" \"");
Slf4jMessageFormatter.format(wrapper, record.getMsg(), record.getMsgArgs().toArray());
wr.append("\" ");
Map attrs = record.getAttrs();
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy