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.plugins.PluginBuilderAttribute; 026import org.apache.logging.log4j.core.util.Constants; 027 028/** 029 * Appends log events as bytes to a byte output stream. The stream encoding is defined in the layout. 030 * 031 * @param <M> The kind of {@link OutputStreamManager} under management 032 */ 033public abstract class AbstractOutputStreamAppender<M extends OutputStreamManager> extends AbstractAppender { 034 035 /** 036 * Subclasses can extend this abstract Builder. 037 * 038 * @param <B> The type to build. 039 */ 040 public abstract static class Builder<B extends Builder<B>> extends AbstractAppender.Builder<B> { 041 042 @PluginBuilderAttribute 043 private boolean bufferedIo = true; 044 045 @PluginBuilderAttribute 046 private int bufferSize = Constants.ENCODER_BYTE_BUFFER_SIZE; 047 048 @PluginBuilderAttribute 049 private boolean immediateFlush = true; 050 051 public int getBufferSize() { 052 return bufferSize; 053 } 054 055 public boolean isBufferedIo() { 056 return bufferedIo; 057 } 058 059 public boolean isImmediateFlush() { 060 return immediateFlush; 061 } 062 063 public B withImmediateFlush(final boolean immediateFlush) { 064 this.immediateFlush = immediateFlush; 065 return asBuilder(); 066 } 067 068 public B withBufferedIo(final boolean bufferedIo) { 069 this.bufferedIo = bufferedIo; 070 return asBuilder(); 071 } 072 073 public B withBufferSize(final int bufferSize) { 074 this.bufferSize = bufferSize; 075 return asBuilder(); 076 } 077 078 } 079 080 /** 081 * Immediate flush means that the underlying writer or output stream will be flushed at the end of each append 082 * operation. Immediate flush is slower but ensures that each append request is actually written. If 083 * <code>immediateFlush</code> is set to {@code false}, then there is a good chance that the last few logs events 084 * are not actually written to persistent media if and when the application crashes. 085 */ 086 private final boolean immediateFlush; 087 088 private final M manager; 089 090 /** 091 * Instantiates a WriterAppender and set the output destination to a new {@link java.io.OutputStreamWriter} 092 * initialized with <code>os</code> as its {@link java.io.OutputStream}. 093 * 094 * @param name The name of the Appender. 095 * @param layout The layout to format the message. 096 * @param manager The OutputStreamManager. 097 */ 098 protected AbstractOutputStreamAppender(final String name, final Layout<? extends Serializable> layout, 099 final Filter filter, final boolean ignoreExceptions, final boolean immediateFlush, final M manager) { 100 super(name, filter, layout, ignoreExceptions); 101 this.manager = manager; 102 this.immediateFlush = immediateFlush; 103 } 104 105 /** 106 * Gets the immediate flush setting. 107 * 108 * @return immediate flush. 109 */ 110 public boolean getImmediateFlush() { 111 return immediateFlush; 112 } 113 114 /** 115 * Gets the manager. 116 * 117 * @return the manager. 118 */ 119 public M getManager() { 120 return manager; 121 } 122 123 @Override 124 public void start() { 125 if (getLayout() == null) { 126 LOGGER.error("No layout set for the appender named [" + getName() + "]."); 127 } 128 if (manager == null) { 129 LOGGER.error("No OutputStreamManager set for the appender named [" + getName() + "]."); 130 } 131 super.start(); 132 } 133 134 @Override 135 public boolean stop(final long timeout, final TimeUnit timeUnit) { 136 return stop(timeout, timeUnit, true); 137 } 138 139 @Override 140 protected boolean stop(final long timeout, final TimeUnit timeUnit, final boolean changeLifeCycleState) { 141 boolean stopped = super.stop(timeout, timeUnit, changeLifeCycleState); 142 stopped &= manager.stop(timeout, timeUnit); 143 if (changeLifeCycleState) { 144 setStopped(); 145 } 146 LOGGER.debug("Appender {} stopped with status {}", getName(), stopped); 147 return stopped; 148 } 149 150 /** 151 * Actual writing occurs here. 152 * <p> 153 * Most subclasses of <code>AbstractOutputStreamAppender</code> will need to override this method. 154 * </p> 155 * 156 * @param event The LogEvent. 157 */ 158 @Override 159 public void append(final LogEvent event) { 160 try { 161 tryAppend(event); 162 } catch (final AppenderLoggingException ex) { 163 error("Unable to write to stream " + manager.getName() + " for appender " + getName() + ": " + ex); 164 throw ex; 165 } 166 } 167 168 private void tryAppend(final LogEvent event) { 169 if (Constants.ENABLE_DIRECT_ENCODERS) { 170 directEncodeEvent(event); 171 } else { 172 writeByteArrayToManager(event); 173 } 174 } 175 176 protected void directEncodeEvent(final LogEvent event) { 177 getLayout().encode(event, manager); 178 if (this.immediateFlush || event.isEndOfBatch()) { 179 manager.flush(); 180 } 181 } 182 183 protected void writeByteArrayToManager(final LogEvent event) { 184 final byte[] bytes = getLayout().toByteArray(event); 185 if (bytes != null && bytes.length > 0) { 186 manager.write(bytes, this.immediateFlush || event.isEndOfBatch()); 187 } 188 } 189}