001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.classic.net; 015 016import java.io.IOException; 017import java.io.OutputStream; 018import java.net.SocketException; 019import java.net.UnknownHostException; 020 021import ch.qos.logback.classic.PatternLayout; 022import ch.qos.logback.classic.pattern.SyslogStartConverter; 023import ch.qos.logback.classic.spi.ILoggingEvent; 024import ch.qos.logback.classic.spi.IThrowableProxy; 025import ch.qos.logback.classic.spi.StackTraceElementProxy; 026import ch.qos.logback.classic.util.LevelToSyslogSeverity; 027import ch.qos.logback.core.CoreConstants; 028import ch.qos.logback.core.Layout; 029import ch.qos.logback.core.net.SyslogAppenderBase; 030import ch.qos.logback.core.net.SyslogOutputStream; 031 032/** 033 * This appender can be used to send messages to a remote syslog daemon. 034 * <p> 035 * For more information about this appender, please refer to the online manual 036 * at http://logback.qos.ch/manual/appenders.html#SyslogAppender 037 * 038 * @author Ceki Gülcü 039 */ 040public class SyslogAppender extends SyslogAppenderBase<ILoggingEvent> { 041 042 static final public String DEFAULT_SUFFIX_PATTERN = "[%thread] %logger %msg"; 043 static final public String DEFAULT_STACKTRACE_PATTERN = "" + CoreConstants.TAB; 044 045 PatternLayout stackTraceLayout = new PatternLayout(); 046 String stackTracePattern = DEFAULT_STACKTRACE_PATTERN; 047 048 boolean throwableExcluded = false; 049 050 public void start() { 051 super.start(); 052 setupStackTraceLayout(); 053 } 054 055 String getPrefixPattern() { 056 return "%syslogStart{" + getFacility() + "}%nopex{}"; 057 } 058 059 @Override 060 public SyslogOutputStream createOutputStream() throws SocketException, UnknownHostException { 061 return new SyslogOutputStream(getSyslogHost(), getPort()); 062 } 063 064 /** 065 * Convert a level to equivalent syslog severity. Only levels for printing 066 * methods i.e. DEBUG, WARN, INFO and ERROR are converted. 067 * 068 * @see ch.qos.logback.core.net.SyslogAppenderBase#getSeverityForEvent(java.lang.Object) 069 */ 070 @Override 071 public int getSeverityForEvent(Object eventObject) { 072 ILoggingEvent event = (ILoggingEvent) eventObject; 073 return LevelToSyslogSeverity.convert(event); 074 } 075 076 @Override 077 protected void postProcess(Object eventObject, OutputStream sw) { 078 if (throwableExcluded) 079 return; 080 081 ILoggingEvent event = (ILoggingEvent) eventObject; 082 IThrowableProxy tp = event.getThrowableProxy(); 083 084 if (tp == null) 085 return; 086 087 String stackTracePrefix = stackTraceLayout.doLayout(event); 088 boolean isRootException = true; 089 while (tp != null) { 090 StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray(); 091 try { 092 handleThrowableFirstLine(sw, tp, stackTracePrefix, isRootException); 093 isRootException = false; 094 for (StackTraceElementProxy step : stepArray) { 095 StringBuilder sb = new StringBuilder(); 096 sb.append(stackTracePrefix).append(step); 097 sw.write(sb.toString().getBytes()); 098 sw.flush(); 099 } 100 } catch (IOException e) { 101 break; 102 } 103 tp = tp.getCause(); 104 } 105 } 106 107 // LOGBACK-411 and LOGBACK-750 108 private void handleThrowableFirstLine(OutputStream sw, IThrowableProxy tp, String stackTracePrefix, 109 boolean isRootException) throws IOException { 110 StringBuilder sb = new StringBuilder().append(stackTracePrefix); 111 112 if (!isRootException) { 113 sb.append(CoreConstants.CAUSED_BY); 114 } 115 sb.append(tp.getClassName()).append(": ").append(tp.getMessage()); 116 sw.write(sb.toString().getBytes()); 117 sw.flush(); 118 } 119 120 boolean stackTraceHeaderLine(StringBuilder sb, boolean topException) { 121 122 return false; 123 } 124 125 public Layout<ILoggingEvent> buildLayout() { 126 PatternLayout layout = new PatternLayout(); 127 layout.getInstanceConverterMap().put("syslogStart", SyslogStartConverter.class.getName()); 128 if (suffixPattern == null) { 129 suffixPattern = DEFAULT_SUFFIX_PATTERN; 130 } 131 layout.setPattern(getPrefixPattern() + suffixPattern); 132 layout.setContext(getContext()); 133 layout.start(); 134 return layout; 135 } 136 137 private void setupStackTraceLayout() { 138 stackTraceLayout.getInstanceConverterMap().put("syslogStart", SyslogStartConverter.class.getName()); 139 140 stackTraceLayout.setPattern(getPrefixPattern() + stackTracePattern); 141 stackTraceLayout.setContext(getContext()); 142 stackTraceLayout.start(); 143 } 144 145 public boolean isThrowableExcluded() { 146 return throwableExcluded; 147 } 148 149 /** 150 * Setting throwableExcluded to true causes no Throwable's stack trace data to 151 * be sent to the syslog daemon. By default, stack trace data is sent to syslog 152 * daemon. 153 * 154 * @param throwableExcluded 155 * @since 1.0.4 156 */ 157 public void setThrowableExcluded(boolean throwableExcluded) { 158 this.throwableExcluded = throwableExcluded; 159 } 160 161 /** 162 * See {@link #setStackTracePattern(String). 163 * 164 * @return the stackTraceSuffixPattern 165 * @since 1.0.4 166 */ 167 public String getStackTracePattern() { 168 return stackTracePattern; 169 } 170 171 /** 172 * Stack trace lines are sent to the syslog server separately from the main 173 * message For stack trace lines, the stackTracePattern is used instead of 174 * {@link #suffixPattern}. The <b>stackTracePattern</b> option allows 175 * specification of a separate format for the non-standardized part of stack 176 * trace lines. 177 * 178 * @param stackTracePattern 179 * @since 1.0.4 180 */ 181 public void setStackTracePattern(String stackTracePattern) { 182 this.stackTracePattern = stackTracePattern; 183 } 184}