我们在linux 上调试代码的时候, 往往需要往终端中输出大量的调试信息, 但是如果各个级别的调试信息文字的颜色、大小都是一样的话, 我们往往无法一眼找到比如错误日志或者其他级别的日志
图中只是简单的做了个测试, 真实环境下的调试信息, 远比配图要复杂的多, 如果在调试某个 bug 的时候, 一下子甩出一堆文字, 要定位部分输出, 还是很考究眼力。
有没有什么方法能让调试信息一目了然呢? 当然有, 而且很简单。
在linux终端中, 字符颜色和显示方式是由转义序列
控制的, 是文本模式下的系统显示功能, 与具体开发语言并无关系(这就代表, 任何开发语言都能够使用这个方法😯)。
转义序列
转义序列以控制字符ESC
开头, 该字符的 ASCII 码十进制表示为 27, 十六进制为 0x1B, 八进制表示为 033 。多数的转义序列超过两个字符, 所以通常以ESC
和左括号[
开头。该起始序列称为控制序列引导符(CSI, Control Sequence Intro), 通常由\033
或者\e[
代替。
通过转义序列设置终端显示属性时, 可以采用下列格式
\033[ Param {;Param;...} m
或者是:
\e[ Param {;Param;...}m
其中, \033[
或\e[
引导转义序列, m
表示设置属性并结束转义。Param
为属性值, {...}
表示参数是可以多选的, 多个参数之间用分号隔开, 不限制顺序。
转义序列可被控制字符 CAN(Cancel) 和 SUB(Substitute)中断。
转义序列相关常用参数
man console_codes 可以查看更多的参数描述
显示:
- 0: 默认
- 1: 粗体/高亮
- 22: 非粗体
- 4: 单条下滑线
- 24: 无下滑线
- 5: 闪烁
- 25: 吴闪烁
- 7: 反显、翻转前景色和背景色
- 27: 无反显
颜色:
- 0: 黑
- 1: 红
- 2: 绿
- 3: 黄
- 4: 蓝
- 5: 洋红
- 6: 青色
- 7: 白
前景色为 30 + 颜色值, 如31表示前景色是红色
背景色为 40 + 颜色值, 如41表示背景色为红色
因此, 通过转义序列设置终端显示属性时, 常见格式为:
\033[显示方式;前景色;背景色m输出字符串\e[显示方式;前景色;背景色m输出字符串\e[0m
此外, 还有一些 ANSI 控制码, 如:
- nA(光标上移n行)
- nB(光标下移动n行)
- C(光标右移动n行)
- nD (光标左移n行 )
- 2J(清屏)
- K(清除从光标到行尾的内容)
- s(保存光标位置)
- u(恢复光标位置)
- ?25l(隐藏光标)
- ?25l(显示光标)
\e[0m # 用于恢复默认终端输出属性, 否则影响后续输出。
php 中实现
先把常用的属性定义为常量, 方便后续调用:
<?php
/**
* Created by PhpStorm.
* User: collin
* Date: 2017/4/17
* Time: 下午3:45
*/
class Log {
const DEBUG = 1;
// 颜色
private const NONE = "\e[0m";
private const BLACK = "\e[0;30m";
private const L_BLACK = "\e[1;30m";
private const RED = "\e[0;31m";
private const L_RED = "\e[1;31m";
private const GREEN = "\e[0;32m";
private const L_GREEN = "\e[1;32m";
private const BROWN = "\e[0;33m";
private const YELLOW = "\e[1;33m";
private const BLUE = "\e[0;34m";
private const L_BLUE = "\e[1;34m";
private const PURPLE = "\e[0;35m";
private const L_PURPLE = "\e[1;35m";
private const CYAN = "\e[0;36m";
private const L_CYAN = "\e[1;36m";
private const GRAY = "\e[0;37m";
private const WHITE = "\e[1;37m";
// 字体
private const BOLD = "\e[1m";
private const UNDERLINE = "\e[4m";
private const BLINK = "\e[5m";
private const REVERSE = "\e[7m";
private const HIDE = "\e[8m";
private const CLEAR = "\e[2J";
private const CLRLINE = "\r\e[K\" //or \"\e[1K\r";
/**
* 控制台输出 success 级别消息
* @param $str 日志内容
*/
static public function console_success($str) {
echo self::GREEN . 'SUCCESS['. date('Y-m-d H:i:s', time()).']: ' . $str . self::NONE . PHP_EOL;
}
/**
* 控制台输出 error 级别消息
* @param $str 日志内容
*/
static public function console_error($str) {
echo self::RED . 'ERROR['. date('Y-m-d H:i:s', time()).']: ' . $str . PHP_EOL;
$arr = debug_backtrace();
echo self::format($arr);
echo self::NONE . PHP_EOL;
}
/**
* 控制台输出 warning 级别消息
* @param $str 日志内容
*/
static public function console_warning($str) {
echo self::BROWN . 'WARNING['. date('Y-m-d H:i:s', time()).']: ' . $str . PHP_EOL;
$arr = debug_backtrace();
echo self::format($arr);
echo self::NONE . PHP_EOL;
}
/**
* 如果DEBUG = 1, 则输出 debug 级别消息
* @param $str 日志内容
*/
static public function console_debug($str) {
if (self::DEBUG) {
echo self::CYAN . 'DEBUG['. date('Y-m-d H:i:s', time()).']: ' . $str . PHP_EOL;
$arr = debug_backtrace();
echo self::format($arr);
echo self::NONE . PHP_EOL;
}
}
/**
* 如果 DEBUG = 1, 则遍历对象
* @param $arr object/array
*/
static public function console_dump_debug($arr) {
if (self::DEBUG) {
echo self::CYAN . 'DEBUG dump['. date('Y-m-d H:i:s', time()).']: '. PHP_EOL;
var_dump($arr);
echo self::NONE . PHP_EOL;
}
}
/**
* 控制台输出 info 级别消息
* @param $str 日志内容
*/
static public function console_info($str) {
echo self::UNDERLINE . 'INFO['. date('Y-m-d H:i:s', time()).']: ' . $str . self::NONE . PHP_EOL;
}
/**
* 格式化堆栈信息
* @param $arr
* @return string
*/
static private function format($arr) {
$str = '';
foreach ($arr as $k => $v) {
$file = $v['file'];
$line = $v['line'];
$class = isset($v['class']) ? $v['class'] . '\\' : 'stdClass\\';
$function = $v['function'] ?? '';
$str .= "#$k $file($line): {$class}{$function}" . PHP_EOL;
}
return $str;
}
}
上面的代码是我简单写的一个带色彩的日志类。