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&uuml;lc&uuml;
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}