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.spi;
018
019import org.apache.logging.log4j.Level;
020import org.apache.logging.log4j.Marker;
021import org.apache.logging.log4j.message.Message;
022
023import java.io.ByteArrayOutputStream;
024import java.io.IOException;
025import java.io.PrintStream;
026import java.util.Locale;
027
028/**
029 * Output stream that logs each line written to a pre-defined level. Can also
030 * be configured with a Marker. This class provides an interface that follows
031 * the {@link java.io.PrintStream} methods in spirit, but doesn't output to
032 * any external stream. This class should <em>not</em> be used as a stream for an
033 * underlying logger unless it's being used as a bridge. Otherwise, infinite
034 * loops may occur!
035 */
036public class LoggerStream extends PrintStream {
037
038    final PrintStream stream;
039
040    public LoggerStream(final AbstractLogger logger, final Level level) {
041        super(System.out);
042        stream = new PrintStream(new HelperStream(logger, null, level), true);
043    }
044
045    public LoggerStream(final AbstractLogger logger, final Marker marker, final Level level) {
046        super(System.out);
047        stream = new PrintStream(new HelperStream(logger, marker, level), true);
048    }
049
050    @Override
051    public void write(int b) {
052        stream.write(b);
053    }
054
055    @Override
056    public void write(byte[] b) throws IOException {
057        stream.write(b);
058    }
059
060    @Override
061    public void write(byte[] b, int off, int len) {
062        stream.write(b, off, len);
063    }
064
065    @Override
066    public void flush() {
067        stream.flush();
068    }
069
070    @Override
071    public void close() {
072        stream.close();
073    }
074
075    @Override
076    public void print(boolean b) {
077        stream.print(b);
078    }
079
080    @Override
081    public void print(char c) {
082        stream.print(c);
083    }
084
085    @Override
086    public void print(int i) {
087        stream.print(i);
088    }
089
090    @Override
091    public void print(long l) {
092        stream.print(l);
093    }
094
095    @Override
096    public void print(float f) {
097        stream.print(f);
098    }
099
100    @Override
101    public void print(double d) {
102        stream.print(d);
103    }
104
105    @Override
106    public void print(char[] s) {
107        stream.print(s);
108    }
109
110    @Override
111    public void print(String s) {
112        stream.print(s);
113    }
114
115    @Override
116    public void print(Object obj) {
117        stream.print(obj);
118    }
119
120    @Override
121    public void println() {
122        stream.println();
123    }
124
125    @Override
126    public void println(boolean x) {
127        stream.println(x);
128    }
129
130    @Override
131    public void println(char x) {
132        stream.println(x);
133    }
134
135    @Override
136    public void println(int x) {
137        stream.println(x);
138    }
139
140    @Override
141    public void println(long x) {
142        stream.println(x);
143    }
144
145    @Override
146    public void println(float x) {
147        stream.println(x);
148    }
149
150    @Override
151    public void println(double x) {
152        stream.println(x);
153    }
154
155    @Override
156    public void println(char[] x) {
157        stream.println(x);
158    }
159
160    @Override
161    public void println(String x) {
162        stream.println(x);
163    }
164
165    @Override
166    public void println(Object x) {
167        stream.println(x);
168    }
169
170    @Override
171    public LoggerStream printf(String format, Object... args) {
172        stream.printf(format, args);
173        return this;
174    }
175
176    @Override
177    public LoggerStream printf(Locale l, String format, Object... args) {
178        stream.printf(l, format, args);
179        return this;
180    }
181
182    @Override
183    public LoggerStream append(char c) {
184        stream.append(c);
185        return this;
186    }
187
188    @Override
189    public LoggerStream append(CharSequence csq) {
190        stream.append(csq);
191        return this;
192    }
193
194    @Override
195    public LoggerStream append(CharSequence csq, int start, int end) {
196        stream.append(csq, start, end);
197        return this;
198    }
199
200    @Override
201    public LoggerStream format(String format, Object... args) {
202        stream.format(format, args);
203        return this;
204    }
205
206    @Override
207    public LoggerStream format(Locale l, String format, Object... args) {
208        stream.format(l, format, args);
209        return this;
210    }
211
212    @Override
213    public boolean checkError() {
214        return stream.checkError();
215    }
216
217    @Override
218    public String toString() {
219        return "LoggerStream{" +
220                "stream=" + stream +
221                '}';
222    }
223
224    @Override
225    public boolean equals(Object other) {
226        return this == other
227                || !(other == null || getClass() != other.getClass())
228                && stream.equals(((LoggerStream) other).stream);
229    }
230
231    @Override
232    public int hashCode() {
233        return stream.hashCode();
234    }
235
236    private static class HelperStream extends ByteArrayOutputStream {
237        private static final String FQCN = LoggerStream.class.getName();
238        private final AbstractLogger logger;
239        private final Level level;
240        private final Marker marker;
241
242        private HelperStream(AbstractLogger logger, Marker marker, Level level) {
243            this.logger = logger;
244            this.marker = marker;
245            this.level = level;
246        }
247
248        private void log(int upTo) {
249            if (upTo < 0 || upTo >= count) {
250                throw new IndexOutOfBoundsException();
251            }
252            final Message message = logger.getMessageFactory().newMessage(extractLine(upTo));
253            logger.log(marker, FQCN, level, message, null);
254        }
255
256        private String extractLine(int upTo) {
257            final String line = new String(buf, 0, upTo);
258            leftShiftBuffer(upTo + 1);
259            return line;
260        }
261
262        private void leftShiftBuffer(int numBytes) {
263            int remaining = count - numBytes;
264            if (remaining > 0) {
265                System.arraycopy(buf, numBytes, buf, 0, remaining);
266                count = remaining + 1;
267            } else {
268                reset();
269            }
270        }
271
272        @Override
273        public synchronized void write(int b) {
274            if (b == '\r') {
275                return;
276            }
277            super.write(b);
278            if (b == '\n') {
279                log(count - 1);
280            }
281        }
282
283        @Override
284        public synchronized void write(byte[] b, int off, int len) {
285            for (int i = 0; i < len; ++i) {
286                write(b[off + i]);
287            }
288        }
289    }
290}