42、Java开发中的调试与日志记录全解析

Java开发中的调试与日志记录全解析

1. JBuilder调试器入门

JBuilder的调试命令会依据你指定的调试信息启动虚拟机,这些信息会自动添加到命令行中。当你在调试器中运行应用程序时,JBuilder屏幕底部的消息面板左侧会新增一些标签页,各标签页内容如下表所示:
| 标签 | 描述 |
| — | — |
| Console Output | 显示应用服务器的控制台窗口输出。当有新输出出现且选中其他标签页时,此标签页会变绿。 |
| Threads | 显示正在运行的线程以及该线程作用域内的所有局部变量。 |
| Watches | 显示已设置的监视变量的值。 |
| Loaded Classes | 显示应用程序已加载的所有类的列表。 |
| Breakpoints | 显示用户设置的所有断点的详细信息。 |
| Classes With Tracing Disabled | 显示标记为禁用跟踪的类的列表,可用于标记不想意外进入调试的类(如SDK中的类)。 |

JBuilder支持基于IDE的调试器的常见功能,如查看输出控制台、设置监视变量、使用多种标准设置断点(共有五种不同方式)以及查看线程等。这些功能与NetBeans类似,不过用户界面略有不同。例如,两个调试器都允许你将光标悬停在变量上查看其当前值。

2. JBuilder与NetBeans调试器的差异

JBuilder消息面板中的每个标签页(除了控制台标签)都可以分离。你只需右键单击标签,通过弹出菜单将其分离到一个浮动窗口中。当你想同时查看调试器标签维护的两条信息时,这个功能非常实用。比如,你可以同时查看监视变量的值和控制台信息。

JBuilder的线程视图包含了大量关于运行中应用程序的信息。由于Java中的局部变量存储在线程的栈帧中,查看线程的特征也能揭示该线程所拥有的变量信息。线程显示在左侧面板,每个线程引用的变量显示在右侧。右侧的对象引用以大纲形式呈现,你可以展开引用以显示变量的值。与NetBeans不同,JBuilder不仅会显示对象引用的内部ID,还会直接显示对象的详细值,这种视图能将大量信息整合在一个小区域内,非常实用。

3. JBuilder的其他实用视图
  • 加载类视图 :该视图以包为分类,展示应用程序加载的所有类的大纲。每个类视图会显示该类中定义的类级变量的值,对于调试动态类加载特别有用,尤其是在使用工厂类加载其他类时。
  • 评估和修改对话框 :这可能是JBuilder最强大的调试视图,你可以通过运行菜单中的“评估/修改”选项打开它。该对话框允许你在调试器停在断点时评估变量和表达式。它支持Code Insight功能,当你在顶部的表达式文本框中输入表达式时,会提供可用方法的列表。表达式文本框可以接受任意复杂的Java表达式,借助Code Insight,你可以在调试会话中构建复杂的表达式,动态调查应用程序的上下文。
4. 调试器评估

在开发过程中,特别是在Web应用领域,调试能力对项目的成功至关重要。选择调试器时,有几个因素会影响你的决策:
- 免费调试器 :你可以选择SDK调试器或开源调试器。SDK调试器适合喜欢使用命令行的开发者,虽然能获取有用信息,但比基于IDE的调试器更耗费精力。不过,熟练使用SDK调试器很重要,因为它是始终可用的调试器,在需要对远离你所选IDE的服务器上运行的已部署应用进行快速调试时,可能是你唯一的选择。
- 开源调试器 :开源调试器是不错的选择。虽然将项目设置为在IDE中运行需要一些努力,但这是一次性操作,使用调试器节省的时间会远超设置的时间。如今Java调试器普遍有了显著改进,很少有功能严重不足的调试器。
- 商业IDE :商业IDE在生产力和灵活性方面表现出色。它们比开源或免费的调试器更完善,通常更容易设置和运行。优秀的商业IDE设计精良,具有极高的灵活性。几乎每个商业IDE在调试方面都比开源调试器更出色。例如,JBuilder提供了整合有价值信息组合的视图。虽然购买商业IDE需要额外费用,但考虑到开发者时间的价值,这是值得的。因为支付开发者工资成本很高,如果能提供工具使他们提高生产力(即使只提高10%),最终也会有回报。商业IDE的初始成本与支付开发者数月甚至数年的工资相比微不足道。

下面是选择调试器的决策流程图:

graph LR
    A[选择调试器] --> B{是否考虑成本}
    B -- 是 --> C{是否接受命令行操作}
    C -- 是 --> D[SDK调试器]
    C -- 否 --> E[开源调试器]
    B -- 否 --> F[商业IDE]
5. 不同框架中的调试

