/* * Copyright (c) 2012-2014 Spotify AB * * 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 de.rwth.idsg.bikeman.utils; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.Field; import java.nio.charset.Charset; import ch.qos.logback.classic.PatternLayout; import ch.qos.logback.classic.net.SyslogAppender; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.IThrowableProxy; import ch.qos.logback.classic.spi.StackTraceElementProxy; import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.Layout; import ch.qos.logback.core.net.SyslogAppenderBase; import com.google.common.base.Charsets; import com.google.common.base.Throwables; /** * Taken from: https://github.com/spotify/logging-java/tree/master/src/main/java/com/spotify/logging/logback * * A {@link SyslogAppender} with millisecond timestamp precision. */ public class MillisecondPrecisionSyslogAppender extends SyslogAppender { private Charset charset = Charsets.UTF_8; PatternLayout stackTraceLayout = new PatternLayout(); private OutputStream sos; @Override public void start() { super.start(); sos = getSyslogOutputStream(); setupStackTraceLayout(); } String getPrefixPattern() { return "%syslogStart{" + getFacility() + "}%nopex{}"; } @Override protected void append(ILoggingEvent eventObject) { // code based on ch.qos.logback.core.net.SyslogAppenderBase.append() if (!isStarted()) { return; } try { String msg = getLayout().doLayout(eventObject); if(msg == null) { return; } if (msg.length() > getMaxMessageSize()) { msg = msg.substring(0, getMaxMessageSize()); } sos.write(msg.getBytes(charset)); sos.flush(); postProcess(eventObject, sos); } catch (IOException ioe) { addError("Failed to send diagram to " + getSyslogHost(), ioe); } } @Override protected void postProcess(final Object eventObject, final OutputStream sw) { if (isThrowableExcluded()) { return; } final ILoggingEvent event = (ILoggingEvent) eventObject; IThrowableProxy tp = event.getThrowableProxy(); if (tp == null) { return; } final String stackTracePrefix = stackTraceLayout.doLayout(event); boolean isRootException = true; while (tp != null) { final StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray(); try { handleThrowableFirstLine(sw, tp, stackTracePrefix, isRootException); isRootException = false; for (final StackTraceElementProxy step : stepArray) { final StringBuilder sb = new StringBuilder(); sb.append(stackTracePrefix).append(step); sw.write(sb.toString().getBytes()); sw.flush(); } } catch (IOException e) { break; } tp = tp.getCause(); } } private void handleThrowableFirstLine(final OutputStream sw, final IThrowableProxy tp, final String stackTracePrefix, final boolean isRootException) throws IOException { final StringBuilder sb = new StringBuilder().append(stackTracePrefix); if (!isRootException) { sb.append(CoreConstants.CAUSED_BY); } sb.append(tp.getClassName()).append(": ").append(tp.getMessage()); sw.write(sb.toString().getBytes()); sw.flush(); } @Override public Layout<ILoggingEvent> buildLayout() { final PatternLayout layout = new PatternLayout(); layout.getInstanceConverterMap().put("syslogStart", MillisecondPrecisionSyslogStartConverter.class.getName()); if (suffixPattern == null) { suffixPattern = DEFAULT_SUFFIX_PATTERN; } layout.setPattern(getPrefixPattern() + suffixPattern); layout.setContext(getContext()); layout.start(); return layout; } private void setupStackTraceLayout() { stackTraceLayout.getInstanceConverterMap().put( "syslogStart", MillisecondPrecisionSyslogStartConverter.class.getName()); stackTraceLayout.setPattern(getPrefixPattern() + getStackTracePattern()); stackTraceLayout.setContext(getContext()); stackTraceLayout.start(); } // Horrible hack to access the syslog stream through reflection private OutputStream getSyslogOutputStream() { Field f; try { f = SyslogAppenderBase.class.getDeclaredField("sos"); f.setAccessible(true); return (OutputStream) f.get(this); } catch (ReflectiveOperationException e) { throw Throwables.propagate(e); } } /** * @return the charset used for encoding the output */ public Charset getCharset() { return charset; } /** * @param charset the charset to use for encoding the output */ public void setCharset(Charset charset) { this.charset = charset; } }