/* * This file is part of FTB Launcher. * * Copyright © 2012-2016, FTB Launcher Contributors <https://github.com/Slowpoke101/FTBLaunch/> * FTB Launcher is licensed 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 net.ftb.log; import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.PrintStream; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.CopyOnWriteArrayList; public class Logger { private static final List<ILogListener> listeners = new CopyOnWriteArrayList<ILogListener>(); private static final ConcurrentIterable<LogEntry> logEntries = new ConcurrentIterable<LogEntry>(); private static LogThread logThread = new LogThread(listeners); private static PrintStream standardErrorPrintStream = new PrintStream(new FileOutputStream(FileDescriptor.err)); /** * Default constructor * creates lists for listeners and log messages, creates and starts log dispather thread */ static { logThread.start(); } public static void log (LogEntry entry) { logEntries.add(entry); logThread.handleLog(entry); } public static void log (String message, LogLevel level, Throwable t) { log(new LogEntry().level(level).message(message).cause(t)); } public static void logDebug (String message) { logDebug(message, null); } public static void logInfo (String message) { logInfo(message, null); } public static void logWarn (String message) { logWarn(message, null); } public static void logError (String message) { logError(message, null); } public static void logDebug (String message, Throwable t) { log(message, LogLevel.DEBUG, t); } public static void logInfo (String message, Throwable t) { log(message, LogLevel.INFO, t); } public static void logWarn (String message, Throwable t) { log(message, LogLevel.WARN, t); } public static void logError (String message, Throwable t) { log(message, LogLevel.ERROR, t); } /** * Used to log an error which occurs while logging. Logs directly to standard error * Use only in cases where logging properly could cause recursive errors, for example * an error writing to a log file -> log -> tries to write to log file */ public static void logLoggingError (String error, Throwable t) { if (error != null) { standardErrorPrintStream.append(error).append('\n'); } if (t != null) { t.printStackTrace(standardErrorPrintStream); } standardErrorPrintStream.flush(); } public static void addListener (ILogListener listener) { listeners.add(listener); } public static void removeListener (ILogListener listener) { listeners.remove(listener); } public static Iterable<LogEntry> getLogEntries () { return logEntries; } public static String getLogs () { return getLogs(LogType.EXTENDED); } private static String getLogs (LogType type) { StringBuilder logStringBuilder = new StringBuilder(); for(LogEntry entry : getLogEntries()) { logStringBuilder.append(entry.toString(type)).append("\n"); } return logStringBuilder.toString(); } /** * Simple iterable data structure which can be iterated while being modified, * and is backed by an array. More compact than a ConcurrentLinkedQueue */ private static class ConcurrentIterable<T> implements Iterable<T> { int length = 0; private Object[] entries = new Object[0]; @Override public Iterator<T> iterator () { return new Iterator<T>() { int position = 0; Object next = getNext(); @Override public boolean hasNext () { return next != null; } @Override public T next () { Object current = next; if (current == null) { throw new NoSuchElementException(); } next = getNext(); return (T)current; } @Override public void remove () { throw new UnsupportedOperationException("remove"); } private Object getNext () { Object[] currentEntries = entries; int currentLength = length; if (position >= currentLength || position >= currentEntries.length) { return null; } return currentEntries[position++]; } }; } public void add (T entry) { synchronized (this) { if (entries.length == length) { entries = Arrays.copyOf(entries, entries.length == 0 ? 64 : (entries.length + (entries.length >> 1))); } entries[length++] = entry; } } } }