我们正在开发一个Web应用程序,并且我正在服务器端实现事件日志记录。我决定一种很好的方法是扩展Java日志API以将事件记录到CSV文件。我创建了一个position: absolute;
top: 0;
left: 0;
right: 0;
类,如下所示:
java.util.logging.Formatter
}
实现getHead()方法的想法是为每个CSV文件提供列标题。请注意,Java自己的XMLFormatter在其public class EventLogCsvFormatter extends Formatter {
private static final SimpleDateFormat SDF = new SimpleDateFormat("MM/dd/yyyy h:mm a");
private static final String[] COL_HEADERS = {"Date/Time", "Source", "Event Type", "Application Context", "Description", "Fields"};
public EventLogCsvFormatter() {
}
@Override
public String getHead(Handler h) {
return String.join(",", COL_HEADERS);
}
@Override
public String format(LogRecord record) {
if (record instanceof EventLogRecord) {
EventLogRecord customLogRecord = (EventLogRecord) record;
String[] fields = customLogRecord.getFields();
List<String> textList = new ArrayList<>();
textList.add(SDF.format(new Date(record.getMillis())));
textList.add(customLogRecord.getSource() != null ? customLogRecord.getSource() : "Not Given");
textList.add(customLogRecord.getEventType() != null ? customLogRecord.getEventType().toString() : "Not Given");
textList.add(customLogRecord.getEventContext() != null ? customLogRecord.getEventContext().toString() : "Not Given");
textList.add(customLogRecord.getMessage());
if (fields != null && fields.length > 0) {
for (String field : fields) {
textList.add(field);
}
}
String retVal = "\n" + String.join(",", textList);
return retVal;
}
return "";
}
方法中返回XML文件头字符串时做了类似的事情。事实证明,Java日志记录在调用getHead()时没有考虑append标志。 append标志基本上告诉记录器重新打开现有文件并在启动时继续记录到它。由于我们的测试服务器反弹很多,因此生成的大多数CSV文件都在文件中的多个位置具有列标题名称,而不仅仅是在顶部。
我认为没有任何方法可以解决这个问题,因为几乎所有的Java日志处理程序代码都有私有或包范围的字段和方法。所以,我甚至不能写自己的自定义处理程序来工作。这是一个错误吗?我是SOL?我应该继续使用不同的日志API(例如Log4J)吗?
答案 0 :(得分:2)
与此问题相关的FileHandler RFE为JDK-4629315: Appending of XML Logfiles doesn't merge new records和JDK-5036335: Provide method to obtain log file name(s) and path(s)。
使这项工作的诀窍是,您必须能够在请求标头时查询当前日志文件。如果当前日志文件长度为零,则格式化程序必须返回标题。
在自定义格式化程序中,您可以使用getHead方法尝试找到打开的文件,并使用java.io
或java.nio
查询长度。
@Override
public String getHead(Handler h) {
boolean writeHeader = true;
try {
if (h instanceof FileHandler) {
writeHeader = lengthOpen((FileHandler) h).longValue() == 0L;
}
} catch (SecurityException ignore) {
}
if (writeHeader) {
return ""; //TODO: Insert your CSV headers.
} else {
return super.getHead(h); //Skip headers.
}
}
private Number lengthOpen(Handler h) {
if (h instanceof FileHandler) {
String p = h.getClass().getName();
LogManager manager = LogManager.getLogManager();
p = manager.getProperty(p.concat(".pattern"));
//TODO: Deal with FileHandler patterns.
if (p != null) {
File f = new File(p);
//TODO: Implement file listing and filtering.
return f.length();
}
}
return 0L;
}
否则,如果你想做一些hackery,你可以使用反射。
@Override
public String getHead(Handler h) {
boolean writeHeader = true;
try {
if (h instanceof FileHandler) {
writeHeader = lengthFrom((FileHandler) h).longValue() == 0L;
}
} catch (SecurityException ignore) {
}
if (writeHeader) {
return ""; //TODO: Insert your CSV headers here.
} else {
return super.getHead(h); //Skip headers.
}
}
private Number lengthFrom(FileHandler h) {
try {
Field f = StreamHandler.class.getDeclaredField("output");
f.setAccessible(true);
OutputStream out = (OutputStream) f.get(h);
f = out.getClass().getDeclaredField("written");
f.setAccessible(true);
return (Number) f.get(out);
} catch (ReflectiveOperationException roe) {
h.getErrorManager().error(null, roe, ErrorManager.FORMAT_FAILURE);
}
return 0L;
}