All Downloads are FREE. Search and download functionalities are using the official Maven repository.

framework.Log Maven / Gradle / Ivy

package framework;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.logging.ConsoleHandler;
import java.util.logging.ErrorManager;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

import app.config.Sys;

/**
 * log
 * 
    *
  1. log file by level, date
  2. *
*/ public class Log extends Handler { /** * Class name */ public static final String CLASS_NAME = Log.class.getName(); /** * log formatter */ public static class Formatter extends java.util.logging.Formatter { /** * log format */ protected String format; /** * logger-name editor */ protected Function editor; /** * constructor * * @param format format * @param editor logger-name editor */ public Formatter(String format, Function editor) { this.format = format; this.editor = editor; } /* * (non-Javadoc) * * @see java.util.logging.Formatter#format(java.util.logging.LogRecord)
  1. timestamp
  2. method
  3. logger name
  4. level
  5. *
  6. message
  7. exception
  8. request id
  9. session id
  10. application id
  11. remote ip
  12. */ @Override public String format(LogRecord record) { return String.format(format, record.getMillis(), record.getSourceClassName() + '.' + record.getSourceMethodName(), editor .apply(record.getLoggerName()), record.getLevel() .getName(), formatMessage(record), Tool.of(record.getThrown()) .map(t -> Tool.print(t::printStackTrace)) .orElse(""), Request.current() .map(Object::hashCode) .orElse(0), Session.current() .map(Object::hashCode) .orElse(0), Application.current() .map(Object::hashCode) .orElse(0), Request.current() .map(Request::getRemoteIp) .orElse("(local)")); } /** * @param className Class name * @return compact package name */ static String compact(String className) { String[] parts = className.split("[.]"); for (int i = 0; i < parts.length - 1; i++) { String part = parts[i]; if (part.length() > 0) { char c = part.charAt(0); if (Character.isUpperCase(c)) { break; } parts[i] = String.valueOf(c); } } return String.join(".", parts); } } /** * output map */ protected final ConcurrentHashMap outMap; /** * output folder */ protected final String folder; /** * current output file */ protected String file; /** * formatter */ protected final DateTimeFormatter formatter; /** * constructor * * @param folder output folder * @param formatter log file formatter (DateTimeFormatter format, ll replace to log level, able to include folder) */ public Log(String folder, DateTimeFormatter formatter) { outMap = new ConcurrentHashMap<>(); this.folder = folder; this.formatter = formatter; } /* * (non-Javadoc) * * @see java.util.logging.Handler#publish(java.util.logging.LogRecord) */ @Override public void publish(LogRecord record) { if (!isLoggable(record)) { return; } try { LocalDateTime now = LocalDateTime.ofInstant(Instant.ofEpochMilli(record.getMillis()), ZoneId.systemDefault()); String newFile = now.format(formatter); if (!newFile.equals(file)) { close(); file = newFile; } Level level; String realFile; if (file.indexOf("ll") < 0) { level = Level.ALL; realFile = file; } else { level = record.getLevel(); realFile = file.replace("ll", level.getName() .toLowerCase(Locale.ENGLISH)); } String message = getFormatter().format(record); Charset encoding = Tool.of(getEncoding()) .map(Charset::forName) .orElse(Charset.defaultCharset()); FileChannel channel = outMap.computeIfAbsent(level, i -> { FileChannel c = null; try { Path path = Paths.get(folder, realFile); Path parent = path.getParent(); if (Files.notExists(parent)) { Files.createDirectories(parent); } c = FileChannel.open(path, StandardOpenOption.CREATE, StandardOpenOption.APPEND); if (!Sys.Log.is_shared && c.tryLock() == null) { throw new Exception("lock failed: " + path); } System.err.println("log open #" + c.hashCode() + " : " + path); return c; } catch (Exception e) { if (c != null) { Try.r(c::close, ee -> Log.warning(ee, () -> "close error")) .run(); } reportError(null, e, ErrorManager.OPEN_FAILURE); return null; } }); if (channel != null) { channel.write(ByteBuffer.wrap(message.getBytes(encoding))); } } catch (IOException e) { reportError(null, e, ErrorManager.WRITE_FAILURE); close(); } catch (Exception e) { reportError(null, e, ErrorManager.FORMAT_FAILURE); } } /* * (non-Javadoc) * * @see java.util.logging.Handler#flush() */ @Override public void flush() { } /* * (non-Javadoc) * * @see java.util.logging.Handler#close() */ @Override public void close() throws SecurityException { for (Iterator> i = outMap.entrySet() .iterator(); i.hasNext();) { try { Tool.peek(i.next() .getValue(), c -> System.err.println("log close #" + c.hashCode())) .close(); i.remove(); } catch (Exception e) { reportError(null, e, ErrorManager.CLOSE_FAILURE); } } } /** * for initialize */ private static final AtomicBoolean first = new AtomicBoolean(true); /** * log handler */ private static volatile Handler handler; /** * log initialize */ public static void startup() { BiConsumer setup = (handler, level) -> { handler.setLevel(level); handler.setFormatter(new Formatter(Sys.Log.format, Sys.Log.compact_package ? Formatter::compact : Function.identity())); if (!Sys.Log.ignore_prefixes.isEmpty()) { handler.setFilter(r -> r.getThrown() != null || Sys.Log.ignore_prefixes.stream() .noneMatch(r.getLoggerName()::startsWith)); } }; try { Logger root = Logger.getLogger(""); Level level = Sys.Log.level; Level consoleLevel = Sys.Log.console_level.orElse(level); root.setLevel(level.intValue() < consoleLevel.intValue() ? level : consoleLevel); boolean noEntry = true; for (Handler i : root.getHandlers()) { if (i instanceof ConsoleHandler && !(i.getFormatter() instanceof Formatter)) { setup.accept(i, consoleLevel); } if (i instanceof Log) { noEntry = false; } } if (noEntry) { if (first.compareAndSet(true, false)) { handler = new Log(Sys.Log.folder, Sys.Log.file_pattern); setup.accept(handler, level); } root.addHandler(handler); Logger.getLogger(Log.class.getCanonicalName()) .config("addHandler: " + handler); } } catch (Throwable e) { Logger.getGlobal() .log(Level.WARNING, e.getMessage(), e); } } /** * log finalize */ public static void shutdown() { Logger root = Logger.getLogger(""); for (Handler i : root.getHandlers()) { if (i instanceof Log) { Logger.getGlobal() .config("removeHandler: " + i); i.close(); root.removeHandler(i); } } } /** * @param message message */ public static void severe(String message) { log(Level.SEVERE, null, () -> message); } /** * @param message message */ public static void severe(Supplier message) { log(Level.SEVERE, null, message); } /** * @param thrown Throwable associated with log message. * @param message The string message (or a key in the message catalog) */ public static void severe(Throwable thrown, Supplier message) { log(Level.SEVERE, thrown, message); } /** * @param message message */ public static void warning(String message) { log(Level.WARNING, null, () -> message); } /** * @param message message */ public static void warning(Supplier message) { log(Level.WARNING, null, message); } /** * @param thrown Throwable associated with log message. * @param message The string message (or a key in the message catalog) */ public static void warning(Throwable thrown, Supplier message) { log(Level.WARNING, thrown, message); } /** * @param message message */ public static void info(String message) { log(Level.INFO, null, () -> message); } /** * @param message message */ public static void info(Supplier message) { log(Level.INFO, null, message); } /** * @param thrown Throwable associated with log message. * @param message The string message (or a key in the message catalog) */ public static void info(Throwable thrown, Supplier message) { log(Level.INFO, thrown, message); } /** * @param message message */ public static void config(String message) { log(Level.CONFIG, null, () -> message); } /** * @param message message */ public static void config(Supplier message) { log(Level.CONFIG, null, message); } /** * @param thrown Throwable associated with log message. * @param message The string message (or a key in the message catalog) */ public static void config(Throwable thrown, Supplier message) { log(Level.CONFIG, thrown, message); } /** * @param message message */ public static void fine(String message) { log(Level.FINE, null, () -> message); } /** * @param message message */ public static void fine(Supplier message) { log(Level.FINE, null, message); } /** * @param thrown Throwable associated with log message. * @param message The string message (or a key in the message catalog) */ public static void fine(Throwable thrown, Supplier message) { log(Level.FINE, thrown, message); } /** * @param message message */ public static void finer(String message) { log(Level.FINER, null, () -> message); } /** * @param message message */ public static void finer(Supplier message) { log(Level.FINER, null, message); } /** * @param thrown Throwable associated with log message. * @param message The string message (or a key in the message catalog) */ public static void finer(Throwable thrown, Supplier message) { log(Level.FINER, thrown, message); } /** * @param message message */ public static void finest(String message) { log(Level.FINEST, null, () -> message); } /** * @param message message */ public static void finest(Supplier message) { log(Level.FINEST, null, message); } /** * @param thrown Throwable associated with log message. * @param message The string message (or a key in the message catalog) */ public static void finest(Throwable thrown, Supplier message) { log(Level.FINEST, thrown, message); } /** * @param level Log level * @param thrown Throwable associated with log message. * @param message message The string message (or a key in the message catalog) */ public static void log(Level level, Throwable thrown, Supplier message) { log(0, level, thrown, message); } /** * @param skip Skips of stack trace * @param level Log level * @param thrown Throwable associated with log message. * @param message message The string message (or a key in the message catalog) */ public static void log(int skip, Level level, Throwable thrown, Supplier message) { int levelValue = level.intValue(); if (level == Level.OFF) { return; } LogRecord record = new LogRecord(level, message.get()); if (thrown != null) { record.setThrown(thrown); } StackTraceElement[] stackTraces = new Throwable().getStackTrace(); int max = skip; int first = skip; for (int i = 0, i2 = stackTraces.length; i < i2; i++) { String className = stackTraces[i] .getClassName(); if(CLASS_NAME.equals(className) && i + 1 < i2) { first = i; } if (Sys.Log.ignore_prefixes.stream() .anyMatch(className::startsWith)) { max = first + 1; break; } max = i; if (Sys.Log.skip_prefixes.stream() .noneMatch(className::startsWith)) { break; } } StackTraceElement frame = stackTraces[max]; String className = frame.getClassName(); String methodName = frame.getMethodName(); record.setSourceClassName(className); record.setSourceMethodName(methodName); record.setLoggerName(className + "." + methodName + "(" + frame.getLineNumber() + ")"); for (Logger logger = Logger.getGlobal(); logger != null; logger = logger.getParent()) { for (Handler handler : logger.getHandlers()) { if (levelValue >= handler.getLevel() .intValue() && Tool.of(handler.getFilter()) .map(f -> f.isLoggable(record)) .orElse(true)) { handler.publish(record); } } if (!logger.getUseParentHandlers()) { break; } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy