《Java 核心技术 卷1》 笔记 第11章 异常、日志、断言和调试(8)日志记录说明+调试建议
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",""); 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",""); } 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
操作步骤:
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 使用解析器
点击:File->Open代开GIF源文件
关闭GIF,查看日志
相关内容:选择 《Java核心技术 卷1》查找相关笔记
评论🌹点赞👍收藏✨关注👀,是送给作者最好的礼物,愿我们共同学习,一起进步
如果对作者发布的内容感兴趣,可点击下方关注公众号 钰娘娘知识汇总 查看更多作者文章哦!