001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017package org.apache.logging.log4j.core.appender; 018 019import java.io.Serializable; 020import java.util.concurrent.TimeUnit; 021 022import org.apache.logging.log4j.core.Filter; 023import org.apache.logging.log4j.core.Layout; 024import org.apache.logging.log4j.core.LogEvent; 025import org.apache.logging.log4j.core.config.Property; 026import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; 027import org.apache.logging.log4j.core.util.Constants; 028 029/** 030 * Appends log events as bytes to a byte output stream. The stream encoding is defined in the layout. 031 * 032 * @param <M> The kind of {@link OutputStreamManager} under management 033 */ 034public abstract class AbstractOutputStreamAppender<M extends OutputStreamManager> extends AbstractAppender { 035 036 /** 037 * Subclasses can extend this abstract Builder. 038 * 039 * @param <B> The type to build. 040 */ 041 public abstract static class Builder<B extends Builder<B>> extends AbstractAppender.Builder<B> { 042 043 @PluginBuilderAttribute 044 private boolean bufferedIo = true; 045 046 @PluginBuilderAttribute 047 private int bufferSize = Constants.ENCODER_BYTE_BUFFER_SIZE; 048 049 @PluginBuilderAttribute 050 private boolean immediateFlush = true; 051 052 public int getBufferSize() { 053 return bufferSize; 054 } 055 056 public boolean isBufferedIo() { 057 return bufferedIo; 058 } 059 060 public boolean isImmediateFlush() { 061 return immediateFlush; 062 } 063 064 public B withImmediateFlush(final boolean immediateFlush) { 065 this.immediateFlush = immediateFlush; 066 return asBuilder(); 067 } 068 069 public B withBufferedIo(final boolean bufferedIo) { 070 this.bufferedIo = bufferedIo; 071 return asBuilder(); 072 } 073 074 public B withBufferSize(final int bufferSize) { 075 this.bufferSize = bufferSize; 076 return asBuilder(); 077 } 078 079 } 080 081 /** 082 * Immediate flush means that the underlying writer or output stream will be flushed at the end of each append 083 * operation. Immediate flush is slower but ensures that each append request is actually written. If 084 * <code>immediateFlush</code> is set to {@code false}, then there is a good chance that the last few logs events 085 * are not actually written to persistent media if and when the application crashes. 086 */ 087 private final boolean immediateFlush; 088 089 private final M manager; 090 091 /** 092 * Instantiates a WriterAppender and set the output destination to a new {@link java.io.OutputStreamWriter} 093 * initialized with <code>os</code> as its {@link java.io.OutputStream}. 094 * 095 * @param name The name of the Appender. 096 * @param layout The layout to format the message. 097 * @param manager The OutputStreamManager. 098 * @deprecated Use {@link #AbstractOutputStreamAppender(String, Layout, Filter, boolean, boolean, Property[], OutputStreamManager)} 099 */ 100 @Deprecated 101 protected AbstractOutputStreamAppender(final String name, final Layout<? extends Serializable> layout, 102 final Filter filter, final boolean ignoreExceptions, final boolean immediateFlush, final M manager) { 103 super(name, filter, layout, ignoreExceptions, Property.EMPTY_ARRAY); 104 this.manager = manager; 105 this.immediateFlush = immediateFlush; 106 } 107 108 /** 109 * Instantiates a WriterAppender and set the output destination to a new {@link java.io.OutputStreamWriter} 110 * initialized with <code>os</code> as its {@link java.io.OutputStream}. 111 * 112 * @param name The name of the Appender. 113 * @param layout The layout to format the message. 114 * @param properties optional properties 115 * @param manager The OutputStreamManager. 116 */ 117 protected AbstractOutputStreamAppender(final String name, final Layout<? extends Serializable> layout, 118 final Filter filter, final boolean ignoreExceptions, final boolean immediateFlush, 119 final Property[] properties, final M manager) { 120 super(name, filter, layout, ignoreExceptions, properties); 121 this.manager = manager; 122 this.immediateFlush = immediateFlush; 123 } 124 125 /** 126 * Gets the immediate flush setting. 127 * 128 * @return immediate flush. 129 */ 130 public boolean getImmediateFlush() { 131 return immediateFlush; 132 } 133 134 /** 135 * Gets the manager. 136 * 137 * @return the manager. 138 */ 139 public M getManager() { 140 return manager; 141 } 142 143 @Override 144 public void start() { 145 if (getLayout() == null) { 146 LOGGER.error("No layout set for the appender named [" + getName() + "]."); 147 } 148 if (manager == null) { 149 LOGGER.error("No OutputStreamManager set for the appender named [" + getName() + "]."); 150 } 151 super.start(); 152 } 153 154 @Override 155 public boolean stop(final long timeout, final TimeUnit timeUnit) { 156 return stop(timeout, timeUnit, true); 157 } 158 159 @Override 160 protected boolean stop(final long timeout, final TimeUnit timeUnit, final boolean changeLifeCycleState) { 161 boolean stopped = super.stop(timeout, timeUnit, changeLifeCycleState); 162 stopped &= manager.stop(timeout, timeUnit); 163 if (changeLifeCycleState) { 164 setStopped(); 165 } 166 LOGGER.debug("Appender {} stopped with status {}", getName(), stopped); 167 return stopped; 168 } 169 170 /** 171 * Actual writing occurs here. 172 * <p> 173 * Most subclasses of <code>AbstractOutputStreamAppender</code> will need to override this method. 174 * </p> 175 * 176 * @param event The LogEvent. 177 */ 178 @Override 179 public void append(final LogEvent event) { 180 try { 181 tryAppend(event); 182 } catch (final AppenderLoggingException ex) { 183 error("Unable to write to stream " + manager.getName() + " for appender " + getName(), event, ex); 184 throw ex; 185 } 186 } 187 188 private void tryAppend(final LogEvent event) { 189 if (Constants.ENABLE_DIRECT_ENCODERS) { 190 directEncodeEvent(event); 191 } else { 192 writeByteArrayToManager(event); 193 } 194 } 195 196 protected void directEncodeEvent(final LogEvent event) { 197 getLayout().encode(event, manager); 198 if (this.immediateFlush || event.isEndOfBatch()) { 199 manager.flush(); 200 } 201 } 202 203 protected void writeByteArrayToManager(final LogEvent event) { 204 final byte[] bytes = getLayout().toByteArray(event); 205 if (bytes != null && bytes.length > 0) { 206 manager.write(bytes, this.immediateFlush || event.isEndOfBatch()); 207 } 208 } 209}