使用 databricks 的过程中,发现他将日志分为 log4j、stderr、stdout日志。了解日志是调试程序关键技能。顺着这个思路,我认真学习了一下这几个日志的区别。
这些日志文件有不同的用途和记录内容:
-
log4j-active.log:这是由log4j框架生成的日志文件。log4j是一个用于Java应用程序的日志记录库,能够灵活地控制日志输出的格式和目标(如文件、控制台等)。这个日志文件通常包含应用程序运行时的各种详细信息,包括调试信息、错误消息、警告等。
-
stderr:标准错误输出日志文件。这个文件通常记录程序运行过程中产生的错误信息和异常。这些错误信息对于调试和解决程序问题非常重要。标准错误输出通常用于分离错误消息和正常的程序输出,便于识别和处理。
-
stdout:标准输出日志文件。这个文件记录程序的正常输出信息,比如运行结果、状态信息等。标准输出通常显示程序的正常执行流程和结果,用于监控程序的运行状态。
总结起来:
- log4j-active.log:记录应用程序的详细运行日志,包括调试信息和错误。
- stderr:记录程序运行过程中的错误和异常。
- stdout:记录程序的正常输出信息和运行状态。
当然可以,下面是一些Java代码示例,展示如何使用log4j记录日志,以及如何向标准输出和标准错误输出写信息。
Java 中的日志
目录结构
your-project
│
├── src
│ ├── main
│ │ ├── java
│ │ │ └── Log4jExample.java
│ │ └── resources
│ │ └── log4j.properties
│ └── test
│ ├── java
│ └── resources
│
├── pom.xml
└── ...
使用log4j记录日志
首先,需要在项目中引入log4j依赖(如果使用Maven):
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
然后,确保log4j.properties文件放在项目的src/main/resources目录下。如果这个目录不存在,可以手动创建。配置log4j.properties文件:
# 设置日志级别为DEBUG
log4j.rootLogger=DEBUG, file
# 配置文件输出
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=log4j-active.log
log4j.appender.file.MaxFileSize=5MB
log4j.appender.file.MaxBackupIndex=10
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ISO8601} [%t] %-5p %c - %m%n
最后,编写Java代码使用log4j记录日志:
import org.apache.log4j.Logger;
public class Log4jExample {
private static final Logger logger = Logger.getLogger(Log4jExample.class);
public static void main(String[] args) {
logger.debug("This is a debug message");
logger.info("This is an info message");
logger.warn("This is a warn message");
logger.error("This is an error message");
logger.fatal("This is a fatal message");
}
}
目录结构如下:
运行后日志
向标准输出和标准错误输出写信息
使用Java的System.out和System.err可以分别向标准输出和标准错误输出写信息:
public class StdOutErrExample {
public static void main(String[] args) {
// 向标准输出写信息
System.out.println("This is a standard output message");
// 向标准错误输出写信息
System.err.println("This is a standard error message");
// 模拟一个错误
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.err.println("Caught an exception: " + e.getMessage());
}
}
}
运行结果
-
log4j-active.log 文件将包含:
2024-07-09 10:00:00 [main] DEBUG Log4jExample - This is a debug message 2024-07-09 10:00:00 [main] INFO Log4jExample - This is an info message 2024-07-09 10:00:00 [main] WARN Log4jExample - This is a warn message 2024-07-09 10:00:00 [main] ERROR Log4jExample - This is an error message 2024-07-09 10:00:00 [main] FATAL Log4jExample - This is a fatal message
-
stdout 输出:
This is a standard output message
-
stderr 输出:
This is a standard error message Caught an exception: / by zero
通过这些示例代码,可以看到如何在Java应用程序中使用log4j记录日志,以及如何使用标准输出和标准错误输出来记录不同类型的信息。
以python为例
- log4j-active.log (log4j 输出):
这是使用log4j库记录的应用程序日志。通常用于记录应用程序的运行状态、错误等信息。
import logging
from logging import FileHandler
logger = logging.getLogger('myapp')
logger.setLevel(logging.DEBUG)
handler = FileHandler('log4j-active.log')
logger.addHandler(handler)
logger.info("应用程序启动")
logger.error("发生错误:连接数据库失败")
- stderr (标准错误):
用于输出错误信息和诊断信息。在Python中,可以通过sys.stderr来写入。
import sys
def divide(a, b):
if b == 0:
print("错误:除数不能为零", file=sys.stderr)
return None
return a / b
result = divide(10, 0)
- stdout (标准输出):
用于输出程序的正常信息。在Python中,print()函数默认输出到stdout。
print("这是标准输出信息")
# 也可以显式地使用sys.stdout
import sys
sys.stdout.write("另一种写入标准输出的方式\n")
主要区别:
- log4j-active.log: 专门的日志文件,可配置级别,便于长期存储和分析。
- stderr: 用于即时的错误报告,通常显示在控制台。
- stdout: 用于常规程序输出,也通常显示在控制台。
在实际应用中,stderr和stdout经常被重定向到文件中进行日志记录。log4j提供了更灵活的日志管理功能。
高阶讲解
- Log4j 详解
Log4j 提供了更复杂的日志管理功能,包括不同的日志级别和灵活的输出配置。
import org.apache.log4j.*;
public class AdvancedLog4jExample {
private static final Logger logger = Logger.getLogger(AdvancedLog4jExample.class);
public static void main(String[] args) {
// 配置 Log4j
configureLog4j();
// 使用不同级别的日志
logger.trace("这是一个 TRACE 级别的消息");
logger.debug("这是一个 DEBUG 级别的消息");
logger.info("这是一个 INFO 级别的消息");
logger.warn("这是一个 WARN 级别的消息");
logger.error("这是一个 ERROR 级别的消息");
logger.fatal("这是一个 FATAL 级别的消息");
// 记录异常
try {
throw new RuntimeException("模拟的异常");
} catch (RuntimeException e) {
logger.error("捕获到异常", e);
}
}
private static void configureLog4j() {
// 创建控制台附加器
ConsoleAppender consoleAppender = new ConsoleAppender();
consoleAppender.setLayout(new PatternLayout("%d [%t] %-5p %c - %m%n"));
consoleAppender.setThreshold(Level.INFO);
consoleAppender.activateOptions();
Logger.getRootLogger().addAppender(consoleAppender);
// 创建文件附加器
try {
FileAppender fileAppender = new FileAppender(
new PatternLayout("%d %-5p [%c{1}] %m%n"),
"log4j-active.log",
true);
fileAppender.setThreshold(Level.DEBUG);
Logger.getRootLogger().addAppender(fileAppender);
} catch (Exception e) {
e.printStackTrace();
}
}
}
- System.err 和 System.out 的高级用法
虽然 System.err 和 System.out 相对简单,但它们也有一些高级用法:
import java.io.*;
public class AdvancedSystemIOExample {
public static void main(String[] args) throws IOException {
// 重定向 System.out 到文件
PrintStream outFile = new PrintStream(new FileOutputStream("stdout.log"));
System.setOut(outFile);
// 重定向 System.err 到文件
PrintStream errFile = new PrintStream(new FileOutputStream("stderr.log"));
System.setErr(errFile);
// 现在标准输出会被写入 stdout.log
System.out.println("这条消息会被写入 stdout.log 文件");
// 标准错误会被写入 stderr.log
System.err.println("这条错误消息会被写入 stderr.log 文件");
// 使用自定义的 PrintStream
PrintStream customOut = new PrintStream(System.out) {
@Override
public void println(String x) {
super.println("自定义前缀: " + x);
}
};
System.setOut(customOut);
System.out.println("这条消息会有自定义前缀");
// 恢复原始的标准输出和错误流
System.setOut(System.out);
System.setErr(System.err);
}
}
- 日志使用的最佳实践
- 使用适当的日志级别:TRACE 用于非常详细的信息,DEBUG 用于调试,INFO 用于一般信息,WARN 用于警告,ERROR 用于错误,FATAL 用于严重错误。
- 避免在生产环境中使用 System.out 和 System.err,而应该使用成熟的日志框架。
- 在记录异常时,同时记录异常消息和堆栈跟踪。
- 使用参数化日志消息来提高性能:
logger.debug("User {} logged in from IP {}", username, ipAddress);
- 定期归档和轮转日志文件以管理磁盘空间。
- 在多线程环境中使用线程安全的日志框架(如 Log4j)。
- 日志与应用程序性能
过度的日志记录可能会影响应用程序的性能。应该权衡日志的详细程度和应用程序的性能需求。在性能关键的部分,可以使用条件日志记录:
if (logger.isDebugEnabled()) {
logger.debug("Complex object state: " + expensiveToStringMethod());
}
这样可以避免在日志级别不够的情况下执行昂贵的字符串操作。
总结
-
log4j-active.log (Log4j 输出):
这就像是一本详细的日记本。你可以在里面记录各种重要程度的信息,从小事到大事都有。它很灵活,你可以决定记录什么,怎么记,记到哪里。比如,你可以只记录重要的事,或者把所有细节都记下来。这本日记可以保存很久,方便以后查阅。 -
stderr (标准错误):
想象这是一个紧急警报系统。当出现严重问题时,它会立即发出警报。就像火警警报器,它的目的是立刻引起注意。通常,这些警报会直接显示在屏幕上,让你能立即看到。 -
stdout (标准输出):
这更像是一个普通的广播系统。它用来传达程序正常运行时的信息,就像商场里的广播。它不像警报那么紧急,但仍然是传递信息的重要渠道。
总的来说:
- Log4j 就像一个全面的日志系统,可以记录各种级别的信息,便于长期存储和分析。
- stderr 是用来报告紧急问题的,就像警报器。
- stdout 用于日常的信息输出,像普通广播。
在实际使用中:
- 专业的开发者通常更喜欢用 Log4j 这样的系统,因为它更强大,可以更好地管理所有类型的日志。
- stderr 和 stdout 更简单,常用于快速输出或者在简单的程序中。
记住,好的日志习惯就像是给未来的自己或其他开发者留下清晰的"路标",帮助理解和解决问题。选择合适的日志方式,可以让程序更容易维护和调试。