| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.cassandra; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.PrintStream; |
| import java.util.Locale; |
| |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import ch.qos.logback.core.status.Status; |
| import ch.qos.logback.core.status.StatusListener; |
| |
| /* |
| * Listen for logback readiness and then redirect stdout/stderr to logback |
| */ |
| public class LogbackStatusListener implements StatusListener |
| { |
| |
| public static final PrintStream originalOut = System.out; |
| |
| public static final PrintStream originalErr = System.err; |
| |
| private boolean hadError = false; |
| |
| private PrintStream replacementOut; |
| |
| private PrintStream replacementErr; |
| |
| @Override |
| public void addStatusEvent(Status s) |
| { |
| if (s.getLevel() != 0 || s.getEffectiveLevel() != 0) |
| hadError = true; |
| |
| if (!hadError && s.getMessage().equals("Registering current configuration as safe fallback point")) |
| { |
| try |
| { |
| installReplacementStreams(); |
| } |
| catch (Exception e) |
| { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| if (s.getMessage().equals("Logback context being closed via shutdown hook")) |
| { |
| if (replacementOut != null) replacementOut.flush(); |
| if (replacementErr != null) replacementErr.flush(); |
| System.setErr(originalErr); |
| System.setOut(originalOut); |
| } |
| } |
| |
| private void installReplacementStreams() throws Exception |
| { |
| Logger stdoutLogger = LoggerFactory.getLogger("stdout"); |
| Logger stderrLogger = LoggerFactory.getLogger("stderr"); |
| |
| replacementOut = wrapLogger(stdoutLogger, originalOut, "sun.stdout.encoding", false); |
| System.setOut(replacementOut); |
| replacementErr = wrapLogger(stderrLogger, originalErr, "sun.stderr.encoding", true); |
| System.setErr(replacementErr); |
| } |
| |
| private static PrintStream wrapLogger(final Logger logger, final PrintStream original, String encodingProperty, boolean error) throws Exception |
| { |
| final String encoding = System.getProperty(encodingProperty); |
| OutputStream os = new OutputStream() |
| { |
| |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| |
| @Override |
| public void write(int b) throws IOException |
| { |
| baos.write(b); |
| } |
| |
| @Override |
| public void write(byte[] b, int offset, int length) |
| { |
| baos.write(b, offset, length); |
| } |
| |
| @Override |
| public void write(byte[] b) |
| { |
| write(b, 0, b.length); |
| } |
| |
| @Override |
| public void flush() throws IOException |
| { |
| try |
| { |
| //Filter out stupid PrintStream empty flushes |
| if (baos.size() == 0) return; |
| |
| //Filter out newlines, log framework provides its own |
| if (baos.size() == 1) |
| { |
| byte[] bytes = baos.toByteArray(); |
| if (bytes[0] == 0xA) |
| return; |
| } |
| |
| //Filter out Windows newline |
| if (baos.size() == 2) |
| { |
| byte[] bytes = baos.toByteArray(); |
| if (bytes[0] == 0xD && bytes[1] == 0xA) |
| return; |
| } |
| |
| String statement; |
| if (encoding != null) |
| statement = new String(baos.toByteArray(), encoding); |
| else |
| statement = new String(baos.toByteArray()); |
| |
| if (error) |
| logger.error(statement); |
| else |
| logger.info(statement); |
| } |
| finally |
| { |
| baos.reset(); |
| } |
| } |
| }; |
| |
| if (encoding != null) |
| return new PrintStream(os, true, encoding); |
| return new PrintStream(os, true) |
| { |
| |
| private long asyncAppenderThreadId = Long.MIN_VALUE; |
| |
| /* |
| * Long and the short of it is that we don't want to serve logback a fake System.out/err. |
| * ConsoleAppender is replaced so it always goes to the real System.out/err, but logback itself |
| * will at times try to log to System.out/err when it has issues. |
| * |
| * Now here is the problem. There is a deadlock if a thread logs to System.out, blocks on the async |
| * appender queue, and the async appender thread tries to log to System.out directly as part of some |
| * internal logback issue. |
| * |
| * So to prevent this we have to exhaustively check before locking in the PrintStream and forward |
| * to real System.out/err if it is the async appender |
| */ |
| private boolean isAsyncAppender() |
| { |
| //Set the thread id based on the name |
| if (asyncAppenderThreadId == Long.MIN_VALUE) |
| asyncAppenderThreadId = Thread.currentThread().getName().equals("AsyncAppender-Worker-ASYNC") ? Thread.currentThread().getId() : asyncAppenderThreadId; |
| if (Thread.currentThread().getId() == asyncAppenderThreadId) |
| original.println("Was in async appender"); |
| return Thread.currentThread().getId() == asyncAppenderThreadId; |
| } |
| |
| @Override |
| public void flush() |
| { |
| if (isAsyncAppender()) |
| original.flush(); |
| else |
| super.flush(); |
| } |
| |
| @Override |
| public void close() |
| { |
| if (isAsyncAppender()) |
| original.close(); |
| else |
| super.flush(); |
| } |
| |
| @Override |
| public void write(int b) |
| { |
| if (isAsyncAppender()) |
| original.write(b); |
| else |
| super.write(b); |
| } |
| |
| @Override |
| public void write(byte[] buf, int off, int len) |
| { |
| if (isAsyncAppender()) |
| original.write(buf, off, len); |
| else |
| super.write(buf, off, len); |
| } |
| |
| @Override |
| public void print(boolean b) |
| { |
| if (isAsyncAppender()) |
| original.print(b); |
| else |
| super.print(b); |
| } |
| |
| @Override |
| public void print(char c) |
| { |
| if (isAsyncAppender()) |
| original.print(c); |
| else |
| super.print(c); |
| } |
| |
| @Override |
| public void print(int i) |
| { |
| if (isAsyncAppender()) |
| original.print(i); |
| else |
| super.print(i); |
| } |
| |
| @Override |
| public void print(long l) |
| { |
| if (isAsyncAppender()) |
| original.print(l); |
| else |
| super.print(l); |
| } |
| |
| @Override |
| public void print(float f) |
| { |
| if (isAsyncAppender()) |
| original.print(f); |
| else |
| super.print(f); |
| } |
| |
| @Override |
| public void print(double d) |
| { |
| if (isAsyncAppender()) |
| original.print(d); |
| else |
| super.print(d); |
| } |
| |
| @Override |
| public void print(char[] s) |
| { |
| if(isAsyncAppender()) |
| original.println(s); |
| else |
| super.print(s); |
| } |
| |
| @Override |
| public void print(String s) |
| { |
| if (isAsyncAppender()) |
| original.print(s); |
| else |
| super.print(s); |
| } |
| |
| @Override |
| public void print(Object obj) |
| { |
| if (isAsyncAppender()) |
| original.print(obj); |
| else |
| super.print(obj); |
| } |
| |
| @Override |
| public void println() |
| { |
| if (isAsyncAppender()) |
| original.println(); |
| else |
| super.println(); |
| } |
| |
| @Override |
| public void println(boolean v) |
| { |
| if (isAsyncAppender()) |
| original.println(v); |
| else |
| super.println(v); |
| } |
| |
| @Override |
| public void println(char v) |
| { |
| if (isAsyncAppender()) |
| original.println(v); |
| else |
| super.println(v); |
| } |
| |
| @Override |
| public void println(int v) |
| { |
| if (isAsyncAppender()) |
| original.println(v); |
| else |
| super.println(v); |
| } |
| |
| @Override |
| public void println(long v) |
| { |
| if (isAsyncAppender()) |
| original.println(v); |
| else |
| super.println(v); |
| } |
| |
| @Override |
| public void println(float v) |
| { |
| if (isAsyncAppender()) |
| original.println(v); |
| else |
| super.println(v); |
| } |
| |
| @Override |
| public void println(double v) |
| { |
| if (isAsyncAppender()) |
| original.println(v); |
| else |
| super.println(v); |
| } |
| |
| @Override |
| public void println(char[] v) |
| { |
| if (isAsyncAppender()) |
| original.println(v); |
| else |
| super.println(v); |
| } |
| |
| @Override |
| public void println(String v) |
| { |
| if (isAsyncAppender()) |
| original.println(v); |
| else |
| super.println(v); |
| } |
| |
| @Override |
| public void println(Object v) |
| { |
| if (isAsyncAppender()) |
| original.println(v); |
| else |
| super.println(v); |
| } |
| |
| @Override |
| public PrintStream printf(String format, Object... args) |
| { |
| if (isAsyncAppender()) |
| return original.printf(format, args); |
| else |
| return super.printf(format, args); |
| } |
| |
| @Override |
| public PrintStream printf(Locale l, String format, Object... args) |
| { |
| if (isAsyncAppender()) |
| return original.printf(l, format, args); |
| else |
| return super.printf(l, format, args); |
| } |
| |
| @Override |
| public PrintStream format(String format, Object... args) |
| { |
| if (isAsyncAppender()) |
| return original.format(format, args); |
| else |
| return super.format(format, args); |
| } |
| |
| @Override |
| public PrintStream format(Locale l, String format, Object... args) |
| { |
| if (isAsyncAppender()) |
| return original.format(l, format, args); |
| else |
| return super.format(l, format, args); |
| } |
| |
| @Override |
| public PrintStream append(CharSequence csq) |
| { |
| if (isAsyncAppender()) |
| return original.append(csq); |
| else |
| return super.append(csq); |
| } |
| |
| @Override |
| public PrintStream append(CharSequence csq, int start, int end) |
| { |
| if (isAsyncAppender()) |
| return original.append(csq, start, end); |
| else |
| return super.append(csq, start, end); |
| } |
| |
| @Override |
| public PrintStream append(char c) |
| { |
| if (isAsyncAppender()) |
| return original.append(c); |
| else |
| return super.append(c); |
| } |
| }; |
| } |
| } |