我們在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;
}
}
上面的代碼是我簡單寫的一個帶色彩的日志類。