Java fights bugs: 断言和日志

1. 断言

强迫症程序员的福音

在我们的程序中, 有时:

  1. 命题A必然为真
  2. 命题A必然为真的原因或为spec要求它为真, 或为程序逻辑使得它为真
  3. 程序的其他部分对A的真实性有着依赖
  4. 但我们害怕或由于使用ADT的人没有遵守spec, 或由于编程疏忽, 导致A为假, 我们考虑对A的真实性进行检查
  5. 若采用异常机制, 则会拖慢发行版本的运行速度
此时使用断言, 断言是在开发时起作用的, 检查某些永真命题的强迫症代码

1.1 启用断言

在Java程序运行时使用 -ea 参数, 使用IDE时每个IDE有不同的加参数的方法

1.2 断言语句

有两种断言语句:

assert condition;
assert condition : expression;

断言语句计算condition的值, 若condition == false, 则抛出AssertionError, 第二种语句将expression的值(不一定是String类型)传入AssertionError的构造器并生成一个消息字符串以便显示在控制台上

1.3 何时使用断言 / 何时不该使用断言

一言以蔽之, 满足五条描述的场景可以使用断言
例:
ADT的RI
某些有取值范围的变量
控制流逻辑中永远无法到达的地点
spec的pre- / post-condition

不应该在断言的条件部分写入功能性代码, 因为在发行版中断言会被禁用
有时在控制流中使用断言会使程序缺少某种分支下的返回值而导致无法通过静态类型检查(即便这种分支永远无法到达), 此时的一种解决办法是使用

throw new AssertionError(expression);

代替

assert false : expression;

2. 日志

正经人谁写日记啊
——《邪不压正》

此处介绍java.util.logging的日志框架

2.1 逻辑结构

没啥可描述的
日志由日志记录器记录, 经过日志记录器级别限制和过滤器过滤后, 发送给日志处理器, 经过日志处理器级别限制和过滤器过滤后, 发送到控制台(标准错误流System.err), 文件或某主机的某端口

2.2 日志级别

日志有七个级别

  • SEVERE(致命)
  • WARNING(警告)
  • INFO(信息)
  • CONFIG
  • FINE(黄豆大的小事)
  • FINER(绿豆大的小事)
  • FINEST(芝麻大的小事)

严重程度由高到低排序

2.3 日志记录器(Logger)

全局日志记录器

Logger.getGlobal();

获取全局日志记录器


其他日志记录器

Logger.getLogger(String name);

创建获取一个名为name的日志记录器.
顾名思义, 没有的叫创建, 已有的叫获取, 由于Java有垃圾回收机制, 创建的日志记录器应使用变量(一般为静态变量)保存它以保持强联系, 以防被当作垃圾回收掉.

日志记录器名
日志记录器名有严格的层次结构. 与包结构类似, 日志记录器以日志记录器名为标准被组织成树状结构, 该树的根节点为祖先日志记录器, 祖先日志记录器的名字是空串"", 全局日志记录器的名字是 "global". 且父子日志记录器之间会共享某些属性, 如level. 因此, 尽量永远使用完整的类名做为日志记录器名, 这样就很好的保持了日志记录器间的层次性.


设置日志级别

.setLevel(Level.INFO);

使日志记录器只记录指定级别及以上的日志, 但只设置这一步还不够, 在后面我们会看到, 还要同时设置日志处理器的日志级别


记录日志

//分级的记录方法
.severe(String message);
.warning(String message);
...

//动态指定级别的记录方法
.log(Level.INFO, String message);

//记录异常的简单方法
.log(Level.INFO, String message, Throwable t);
.throwing(String className, String methodName, Throwable t);

2.4 日志记录器与日志处理器

交互
日志记录器会将记录发送给自己的日志处理器列表中的每个处理器.
之后, 沿日志记录器树向根节点追溯, 每遇到一个父日志记录器, 就遍历它的日志处理器列表并发送记录.
直到到达根节点: 祖先日志记录器, 或某个节点禁用了父日志处理器为止.


记录器的处理器们
新创建的日志记录器没有日志处理器
全局日志记录器和祖先日志记录器默认各有一个控制台处理器

.addHandler(...);

该方法为记录器添加一个处理器

2.5 日志处理器(Handler)

设置日志级别

.setLevel(Level.INFO);

使日志处理器发送指定级别及以上的日志


控制台日志处理器
ConsoleHandler


套接字日志处理器
SocketHandler


文件日志处理器
FileHandler
文件日志支持:

  1. 追加, 新记录追加在文件末尾而不是覆盖文件, 默认关闭
  2. 文件循环, 当文件大小超出limit后, 生成新的日志文件, 使用生成号0, 同时其他文件被重命名, 生成号 + 1
    当文件个数将要超出count时, 将最老的文件删除
    默认limit = 5000, count = 1

*日志文件模式串
模式串标识了日志文件的路径和文件名, 默认模式串为%h/java%u.log
其中:

  • %h 为系统属性user.home, 即主目录
  • %u 为"解决冲突的编号", 通常为0, 在多线程场景下才有用
  • %g 为生成号, 未在模式串中指定生成号则在末尾添加

综上, 默认模式串的含义是在主目录下创建名为javan.log的日志文件


设置格式

.setFormatter(...);

为日志处理器安装一个格式化器, 格式化器略, 百度一下, 你就知道
文件日志处理器的默认格式为XML格式

2.6 过滤器

日志记录器和日志处理器可以各拥有一个过滤器, 对记录进行比级别过滤更细致的过滤
二者都可以通过调用

.setFilter(...);

来为自己设置过滤器, 每个器最多有一个过滤器
过滤器略, 百度一下, 你就知道

2.7 日志配置文件

上面说了很多"默认xxx", 这是谁默认的呢? 日志相关的默认设置保存在日志配置文件中, 日志配置文件位于: conf/logging.properties (Java 9), 在Java 9版本之前该文件在另一个位置.
我就不领大家去看了, 我得吃饭了, 再见.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值