是否有一种使用默认Java库为控制台输出添加时间戳的正确方法?
当我处理个人Java项目时,我喜欢有一种将文本很好地记录到控制台的方法。我通常为此创建一些公共静态类来处理消息的前缀和时间戳,这样我就可以省略System.out.println(“Text”);的用法 这是我不久前编写的一个类,我倾向于在不同的项目上重用它,因为我非常喜欢它格式化文本的方式:是否有一种使用默认Java库为控制台输出添加时间戳的正确方法?,java,performance,logging,Java,Performance,Logging,当我处理个人Java项目时,我喜欢有一种将文本很好地记录到控制台的方法。我通常为此创建一些公共静态类来处理消息的前缀和时间戳,这样我就可以省略System.out.println(“Text”);的用法 这是我不久前编写的一个类,我倾向于在不同的项目上重用它,因为我非常喜欢它格式化文本的方式: import java.text.SimpleDateFormat; import java.util.Date; public class Log { public static final
import java.text.SimpleDateFormat;
import java.util.Date;
public class Log {
public static final int LEVEL_INFO = 0;
public static final int LEVEL_WARNING = 1;
public static final int LEVEL_ERROR = 2;
private static SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
/**
* Used instead of System.out.println for consistent formatting.
*
* @param messageToPrefix Message that you want to send to print.
* @param errorLevel Type of message you want to send.
*/
public static void print(String messageToPrefix, int errorLevel) {
StringBuilder message = new StringBuilder();
message.append("[").append(df.format(new Date())).append("] ");
switch(errorLevel) {
case LEVEL_INFO:
message.append("[Info] ");
break;
case LEVEL_WARNING:
message.append("[Warning] ");
break;
case LEVEL_ERROR:
message.append("[Error] ");
break;
}
message.append(messageToPrefix);
System.out.println(message.toString());
}
}
这一切都如预期的那样有效,但我刚才注意到了一个小问题:这种创建时间戳的方法非常耗费资源!调用它需要我想要记录的代码停止,直到时间戳被创建并写入控制台。这对我来说似乎效率低下(即使我们在这里讨论的是几毫秒)。当我将此日志记录方法的运行时与使用System.currentTimeMillis()的运行时进行比较时,我得出了这个结论。System.currentTimeMillis()并没有将其格式化为一个好的时间戳,但如果这样使用,则以毫秒为单位显示运行时:
public class Log {
private static final long startTime = System.currentTimeMillis();
public static final int LOG_LEVEL_INFO = 0;
public static final int LOG_LEVEL_WARNING = 1;
public static final int LOG_LEVEL_ERROR = 2;
public static final int LOG_LEVEL_UNKNOWN = 3;
/**
* Used instead of System.out.println for consistent formatting.
*
* @param msg Message that you want to send to log.
* @param importance How important is the message?
*/
public static void print(String msg, int importance) {
StringBuilder finalMsg = new StringBuilder();
finalMsg.append("[").append(System.currentTimeMillis() - startTime).append(" ms] ");
switch (importance) {
case 0:
finalMsg.append("[INFO] ");
break;
case 1:
finalMsg.append("[WARNING] ");
break;
case 2:
finalMsg.append("[ERROR] ");
break;
default:
finalMsg.append("[UNKNOWN] ");
break;
}
finalMsg.append(msg);
System.out.println(finalMsg);
}
}
事实证明,这种方式即使是在小规模上也要快得多,比如在大约50秒的运行时间内发送50条消息
这让我思考了几个问题:
有没有更好的方法来创建这样的时间戳?
有没有办法不等待日志代码完成?
为此开始一个线程是个好主意吗?
还是我完全走错了路
我不想使用任何额外的库来保持它的小而简单
任何建议都将不胜感激 编辑:好的,我认为(在遵循一些建议之后)这是一种安全的方式来记录和标记消息。这可能不是最快的方法,但它是安全的使用,并给了我想要的结果
import java.text.SimpleDateFormat;
import java.util.Date;
public class Log {
public static final int LEVEL_INFO = 0;
public static final int LEVEL_WARNING = 1;
public static final int LEVEL_ERROR = 2;
private static final ThreadLocal<SimpleDateFormat> formatter = ThreadLocal.withInitial(() -> new SimpleDateFormat("HH:mm:ss"));
/**
* Used instead of System.out.println for consistent formatting.
*
* @param messageToPrefix Message that you want to send to print.
* @param errorLevel Type of message you want to send.
*/
public static void print(String messageToPrefix, int errorLevel) {
StringBuilder message = new StringBuilder();
message.append("[").append(formatter.get().format(new Date())).append("] ");
switch(errorLevel) {
case LEVEL_INFO:
message.append("[Info] ");
break;
case LEVEL_WARNING:
message.append("[Warning] ");
break;
case LEVEL_ERROR:
message.append("[Error] ");
break;
}
message.append(messageToPrefix);
System.out.println(message.toString());
}
}
import java.text.simpleDataFormat;
导入java.util.Date;
公共类日志{
公共静态最终整数级别_INFO=0;
公共静态最终整数级警告=1;
公共静态最终整数级_错误=2;
私有静态最终ThreadLocal格式化程序=ThreadLocal.withInitial(()->新SimpleDataFormat(“HH:mm:ss”);
/**
*用于替代System.out.println以获得一致的格式。
*
*@param messageToPrefix您要发送打印的消息。
*@param errorLevel您要发送的消息类型。
*/
公共静态无效打印(字符串messageToPrefix,int errorLevel){
StringBuilder消息=新建StringBuilder();
message.append(“[”).append(formatter.get().format(new Date())).append(“]);
开关(错误级别){
案例级别信息:
message.append(“[Info]”);
打破
案例级别警告:
message.append(“[警告]”);
打破
案例级错误:
message.append(“[Error]”);
打破
}
message.append(messageToPrefix);
System.out.println(message.toString());
}
}
我要感谢评论中的人提供的所有建议。使用日志库,例如内置的,也被称为JUL。另一个注释:。虽然修复这个问题可能会进一步增加处理时间。@Andreas我明白了。老实说,我不知道它的存在。我会试试的,谢谢。@Marvin Yikes!感谢您的警告。只有在多线程访问应用程序状态的并发环境中运行应用程序时,线程安全才重要。如果这不是真的,锁定会减慢程序的速度。由于这个原因,一些线程安全类(如StringBuffer)被重新实现(作为StringBuilder)。一些线程安全类,例如BigInteger,附带了可变伴生类(位集),用于特定的性能场景。您编写的类似乎是线程安全的,但可变SimpleDataFormat除外。使用Java日期时间库中的相应类,其对象是不可变的(因此始终是线程安全的),将允许您的程序保持线程安全,而无需锁定。同意scottb。如果可以的话,选择Java日期时间库,放弃那些过时的废话。即使没有Java 8,您也可以,因为有一个后端口使用线程不安全的部分时,请考虑使用<代码>线程本地< /COD> ++。考虑将最后一毫秒与格式化字符串一起存储为单个元素缓存。尽管我相信
java.time
优化程度很高,但即使是在现代图书馆中,这也可能很有用。@maaartinus我明白了。这似乎是一个更好的解决方案。我想我会使用我的原始类,然后使用initialValue方法将SimpleDataFormat放在ThreadLocal对象中。请注意,ThreadLocal.initialValue
在(web服务器)容器中重新部署应用程序时会造成内存泄漏。但你可能没有,所以没关系