11.5.8 日志记录说明
(1) 自定义日志记录器
private static final Logger logger = Logger.getLogger("包名全名");个人见解,习惯就最好,定义一个全局的Logger,然后全局都用一个 Logger 也挺好,方便;方便和准确按习惯选择一种就可以了
(2) 自定义默认配置
加载合适的自定义配置文件,后续更加方便使用
(3) 日志级别定义
对于需要的公共内容,直接写到 INFO/WARNING/SERVERE 级别上,其他程序员需要内容,可放在其他级别,方便正式环境的情况屏蔽他们
package com.dyy.jdk8;import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.util.logging.*;
public class Main {
public static final String myPackage = "com.dyy.jdk8";;
public static void main(String[] args) throws IOException {
Main solution = new Main();
try{
Logger.getLogger(myPackage).setLevel(Level.ALL);
final int LOG_ROTATION_COUNT = 10;
Handler handler = new FileHandler("%h/LoggingImageViewer.log",0,LOG_ROTATION_COUNT);
Logger.getLogger(myPackage).addHandler(handler);
}catch (IOException e){
Logger.getLogger(myPackage).log(Level.SEVERE, "Can't create log file handler",e);
}
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
Handler windowHandler = new WindowHandler();
windowHandler.setLevel(Level.ALL);
Logger.getLogger(myPackage).addHandler(windowHandler);
JFrame frame = new ImageViewFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Logger.getLogger(myPackage).fine("Showing frame");
frame.setVisible(true);
}
});
}
}
class ImageViewFrame extends JFrame {
private JButton button;
private static Logger logger = Logger.getLogger(Main.myPackage);
private static final int W = 600;
private static final int H = 600;
public ImageViewFrame(){
logger.entering("ImageViewerFrame","<init>");
setTitle("LoggingImageViewer");
setSize(W,H);
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu menu = new JMenu("File");
menuBar.add(menu);
JMenuItem openItem = new JMenuItem("Open");
menu.add(openItem);
openItem.addActionListener(new FileOpenListener());
JMenuItem exitItem = new JMenuItem("Exit");
menu.add(exitItem);
exitItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
logger.fine("Exiting.");
System.exit(0);
}
});
button = new JButton();
add(button,BorderLayout.CENTER);
button.setBackground(Color.WHITE);
logger.exiting("ImageViewerFrame","<init>");
}
private class FileOpenListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent event) {
logger.entering("ImageViewFrame.FileOpenListener","actionPerformed",event);
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
chooser.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.getName().toLowerCase().endsWith(".gif") || f.isDirectory();
}
@Override
public String getDescription() {
return "GIF Images";
}
});
int r = chooser.showOpenDialog(ImageViewFrame.this);
if(r == JFileChooser.APPROVE_OPTION){
String name = chooser.getSelectedFile().getPath();
logger.log(Level.FINE, "Reading file {0}",name);
button.setIcon(new ImageIcon(name));
}else{
logger.fine("File open dialog canceled.");
}
logger.exiting("ImageViewerFrame.FileOpenListener", "actionPerformed");
}
}
}
class WindowHandler extends StreamHandler{
public void publish(LogRecord record){
super.publish(record);
flush();
}
}
最终还是用了配置文件,因为不用配置文件显示不出日志(可能作者是改了源文件?)
-Djava.util.logging.config.file=D:\program\test\testJ8\logging.properties操作步骤:
- 点击:File->Open代开GIF源文件
- 关闭GIF,查看日志
11.6 调试技术
调试建议:
(1)使用以下方式记录 变量 值
System.out.println(String.valueOf(a));
或者:
Logger.getGlobal().info(String.valueOf(a));
(个人建议:算法题用第1种,项目用第2种)
(2)需要调试的类中放 main 方法,方便测试和提供 case
个人建议:非 spring boot 可用【挺方便的】; spring boot 慎用,spring boot 多 main 会找不到启动类,或者用完屏蔽掉。
(3)使用 JUnit
这个东西确实挺好用的,适用于大型项目,小型的写写 log, main 足够了。
(4)日志代理
调用父类方法并进行输出
Logger.getGlobal().info(super.xx());
(5) 抛出前使用此方法进行日志输出:t.printStackTrace()
try{}catch(Throwable e){e.printStackTrace();
throw e;
}
非异常行为的堆栈追踪(其实就是建立了一个异常来输出堆栈。。。)
Thread.dumpStack();
(6) 堆栈追踪可以传一个输出的处理流,在期望的位置输出异常
public class Main {public static void main(String[] args) throws IOException {
Main solution = new Main();
int[] arr = new int[3];
try{
for(int i = 0; i <= 3; i++){
arr[i]=i;
}
}catch (Exception e){
e.printStackTrace(System.out);
}
}
}
比如这段代码是改成使用输出流输出异常,此时字是白色的:
(7)使用输出符号执行 java 命令,将执行结果、异常输出到文件
测试代码:
public class Main {public static void main(String[] args) throws IOException {
Main solution = new Main();
int[] arr = new int[3];
try{
for(int i = 0; i <= 3; i++){
arr[i]=i;
}
}catch (Exception e){
e.printStackTrace(System.out);
}
}
}
System.out 输出到文件:
java com.dyy.jdk8.Main > info.txt
System.error 输出到文件:
java com.dyy.jdk8.Main > error.txt
输出:
测试方法1:
java com.dyy.jdk8.Main error.txt 2>&1
此方法仅输出,不会输出到文件(Linux和windows效果相同)
输入到同一个文件:
java com.dyy.jdk8.Main >& error.txt
Linux 成功
Windows 失败
(8)未捕获异常的统一处理
使用:Thread.setDefaultUncaughtExceptionHandler() 捕获所有未捕获异常(会导致程序崩溃的异常)
package com.dyy.jdk8;import com.dyy.jdk8.test.ErrorUtil;
import java.io.*;
import java.util.logging.*;
public class Main {
public static void main(String[] args) throws IOException {
Main solution = new Main();
int[] arr = new int[3];
Logger logger = Logger.getLogger("com.dyy.jdk8");
FileHandler handler
= new FileHandler("logs.txt");
handler.setFormatter(new SimpleFormatter());
logger.addHandler(handler);
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
logger.severe(ErrorUtil.exceptionStr((Exception) e));
}
});
for(int i = 0; i <= 3; i++){
arr[i]=i;
}
}
}
这里用了我的大招 ErrorUtil, 为了方便大家看,再贴一次:
package com.dyy.jdk8.test;import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ErrorUtil {
public static String exceptionStr(Exception e, int length) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
e.printStackTrace(new PrintStream(baos));
String exception = baos.toString();
String regEx = "Caused by:(.*)";
Pattern pat = Pattern.compile(regEx);
Matcher mat = pat.matcher(exception);
boolean rs = mat.find();
if (rs) {
if (mat.group(1).length() > length) {
return mat.group(1).substring(0, length);
} else {
return mat.group(1);
}
} else {
if (exception.length() > length) {
return exception.substring(0, length);
} else {
return exception;
}
}
}
public static String exceptionStr(Exception e){
return exceptionStr(e,800);
}
public static String getAllException(Exception e){
ByteArrayOutputStream baos = new ByteArrayOutputStream();
e.printStackTrace(new PrintStream(baos));
String exception = baos.toString();
String regEx = "Caused by:(.*)";
Pattern pat = Pattern.compile(regEx);
Matcher mat = pat.matcher(exception);
boolean rs = mat.find();
if (rs) {
return mat.group(1);
} else {
return exception;
}
}
}
效果:
(9)通过 -verbose 标志观察 Java 虚拟机启动过程
比较适合新代码启动就崩溃的情况查原因
(10) CTRL+SHIFT+F1 可打印层次组件结构(比如刚刚那个弹gif的例子)
(11)Swing 调试器
设置双重缓冲,可以看到绘制时会不断进行红色闪烁:
RepaintManager.currentManager(getRootPane()).setDoubleBufferingEnabled(false);((JComponent) getContentPane()).setDebugGraphicsOptions(DebugGraphics.FLASH_OPTION);
(12)使用 JDK 5 的 -Xlint 选项
通常用 -all 就可以:
javac -Xlint:all com.dyy.jdk8.Main(13)使用 jconsole
这个通常先用 jps -l 查端口(有时候太长 jconsole 看不全)
双击打开jdk/bin/jconsole.exe
打开对应行
可以查看各种信息,比如说对应日志配置的位置 VM 概要-> java.util.logging.config.file
(14)jmap jhat 分析堆
jmap -dump:format=b,file=a.prof 19908
jmap 用来产生一个堆分析文件
jhat a.prof
jhat 用来分析堆(太多会卡死,实际大型的也不会用这个。。。)
此处生成的端口是 7000(注意是自动的), 打开 http://localhost:7000
通过这部分查看堆的占用情况
13、14 这部分工具问题,可以查看我的另外一篇文章:《深入理解Java虚拟机》第4章 虚拟机性能监控与故障处理工具-看看虚拟机工具用起来有多简单
(15)配置参数 -Xprof 使用解析器
相关内容:选择 《Java核心技术 卷1》查找相关笔记
评论