> 文档中心 > 《Java 核心技术 卷1》 笔记 第11章 异常、日志、断言和调试(8)日志记录说明+调试建议

《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的例子)

 11Swing 调试器

设置双重缓冲,可以看到绘制时会不断进行红色闪烁:

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

 14jmap 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》查找相关笔记

评论🌹点赞👍收藏✨关注👀,是送给作者最好的礼物,愿我们共同学习,一起进步

如果对作者发布的内容感兴趣,可点击下方关注公众号 钰娘娘知识汇总 查看更多作者文章哦!