package org.telegram.services; import org.telegram.BuildVars; import javax.validation.constraints.NotNull; import java.io.*; import java.time.LocalDateTime; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.logging.ConsoleHandler; import java.util.logging.Level; import java.util.logging.Logger; /** * @author Ruben Bermudez * @version 2.0 * @brief Logger to file * @date 21/01/15 */ public class BotLogger { private static final Object lockToWrite = new Object(); private static volatile PrintWriter logginFile; private static volatile String currentFileName; private static final Logger logger = Logger.getLogger("Tsupport Bot"); private static volatile LocalDateTime lastFileDate; private static LoggerThread loggerThread = new LoggerThread(); private static final ConcurrentLinkedQueue logsToFile = new ConcurrentLinkedQueue<>(); static { logger.setLevel(Level.ALL); logger.addHandler(new ConsoleHandler()); loggerThread.start(); lastFileDate = LocalDateTime.now(); if ((currentFileName == null) || (currentFileName.compareTo("") == 0)) { currentFileName = dateFormatterForFileName(lastFileDate) + ".log"; try { final File file = new File(currentFileName); if (file.exists()) { logginFile = new PrintWriter(new BufferedWriter(new FileWriter(currentFileName, true))); } else { final boolean created = file.createNewFile(); if (created) { logginFile = new PrintWriter(new BufferedWriter(new FileWriter(currentFileName, true))); } else { throw new NullPointerException("File for logging error"); } } } catch (IOException e) { e.printStackTrace(); } } } public static void log(@NotNull Level level, String tag, String msg) { logger.log(level, String.format("[%s] %s", tag, msg)); logToFile(level, tag, msg); } public static void severe(String tag, String msg) { logger.severe(String.format("[%s] %s", tag, msg)); logToFile(Level.SEVERE, tag, msg); } public static void warn(String tag, String msg) { warning(tag, msg); } public static void debug(String tag, String msg) { fine(tag, msg); } public static void error(String tag, String msg) { severe(tag, msg); } public static void trace(String tag, String msg) { finer(tag, msg); } public static void warning(String tag, String msg) { logger.warning(String.format("[%s] %s", tag, msg)); logToFile(Level.WARNING, tag, msg); } public static void info(String tag, String msg) { logger.info(String.format("[%s] %s", tag, msg)); logToFile(Level.INFO, tag, msg); } public static void config(String tag, String msg) { logger.config(String.format("[%s] %s", tag, msg)); logToFile(Level.CONFIG, tag, msg); } public static void fine(String tag, String msg) { logger.fine(String.format("[%s] %s", tag, msg)); logToFile(Level.FINE, tag, msg); } public static void finer(String tag, String msg) { logger.finer(String.format("[%s] %s", tag, msg)); logToFile(Level.FINER, tag, msg); } public static void finest(String tag, String msg) { logger.finest(String.format("[%s] %s", tag, msg)); logToFile(Level.FINEST, tag, msg); } public static void log(@NotNull Level level, @NotNull String tag, @NotNull Throwable throwable) { logger.log(level, String.format("[%s] Exception", tag), throwable); logToFile(level, tag, throwable); } public static void log(@NotNull Level level, @NotNull String tag, @NotNull String msg, @NotNull Throwable thrown) { logger.log(level, msg, thrown); logToFile(level, msg, thrown); } public static void severe(@NotNull String tag, @NotNull Throwable throwable) { logToFile(Level.SEVERE, tag, throwable); } public static void warning(@NotNull String tag, @NotNull Throwable throwable) { logToFile(Level.WARNING, tag, throwable); } public static void info(@NotNull String tag, @NotNull Throwable throwable) { logToFile(Level.INFO, tag, throwable); } public static void config(@NotNull String tag, @NotNull Throwable throwable) { logToFile(Level.CONFIG, tag, throwable); } public static void fine(@NotNull String tag, @NotNull Throwable throwable) { logToFile(Level.FINE, tag, throwable); } public static void finer(@NotNull String tag, @NotNull Throwable throwable) { logToFile(Level.FINER, tag, throwable); } public static void finest(@NotNull String tag, @NotNull Throwable throwable) { logToFile(Level.FINEST, tag, throwable); } public static void warn(@NotNull String tag, Throwable throwable) { warning(tag, throwable); } public static void debug(@NotNull String tag, Throwable throwable) { fine(tag, throwable); } public static void error(@NotNull String tag, Throwable throwable) { severe(tag, throwable); } public static void trace(@NotNull String tag, Throwable throwable) { finer(tag, throwable); } public static void severe(@NotNull String msg, @NotNull String tag, @NotNull Throwable throwable) { log(Level.SEVERE, tag, msg, throwable); } public static void warning(@NotNull String msg, @NotNull String tag, @NotNull Throwable throwable) { log(Level.WARNING, tag, msg, throwable); } public static void info(@NotNull String msg, @NotNull String tag, @NotNull Throwable throwable) { log(Level.INFO, tag, msg, throwable); } public static void config(@NotNull String msg, @NotNull String tag, @NotNull Throwable throwable) { log(Level.CONFIG, tag, msg, throwable); } public static void fine(@NotNull String msg, @NotNull String tag, @NotNull Throwable throwable) { log(Level.FINE, tag, msg, throwable); } public static void finer(@NotNull String msg, @NotNull String tag, @NotNull Throwable throwable) { log(Level.FINER, tag, msg, throwable); } public static void finest(@NotNull String msg, @NotNull String tag, @NotNull Throwable throwable) { log(Level.FINEST, msg, throwable); } public static void warn(@NotNull String msg, @NotNull String tag, @NotNull Throwable throwable) { log(Level.WARNING, tag, msg, throwable); } public static void debug(@NotNull String msg, @NotNull String tag, @NotNull Throwable throwable) { log(Level.FINE, tag, msg, throwable); } public static void error(@NotNull String msg, @NotNull String tag, @NotNull Throwable throwable) { log(Level.SEVERE, tag, msg, throwable); } public static void trace(@NotNull String msg, @NotNull String tag, @NotNull Throwable throwable) { log(Level.FINER, tag, msg, throwable); } private static boolean isCurrentDate(LocalDateTime dateTime) { return dateTime.toLocalDate().isEqual(lastFileDate.toLocalDate()); } private static String dateFormatterForFileName(@NotNull LocalDateTime dateTime) { String dateString = ""; dateString += dateTime.getDayOfMonth(); dateString += dateTime.getMonthValue(); dateString += dateTime.getYear(); return dateString; } private static String dateFormatterForLogs(@NotNull LocalDateTime dateTime) { String dateString = "["; dateString += dateTime.getDayOfMonth() + "_"; dateString += dateTime.getMonthValue() + "_"; dateString += dateTime.getYear() + "_"; dateString += dateTime.getHour() + ":"; dateString += dateTime.getMinute() + ":"; dateString += dateTime.getSecond(); dateString += "] "; return dateString; } private static void updateAndCreateFile(LocalDateTime dateTime) { if (!isCurrentDate(dateTime)) { lastFileDate = LocalDateTime.now(); currentFileName = BuildVars.pathToLogs + dateFormatterForFileName(lastFileDate) + ".log"; try { logginFile.flush(); logginFile.close(); final File file = new File(currentFileName); if (file.exists()) { logginFile = new PrintWriter(new BufferedWriter(new FileWriter(currentFileName, true))); } else { final boolean created = file.createNewFile(); if (created) { logginFile = new PrintWriter(new BufferedWriter(new FileWriter(currentFileName, true))); } else { throw new NullPointerException("Error updating log file"); } } } catch (IOException ignored) { } } } private static void logToFile(@NotNull Level level, @NotNull String tag, @NotNull Throwable throwable) { if (isLoggable(level)) { synchronized (lockToWrite) { final LocalDateTime currentDate = LocalDateTime.now(); final String dateForLog = dateFormatterForLogs(currentDate); updateAndCreateFile(currentDate); logThrowableToFile(level, tag, throwable, dateForLog); } } } private static void logToFile(@NotNull Level level, @NotNull String tag, @NotNull String msg) { if (isLoggable(level)) { synchronized (lockToWrite) { final LocalDateTime currentDate = LocalDateTime.now(); updateAndCreateFile(currentDate); final String dateForLog = dateFormatterForLogs(currentDate); logMsgToFile(level, tag, msg, dateForLog); } } } private static void logToFile(Level level, @NotNull String tag, @NotNull String msg, @NotNull Throwable throwable) { if (isLoggable(level)) { synchronized (lockToWrite) { final LocalDateTime currentDate = LocalDateTime.now(); updateAndCreateFile(currentDate); final String dateForLog = dateFormatterForLogs(currentDate); logMsgToFile(level, tag, msg, dateForLog); logThrowableToFile(level, tag, throwable, dateForLog); } } } private static void logMsgToFile(@NotNull Level level, @NotNull String tag, @NotNull String msg, @NotNull String dateForLog) { final String logMessage = String.format("%s{%s} %s - %s", dateForLog, level.toString(), tag, msg); logsToFile.add(logMessage); synchronized (logsToFile) { logsToFile.notifyAll(); } } private static void logThrowableToFile(@NotNull Level level, @NotNull String tag, @NotNull Throwable throwable, @NotNull String dateForLog) { String throwableLog = String.format("%s{%s} %s - %s", dateForLog, level.toString(), tag, throwable.toString()); for (StackTraceElement element : throwable.getStackTrace()) { throwableLog += "\tat " + element + "\n"; } logsToFile.add(throwableLog); synchronized (logsToFile) { logsToFile.notifyAll(); } } private static boolean isLoggable(Level level) { return logger.isLoggable(level) && BuildVars.debug; } private static class LoggerThread extends Thread { @Override public void run() { while(true) { final ConcurrentLinkedQueue stringsToLog = new ConcurrentLinkedQueue<>(); synchronized (logsToFile) { if (logsToFile.isEmpty()) { try { logsToFile.wait(); } catch (InterruptedException e) { return; } if (logsToFile.isEmpty()) { continue; } } stringsToLog.addAll(logsToFile); logsToFile.clear(); } stringsToLog.stream().forEach(logginFile::println); logginFile.flush(); } } } }