不同的Java框架在调试方面有不同的特点:
| 框架 | 调试特点 |
| — | — |
| Struts | 调试Struts应用与调试其他Model 2应用基本相同,因为该框架相对轻量级,没有额外的调试支持,但也不需要。不过,与所有基于JSP自定义标签的框架一样,调试器无法评估JSP中的自定义标签,单步执行生成的servlet代码也很难获得有价值的信息。 |
| Tapestry | 提供了良好的调试支持,包括专门的工具。浏览器中显示的堆栈跟踪信息非常丰富,包含Tapestry内部消息和完整的Java堆栈跟踪,便于追踪错误。其Inspector工具能轻松发现框架与封装的Web API之间的交互信息。异常页面设计先进,能自动展开嵌套异常。由于Tapestry不使用JSP,因此调试JSP很容易,无需调试自定义标签。 |
| WebWork | 没有特定的调试支持。如果能提供类似Tapestry的Inspector工具来查看值栈会很有用,因为WebWork的很多行为都围绕值栈。该框架大量使用自定义JSP标签,给调试带来困难,尤其是自定义标签与内置数据结构交互频繁时。 |
| InternetBeans Express | 通常使用JBuilder的调试器进行调试。该框架提供的堆栈跟踪和其他信息详细且切中要点。由于其事件驱动的性质和对回发servlet的依赖,调试有点棘手,但这更多是框架本身的特性,而非调试的问题。 |
| Velocity | 调试Velocity的最大问题是缺乏针对Velocity模板语言的交互式调试器,只能采用迭代的“查找并修复”方法进行调试。虽然可以在Velocity servlet中设置断点,但无法访问模板语言。不过,Velocity框架并不复杂,需要调试的内容不多,简单性是其优点。 |
| Cocoon | 调试Cocoon很困难,因为它是一个两用框架,既是发布引擎又是Web框架。错误消息不直观,很难确定消息是与发布方面还是框架方面相关。Cocoon依赖复杂的站点地图配置文件,对错误容忍度低,出错时生成的消息也不直观。 |

6. 日志记录与调试的关系

在没有IDE的时代,日志记录是对抗错误的关键手段之一。Brian Kernighan和Rob Pike在《The Practice of Programming》一书中提到:“我们个人倾向于仅使用调试器获取堆栈跟踪或一两个变量的值。原因之一是在复杂的数据结构和控制流细节中容易迷失方向;我们发现单步执行程序不如更深入地思考,在关键位置添加输出语句和自我检查代码更有效。逐行点击执行比查看精心放置的显示输出花费的时间更长。即使知道关键代码段的位置,决定在哪里放置打印语句也比单步执行到该位置花费的时间少。更重要的是,调试语句会保留在程序中,而调试会话是短暂的。”

Java中的日志记录最初因开源的log4j包而受到关注,该包托管在Jakarta网站上。随着1.4 SDK的发布,日志记录现在已成为Java核心运行时环境(JRE)的一部分。下面我们来了解日志记录的一些通用概念。

7. 通用日志记录概念
  • 日志记录器层次结构 :在使用日志记录包时,理解日志记录器的层次结构很重要。应用程序中通常有多个日志记录任务,创建日志记录器层次结构可以让你为每个特定任务创建不同的日志记录器。SDK日志记录和log4j都使用工厂设计模式来提供日志记录器对象。创建日志记录器时会为其实例关联一个名称,例如创建名为 com.nealford.art.db 的日志记录器专门处理数据库方法,创建名为 com.nealford.art.web 的日志记录器处理应用程序的Web方面。这些日志记录器即使出现在同一个类中也不会相互干扰。

Java类本身有明确的层次结构,因此可以使用类的全限定名(包括包名)来定义日志记录器的层次结构。每个日志记录器会跟踪其父日志记录器(命名空间层次结构中高一级的日志记录器)。例如,如果有一个名为 com.nealford.art.emotherearth.logging.boundary 的日志记录器,其父日志记录器是 com.nealford.art.emotherearth.logging 。对父日志记录器的更改会向下传播到子日志记录器。如果设置父日志记录器的日志级别为 Level.SEVERE ,子日志记录器如果没有自己的设置,将自动采用该设置。不过,任何子日志记录器都可以通过设置自己的属性值来覆盖从父日志记录器继承的设置。理解日志记录器层次结构后,你会发现它是一个强大的机制,只需对高层次的日志记录器属性进行少量更改,就能控制大部分日志记录行为。

  • 日志级别 :在调试应用程序时,你希望获取尽可能多的关于对象和事件的信息。但应用程序调试完成并投入生产后,你只希望在出现关键错误和情况时得到通知。为了解决这个问题,现代日志记录器定义了日志级别。SDK和log4j的日志激活都封装在 Level 类中,虽然 Level 类的实现有所不同,但语义基本相同。SDK定义的日志级别如下表所示:
    | 级别 | 描述 |
    | — | — |
    | ALL | 表示应记录所有消息。 |
    | CONFIG | 为静态配置消息提供消息级别。 |
    | FINE | 提供跟踪信息。 |
    | FINER | 提供相当详细的跟踪信息。 |
    | FINEST | 提供高度详细的跟踪信息。 |
    | INFO | 包含信息性消息。 |
    | OFF | 关闭所有日志级别。 |
    | SEVERE | 表示严重失败。 |
    | WARNING | 表示潜在问题。 |

