1616 */
1717package org .apache .logging .log4j .core .appender ;
1818
19+ import java .io .FileDescriptor ;
20+ import java .io .FileOutputStream ;
21+ import java .io .IOException ;
1922import java .io .OutputStream ;
2023import java .nio .charset .Charset ;
2124import java .nio .charset .UnsupportedCharsetException ;
22- import java .util .List ;
23- import java .util .Objects ;
2425import java .util .concurrent .atomic .AtomicInteger ;
2526import org .apache .logging .log4j .core .Appender ;
2627import org .apache .logging .log4j .core .Filter ;
2728import org .apache .logging .log4j .core .Layout ;
28- import org .apache .logging .log4j .core .appender .internal .DefaultConsoleStreamSupplier ;
29- import org .apache .logging .log4j .core .config .Configuration ;
3029import org .apache .logging .log4j .core .config .Property ;
3130import org .apache .logging .log4j .core .util .CloseShieldOutputStream ;
32- import org .apache .logging .log4j .kit .env .PropertyEnvironment ;
3331import org .apache .logging .log4j .plugins .Configurable ;
34- import org .apache .logging .log4j .plugins .Namespace ;
3532import org .apache .logging .log4j .plugins .Plugin ;
3633import org .apache .logging .log4j .plugins .PluginBuilderAttribute ;
3734import org .apache .logging .log4j .plugins .PluginFactory ;
38- import org .apache .logging .log4j .plugins .di .Key ;
3935import org .apache .logging .log4j .plugins .validation .constraints .Required ;
40- import org .jspecify .annotations .Nullable ;
4136
4237/**
4338 * Appends log events to <code>System.out</code> or <code>System.err</code> using a layout specified by the user. The
@@ -166,30 +161,15 @@ public B setDirect(final boolean shouldDirect) {
166161
167162 @ Override
168163 public ConsoleAppender build () {
169- final Layout layout = getOrCreateLayout (target .getDefaultCharset ());
170-
171- final Configuration configuration = getConfiguration ();
172- final PropertyEnvironment propertyEnvironment ;
173- final List <ConsoleStreamSupplier > suppliers ;
174- if (configuration != null ) {
175- propertyEnvironment = configuration .getLoggerContext () != null
176- ? configuration .getLoggerContext ().getEnvironment ()
177- : PropertyEnvironment .getGlobal ();
178-
179- suppliers = configuration .getComponent (new @ Namespace (ConsoleStreamSupplier .NAMESPACE ) Key <>() {});
180- } else {
181- propertyEnvironment = PropertyEnvironment .getGlobal ();
182- suppliers = List .of (new DefaultConsoleStreamSupplier ());
164+ if (direct && follow ) {
165+ LOGGER .error ("Cannot use both `direct` and `follow` on ConsoleAppender." );
166+ return null ;
183167 }
168+ final Layout layout = getOrCreateLayout (target .getDefaultCharset ());
184169
185- final OutputStream stream = suppliers .stream ()
186- .map (s -> s .getOutputStream (follow , direct , target , propertyEnvironment ))
187- .filter (Objects ::nonNull )
188- .findFirst ()
189- .orElse (null );
190- if (stream == null ) {
191- LOGGER .warn ("No output stream found for target {}" , target );
192- }
170+ OutputStream stream = direct
171+ ? getDirectOutputStream (target )
172+ : follow ? getFollowOutputStream (target ) : getDefaultOutputStream (target );
193173
194174 final String managerName = target .name () + '.' + follow + '.' + direct ;
195175 final OutputStreamManager manager =
@@ -207,6 +187,83 @@ private static OutputStreamManager getDefaultManager(final Layout layout) {
207187 return OutputStreamManager .getManager (managerName , new FactoryData (os , managerName , layout ), factory );
208188 }
209189
190+ private static OutputStream getDefaultOutputStream (Target target ) {
191+ return new CloseShieldOutputStream (target == Target .SYSTEM_OUT ? System .out : System .err );
192+ }
193+
194+ private static OutputStream getDirectOutputStream (Target target ) {
195+ return new CloseShieldOutputStream (
196+ new FileOutputStream (target == Target .SYSTEM_OUT ? FileDescriptor .out : FileDescriptor .err ));
197+ }
198+
199+ private static OutputStream getFollowOutputStream (Target target ) {
200+ return target == Target .SYSTEM_OUT ? new SystemOutStream () : new SystemErrStream ();
201+ }
202+
203+ /**
204+ * An implementation of OutputStream that redirects to the current System.err.
205+ */
206+ private static class SystemErrStream extends OutputStream {
207+ public SystemErrStream () {}
208+
209+ @ Override
210+ public void close () {
211+ // do not close sys err!
212+ }
213+
214+ @ Override
215+ public void flush () {
216+ System .err .flush ();
217+ }
218+
219+ @ Override
220+ public void write (final byte [] b ) throws IOException {
221+ System .err .write (b );
222+ }
223+
224+ @ Override
225+ public void write (final byte [] b , final int off , final int len ) throws IOException {
226+ System .err .write (b , off , len );
227+ }
228+
229+ @ Override
230+ public void write (final int b ) {
231+ System .err .write (b );
232+ }
233+ }
234+
235+ /**
236+ * An implementation of OutputStream that redirects to the current System.out.
237+ */
238+ private static class SystemOutStream extends OutputStream {
239+ public SystemOutStream () {}
240+
241+ @ Override
242+ public void close () {
243+ // do not close sys out!
244+ }
245+
246+ @ Override
247+ public void flush () {
248+ System .out .flush ();
249+ }
250+
251+ @ Override
252+ public void write (final byte [] b ) throws IOException {
253+ System .out .write (b );
254+ }
255+
256+ @ Override
257+ public void write (final byte [] b , final int off , final int len ) throws IOException {
258+ System .out .write (b , off , len );
259+ }
260+
261+ @ Override
262+ public void write (final int b ) throws IOException {
263+ System .out .write (b );
264+ }
265+ }
266+
210267 /**
211268 * Data to pass to factory method.Unable to instantiate
212269 */
@@ -250,23 +307,4 @@ public OutputStreamManager createManager(final String name, final FactoryData da
250307 public Target getTarget () {
251308 return target ;
252309 }
253-
254- /**
255- * Abstracts the various ways `System.out` can be accessed.
256- *
257- * @since 3.0.0
258- */
259- public interface ConsoleStreamSupplier {
260-
261- /**
262- * The Log4j plugin namespace of plugins implementing this interface.
263- */
264- String NAMESPACE = "Console" ;
265-
266- /**
267- * @return Selects the output stream to use or {@code null} in case of error.
268- */
269- @ Nullable
270- OutputStream getOutputStream (boolean follow , boolean direct , Target target , PropertyEnvironment properties );
271- }
272310}
0 commit comments