/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see .
*/
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see .
*
*/
package com.itsaky.androidide.utils;
import static com.itsaky.androidide.utils.LogUtils.preProcessLogTag;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.WeakHashMap;
/**
* Logger for the IDE.
*
*
If a {@link Throwable} is passed to any of the logging methods, whole stack trace of the
* throwable is printed. Any modifications to this class will affect every log message in the IDE.
*
* @author Akash Yadav
*/
public abstract class ILogger {
public static final String DEFAULT_TAG = "AndroidIDE";
public static final String MSG_SEPARATOR = " "; // Separate messages with a space.
private static final List logListeners = new ArrayList<>();
private static final Map cachedLoggers = new WeakHashMap<>();
private static ILogger instance;
protected final String TAG;
protected ILogger(String tag) {
TAG = preProcessLogTag(tag);
}
public static ILogger instance() {
return cachedLoggers.computeIfAbsent(DEFAULT_TAG, ILogger::createInstance);
}
private static ILogger createInstance(String tag) {
return cachedLoggers.computeIfAbsent(tag, ILogger::newPlatformDependentLogger);
}
private static ILogger newPlatformDependentLogger(String tag) {
return LogUtils.isJvm() ? new JvmLogger(tag) : new AndroidLogger(tag);
}
public static void addLogListener(LogListener listener) {
logListeners.add(Objects.requireNonNull(listener));
}
public static void removeLogListener(LogListener listener) {
logListeners.remove(Objects.requireNonNull(listener));
}
public static ILogger newInstance(String tag) {
return createInstance(tag);
}
public static Priority priority(char priorityChar) {
for (var priority : Priority.values()) {
if (priorityChar(priority) == priorityChar) {
return priority;
}
}
throw new IllegalArgumentException("Invalid priority character: " + priorityChar);
}
public static char priorityChar(Priority priority) {
return Character.toUpperCase(priorityText(priority).charAt(0));
}
public static String priorityText(Priority priority) {
return priority.name();
}
/**
* Log error messages.
*
* @param messages The messages to log.
* @return This logger instance.
*/
public ILogger error(Object... messages) {
return log(Priority.ERROR, messages);
}
/**
* Log messages with the given priority.
*
* @param priority The priority of the log messages.
* @param messages The messages to log.
* @return This logger instance.
*/
public ILogger log(Priority priority, Object... messages) {
logAndNotify(priority, generateMessage(messages));
return this;
}
private void logAndNotify(Priority priority, String msg) {
doLog(priority, msg);
for (final var listener : logListeners) {
listener.log(priority, TAG, msg);
}
}
/**
* Log the message to an appropriate stream where the user can see the log messages.
*
* @param priority The priority for this log message.
* @param message The full generated message for this log. Might contain new lines.
* @see ILogger.Priority#DEBUG
* @see ILogger.Priority#ERROR
* @see ILogger.Priority#WARNING
* @see ILogger.Priority#VERBOSE
* @see ILogger.Priority#INFO
*/
protected abstract void doLog(Priority priority, String message);
protected String generateMessage(Object... messages) {
StringBuilder sb = new StringBuilder();
if (messages == null) {
return "null";
}
for (Object msg : messages) {
sb.append(msg instanceof Throwable ? "\n" : MSG_SEPARATOR);
sb.append(msg instanceof Throwable ? LogUtils.getFullStackTrace(((Throwable) msg)) : msg);
sb.append(msg instanceof Throwable ? "\n" : MSG_SEPARATOR);
}
return sb.toString();
}
/**
* Log warning messages.
*
* @param messages The messages to log.
* @return This logger instance.
*/
public ILogger warn(Object... messages) {
return log(Priority.WARNING, messages);
}
/**
* Log verbose messages.
*
* @param messages The messages to log.
* @return This logger instance.
*/
public ILogger verbose(Object... messages) {
return log(Priority.VERBOSE, messages);
}
/**
* Log information messages.
*
* @param messages The messages to log.
* @return This logger instance.
*/
public ILogger info(Object... messages) {
return log(Priority.INFO, messages);
}
/** Logs the name of method and class which calls this method. */
public void logThis() {
debug(getCallerClassDescription());
}
/**
* Log debug messages.
*
* @param messages The messages to log.
* @return This logger instance.
*/
public ILogger debug(Object... messages) {
return log(Priority.DEBUG, messages);
}
protected String getCallerClassDescription() {
final var elements = Thread.currentThread().getStackTrace();
for (int i = 1, elementsLength = elements.length; i < elementsLength; i++) {
final var element = elements[i];
final var klass = element.getClassName();
final var method = element.getMethodName();
if (ILogger.class.getName().equals(klass) || klass.contains("java.lang.Thread")) {
continue;
}
return String.format("%s [%s]", method, klass);
}
return " ";
}
/** Logging priority. */
public enum Priority {
DEBUG,
WARNING,
ERROR,
INFO,
VERBOSE
}
/** A listener which can be used to listen to log events. */
public interface LogListener {
void log(Priority priority, String tag, String message);
}
}