通过合理设置日志级别,你可以在调试和生产环境中灵活控制日志记录的详细程度。

8. 使用SDK日志记录

Java 1.4 SDK 引入了日志记录功能,它成为 Java 核心运行时环境(JRE)的一部分。以下是使用 SDK 日志记录的基本步骤:

步骤 1:获取日志记录器
使用 Logger.getLogger 方法获取日志记录器实例,传入日志记录器的名称。例如:

import java.util.logging.Logger;

public class SDKLoggingExample {
    private static final Logger logger = Logger.getLogger("com.example.myapp");

    public static void main(String[] args) {
        // 后续日志操作
    }
}

步骤 2:设置日志级别
可以通过 setLevel 方法设置日志记录器的日志级别。例如:

import java.util.logging.Level;
import java.util.logging.Logger;

public class SDKLoggingExample {
    private static final Logger logger = Logger.getLogger("com.example.myapp");

    public static void main(String[] args) {
        logger.setLevel(Level.INFO);
        // 后续日志操作
    }
}

步骤 3:记录日志
使用日志记录器的不同方法记录不同级别的日志。例如:

import java.util.logging.Level;
import java.util.logging.Logger;

public class SDKLoggingExample {
    private static final Logger logger = Logger.getLogger("com.example.myapp");

    public static void main(String[] args) {
        logger.setLevel(Level.INFO);
        logger.info("This is an info message.");
        logger.warning("This is a warning message.");
        logger.severe("This is a severe message.");
    }
}
9. 使用 log4j 日志记录

log4j 是一个流行的开源日志记录包,以下是使用 log4j 进行日志记录的基本步骤:

步骤 1:添加依赖
如果你使用 Maven 项目,在 pom.xml 中添加 log4j 依赖:

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

步骤 2:配置 log4j
创建一个 log4j.properties 文件,配置日志记录的输出方式、日志级别等。例如:

log4j.rootLogger=INFO, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

步骤 3:获取日志记录器并记录日志

import org.apache.log4j.Logger;

public class Log4jExample {
    private static final Logger logger = Logger.getLogger(Log4jExample.class);

    public static void main(String[] args) {
        logger.info("This is an info message using log4j.");
        logger.error("This is an error message using log4j.");
    }
}
10. SDK 日志与 log4j 的比较
比较项 SDK 日志 log4j
易用性 集成在 JRE 中,无需额外添加依赖,基本使用简单。 需要添加依赖和配置文件,配置相对复杂,但提供更灵活的配置选项。
功能丰富度 提供基本的日志记录功能和日志级别控制。 功能更丰富,支持多种输出目标(如文件、数据库等),有更强大的日志过滤和格式化功能。
社区支持 作为 Java 标准库的一部分,有官方支持。 有庞大的开源社区支持,有大量的文档和示例。
11. 日志记录最佳实践
  • 合理设置日志级别 :在开发和测试阶段,将日志级别设置为较低级别(如 ALL FINE ),以便获取详细的调试信息。在生产环境中,将日志级别设置为较高级别(如 INFO SEVERE ),避免记录过多无用信息。
  • 使用有意义的日志消息 :日志消息应该清晰、准确地描述发生的事件,便于后续排查问题。例如,不要只记录 “Error occurred”,而是记录具体的错误信息和上下文。
  • 避免在循环中记录大量日志 :在循环中记录日志会导致性能问题,尽量只在必要时记录关键信息。
  • 定期清理日志文件 :日志文件会占用大量磁盘空间,定期清理过期的日志文件可以避免磁盘空间不足。
12. 总结

调试和日志记录是 Java 开发中不可或缺的环节。选择合适的调试器可以提高开发效率,快速定位和解决问题。不同的框架在调试方面有各自的特点,开发者需要根据具体情况选择合适的调试方法。日志记录则可以帮助开发者在不同阶段获取应用程序的运行信息,通过合理设置日志级别和使用日志记录器层次结构,可以更好地控制日志记录的详细程度和范围。无论是使用 SDK 日志还是 log4j,都需要遵循日志记录的最佳实践,以确保日志记录的有效性和性能。

下面是一个总结调试和日志记录流程的 mermaid 流程图:

graph LR
    A[开发过程] --> B{选择调试方法}
    B -- 基于 IDE --> C[使用 JBuilder 等调试器]
    B -- 命令行 --> D[使用 SDK 调试器]
    A --> E{选择日志记录方式}
    E -- SDK 日志 --> F[使用 Java 1.4 SDK 日志功能]
    E -- log4j --> G[使用 log4j 日志包]
    C --> H[调试应用程序]
    D --> H
    F --> I[记录应用程序日志]
    G --> I
    H --> J[定位和解决问题]
    I --> J
    J --> K[优化应用程序]

通过以上的调试和日志记录方法,开发者可以更加高效地开发和维护 Java 应用程序,提高应用程序的稳定性和可靠性。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值