package com.yammer.telemetry.tracing.logging; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.fasterxml.jackson.datatype.guava.GuavaModule; import com.yammer.telemetry.tracing.AsynchronousSpanSink; import com.yammer.telemetry.tracing.SpanData; import java.io.*; import java.nio.charset.Charset; import java.util.logging.Logger; public class LogJobFactory implements AsynchronousSpanSink.JobFactory { private static final Logger LOG = Logger.getLogger(LogJob.class.getName()); private final WriterProvider writerProvider; private ObjectMapper objectMapper; private LogJobFactory(WriterProvider writerProvider) { this.writerProvider = writerProvider; this.objectMapper = new ObjectMapper().setPropertyNamingStrategy(new PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy()).setSerializationInclusion(JsonInclude.Include.NON_NULL).registerModule(new GuavaModule()); } public static LogJobFactory withWriter(final Writer writer) { if (writer == null) throw new NullPointerException("Writer must not be null"); return withWriterProvider(new WriterProvider() { @Override public Writer getWriter() throws IOException { return writer; } }); } public static LogJobFactory withFile(final String file) throws IOException { // We try and open the file for append to get early warning of misconfiguration, it will fail is file is not // writable. try (FileOutputStream ignored = new FileOutputStream(file, true)) { return withWriterProvider(new WriterProvider() { @Override public Writer getWriter() throws IOException { return new OutputStreamWriter(new FileOutputStream(file, true), Charset.forName("UTF-8").newEncoder()); } }); } } @Override public Runnable createJob(SpanData data) { return new LogJob(data); } private static LogJobFactory withWriterProvider(WriterProvider writerProvider) { return new LogJobFactory(writerProvider); } private class LogJob implements Runnable { private final Object object; public LogJob(Object object) { this.object = object; } @Override public void run() { try (PrintWriter writer = new PrintWriter(writerProvider.getWriter())) { writer.println(objectMapper.writeValueAsString(object)); writer.flush(); } catch (IOException e) { LOG.throwing(LogJob.class.getName(), "run", e); } } } private static interface WriterProvider { Writer getWriter() throws IOException; } }