Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
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
*
* log file by level, date
*
*/
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) timestamp method logger name level
* message exception request id session id application id remote ip
*/
@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;
}
}
}
}