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. <p> For
034 * more information about this appender, please refer to the online manual at
035 * http://logback.qos.ch/manual/appenders.html#SyslogAppender
036 *
037 * @author Ceki G&uuml;lc&uuml;
038 */
039public class SyslogAppender extends SyslogAppenderBase<ILoggingEvent> {
040
041    static final public String DEFAULT_SUFFIX_PATTERN = "[%thread] %logger %msg";
042    static final public String DEFAULT_STACKTRACE_PATTERN = "" + CoreConstants.TAB;
043
044    PatternLayout stackTraceLayout = new PatternLayout();
045    String stackTracePattern = DEFAULT_STACKTRACE_PATTERN;
046
047    boolean throwableExcluded = false;
048
049    public void start() {
050        super.start();
051        setupStackTraceLayout();
052    }
053
054    String getPrefixPattern() {
055        return "%syslogStart{" + getFacility() + "}%nopex{}";
056    }
057
058    @Override
059    public SyslogOutputStream createOutputStream() throws SocketException, UnknownHostException {
060        return new SyslogOutputStream(getSyslogHost(), getPort());
061    }
062
063    /**
064     * Convert a level to equivalent syslog severity. Only levels for printing
065     * methods i.e DEBUG, WARN, INFO and ERROR are converted.
066     *
067     * @see ch.qos.logback.core.net.SyslogAppenderBase#getSeverityForEvent(java.lang.Object)
068     */
069    @Override
070    public int getSeverityForEvent(Object eventObject) {
071        ILoggingEvent event = (ILoggingEvent) eventObject;
072        return LevelToSyslogSeverity.convert(event);
073    }
074
075    @Override
076    protected void postProcess(Object eventObject, OutputStream sw) {
077        if (throwableExcluded)
078            return;
079
080        ILoggingEvent event = (ILoggingEvent) eventObject;
081        IThrowableProxy tp = event.getThrowableProxy();
082
083        if (tp == null)
084            return;
085
086        String stackTracePrefix = stackTraceLayout.doLayout(event);
087        boolean isRootException = true;
088        while (tp != null) {
089            StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray();
090            try {
091                handleThrowableFirstLine(sw, tp, stackTracePrefix, isRootException);
092                isRootException = false;
093                for (StackTraceElementProxy step : stepArray) {
094                    StringBuilder sb = new StringBuilder();
095                    sb.append(stackTracePrefix).append(step);
096                    sw.write(sb.toString().getBytes());
097                    sw.flush();
098                }
099            } catch (IOException e) {
100                break;
101            }
102            tp = tp.getCause();
103        }
104    }
105
106    // LOGBACK-411 and LOGBACK-750
107    private void handleThrowableFirstLine(OutputStream sw, IThrowableProxy tp, String stackTracePrefix, boolean isRootException) throws IOException {
108        StringBuilder sb = new StringBuilder().append(stackTracePrefix);
109
110        if (!isRootException) {
111            sb.append(CoreConstants.CAUSED_BY);
112        }
113        sb.append(tp.getClassName()).append(": ").append(tp.getMessage());
114        sw.write(sb.toString().getBytes());
115        sw.flush();
116    }
117
118    boolean stackTraceHeaderLine(StringBuilder sb, boolean topException) {
119
120        return false;
121    }
122
123    public Layout<ILoggingEvent> buildLayout() {
124        PatternLayout layout = new PatternLayout();
125        layout.getInstanceConverterMap().put("syslogStart", SyslogStartConverter.class.getName());
126        if (suffixPattern == null) {
127            suffixPattern = DEFAULT_SUFFIX_PATTERN;
128        }
129        layout.setPattern(getPrefixPattern() + suffixPattern);
130        layout.setContext(getContext());
131        layout.start();
132        return layout;
133    }
134
135    private void setupStackTraceLayout() {
136        stackTraceLayout.getInstanceConverterMap().put("syslogStart", SyslogStartConverter.class.getName());
137
138        stackTraceLayout.setPattern(getPrefixPattern() + stackTracePattern);
139        stackTraceLayout.setContext(getContext());
140        stackTraceLayout.start();
141    }
142
143    public boolean isThrowableExcluded() {
144        return throwableExcluded;
145    }
146
147    /**
148     * Setting throwableExcluded to true causes no Throwable's stack trace data to be sent to
149     * the syslog daemon. By default, stack trace data is sent to syslog daemon.
150     *
151     * @param throwableExcluded
152     * @since 1.0.4
153     */
154    public void setThrowableExcluded(boolean throwableExcluded) {
155        this.throwableExcluded = throwableExcluded;
156    }
157
158    /**
159     * See {@link #setStackTracePattern(String).
160     *
161     * @return the stackTraceSuffixPattern
162     * @since 1.0.4
163     */
164    public String getStackTracePattern() {
165        return stackTracePattern;
166    }
167
168    /**
169     * Stack trace lines are sent to the syslog server separately from the main message
170     * For stack trace lines, the stackTracePattern is used instead of {@link #suffixPattern}.
171     * The <b>stackTracePattern</b> option allows specification of a separately format for the
172     * non-standardized part of stack trace lines.
173     *
174     * @param stackTracePattern
175     * @since 1.0.4
176     */
177    public void setStackTracePattern(String stackTracePattern) {
178        this.stackTracePattern = stackTracePattern;
179    }
180}