用 go 写了一个彩色的 log - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
liyu4
V2EX    程序员

用 go 写了一个彩色的 log

  liyu4 2016-12-26 13:41:32 +08:00 8097 次点击
这是一个创建于 3217 天前的主题,其中的信息可能已经有所发展或是发生改变。

#教你写一个 color 日志库,不止有代码还有原理。

##前言 在计算机里面, ansi 转义码是使用带内信号去控制格式化,颜色,或者其他的输出选项在视频流或者文本终端中的一种办法。编码这些格式化信息,就是在确定的字节序列中,把上述所说的 ansi 码嵌入到这个文本中。终端会去寻找命令去解释这些字符,而不是把它看作简单的字符码( ascii )。

1970 年提出的 ansi 码,但是直到 1980 年的早期才普及在了迷你主机和大型机房中。它被使用在早期的电子公告板上,对比之前缺乏光标移动的系统,它改善了显示的效果,这导致了它被广泛的使用。

尽管硬件文本终端,在 21 世纪已经日益稀少。但是不动摇 ansi 标准的影响。因为大部分的文本模拟器的解释工作,至少还有相当一部分的文本存在 ansi 转义序列。唯一的例外就是微软的 win32 console 。不过这些在微软升级到 window10 之后已经被解决了。

所以这个古老的协议对我们现在的文本输出依然是有很重要的影响。

###一条小命令

echo -e "\033[1;31mI You \e[0m" 

output:

不上图我写什么文档,对吧。这条命令可以用来简单的告白用。送给大家了。从这里我们引出了 ansi 对文本颜色的输出影响,看,就是图中字符的颜色发生了变化。利用这样的性质我们今天就要分享一下如何写一个彩色的日子库( color log )

###CSI

转义序列使用 ESC 控制字符开始,对于 2 个字符序列,第二个字符是 ASCII 的 64 到 95 。(@到_,还有所有大写英文字母和[]^),然而,序列中大多数是超过 2 个字符的,并以 ESC 控制字符和左中括号开始。序列被称作 CSI ,即控制序列引导器或者控制序列启动器的简称。这个序列的最后一个字符是在 ASCII 范围 64 到 126 。也还有一个单字符的 CSI (155/0x9B/0233 ) ESC[,这两个字符序列比单个字符形式用的更多,细节参看 C0 和 C1 控制编码。(下文会添加文档的链接),使用 UTF-8 编码的终端上,两种形式都使用 2 字节( CSI in UTF-8 is 0xC2, 0x9B ),但 ESC[序列更加明了。

###文本颜色

文本颜色(和 SGR (Select Graphic Rendition)参数)使用 CSI n1 [;n2 [; ...]] m 序列来处理,如上所示,序列中每一个 n1, n2, ...就是一个 SGR 参数。因此,例如,你使用 30 ~ 37 表示前景色, 40 ~ 47 表示背景色。下图是一张颜色过渡图。

###开始编码

package util import ( "fmt" "time" ) const ( color_red = uint8(iota + 91) color_green color_yellow color_blue color_magenta //洋红 info = "[INFO]" trac = "[TRAC]" erro = "[ERRO]" warn = "[WARN]" succ = "[SUCC]" ) // see complete color rules in document in https://en.wikipedia.org/wiki/ANSI_escape_code#cite_note-ecma48-13 func Trace(format string, a ...interface{}) { prefix := yellow(trac) fmt.Println(formatLog(prefix), fmt.Sprintf(format, a...)) } func Info(format string, a ...interface{}) { prefix := blue(info) fmt.Println(formatLog(prefix), fmt.Sprintf(format, a...)) } func Success(format string, a ...interface{}) { prefix := green(succ) fmt.Println(formatLog(prefix), fmt.Sprintf(format, a...)) } func Warning(format string, a ...interface{}) { prefix := magenta(warn) fmt.Println(formatLog(prefix), fmt.Sprintf(format, a...)) } func Error(format string, a ...interface{}) { prefix := red(erro) fmt.Println(formatLog(prefix), fmt.Sprintf(format, a...)) } func red(s string) string { return fmt.Sprintf("\x1b[%dm%s\x1b[0m", color_red, s) } func green(s string) string { return fmt.Sprintf("\x1b[%dm%s\x1b[0m", color_green, s) } func yellow(s string) string { return fmt.Sprintf("\x1b[%dm%s\x1b[0m", color_yellow, s) } func blue(s string) string { return fmt.Sprintf("\x1b[%dm%s\x1b[0m", color_blue, s) } func magenta(s string) string { return fmt.Sprintf("\x1b[%dm%s\x1b[0m", color_magenta, s) } func formatLog(prefix string) string { return time.Now().Format("2006/01/02 15:04:05") + " " + prefix + " " } 

上面就是一个完整的彩色 log 的代码 输出的效果:

##完整代码 完整的 colorlog 代码 测试代码 如果你需要在本地测试,请确保你搭建了正确的 go 开发环境,并且 down 下 https://github.com/liyu4/chill 这个项目。找到 util 单元

yourpath 指的是你的项目路径。 cd yourpath/chill/util go test -v 

##后记 在其中 \x1b[ 实现 CSI :
转换前景色为黑色,使用\x1b[30m
转换为红色,使用\x1b[31m
如果使用加粗参数,灰色写作\x1b[30;1m
获取红色加粗,使用\x1b[31;1m
重置颜色为缺省值,使用\x1b[39;49m (或者使用 \x1b[0m 重置所有属性) \033[0m 重置为正常
\033[1m 设置高亮度或加粗
\033[4m 下划线
\033[5m 闪烁
\033[7m 反显
\033[8m 消隐
\033[30m -- /33[37m 设置前景色
\033[40m -- /33[47m 设置背景色 控制符 ESC 的常用表示方法\e 、\x1b(\x1B)、\033 都可以
\e 指代 Escape ,对应八进制\033 ,对应十六进制\x1b

###参考文档

ansi 转义序列 中文文档

28 条回复    2018-01-08 17:20:26 +08:00
zzWinD
    1
zzWinD  
   2016-12-26 13:49:27 +08:00
不错,支持一下。 go 貌似也有其他彩色的 log 库
mark 下 以后学习~
Shared
    2
Shared  
   2016-12-26 14:00:39 +08:00
用 `tput` 也可以哦,参看我的 dotfiles 安装脚本 https://github.com/Vayn/dotfiles/blob/master/install.sh#L9
liyu4
    3
liyu4  
OP
   2016-12-26 14:12:44 +08:00
@Shared 这是命令是兼容的吗?
liyu4
    4
liyu4  
OP
   2016-12-26 14:13:16 +08:00
@zzWinD 有问题可以找我,一起学习和加油!(可加微信)
unity0703
    5
unity0703  
   2016-12-26 14:15:42 +08:00
fmt.Println 都输出到 stdout 了,我觉得你可以用 fmt.Fprintf ,在参数里指定一个输出设备,这样更加通用
Shared
    6
Shared  
   2016-12-26 14:17:31 +08:00
@liyu4 是的, tput 的功能实际由 terminfo 和 termcap 提供
liyu4
    7
liyu4  
OP
   2016-12-26 14:20:34 +08:00
@unity0703 你的建议很好,谢谢啦!
liyu4
    8
liyu4  
OP
   2016-12-26 14:21:05 +08:00
@Shared 懂了,底层是 termcap 那兼容性各种终端应该不是什么问题了,学习了。
urmyfaith
    9
urmyfaith  
   2016-12-26 14:25:19 +08:00
孤独的根号 3

= = !
liyu4
    10
liyu4  
OP
   2016-12-26 14:47:36 +08:00
@urmyfaith 一辈子只做技术。
tonic
    11
tonic  
   2016-12-26 15:05:17 +08:00
github.com/Siruspen/logrus 挺好用啊... 也是彩色的, 就是丑...
liyu4
    12
liyu4  
OP
   2016-12-26 15:13:14 +08:00
@tonic 如果你习惯了用这个,那么你可以直接改它里面的颜色那部分代码,改成你喜欢的样子。祝你五颜六色。
q397064399
    13
q397064399  
   2016-12-26 16:31:18 +08:00
弱弱的问一句,有妹纸会喜欢你的终端告白么?
tomwei7
    14
tomwei7  
   2016-12-26 17:31:22 +08:00
我也有个 golang 彩色的 log https://github.com/tomwei7/tlog
liyu4
    15
liyu4  
OP
   2016-12-26 17:39:54 +08:00
@q397064399 开个玩笑的
liyu4
    16
liyu4  
OP
   2016-12-26 17:40:27 +08:00
@tomwei7 看了一下,感觉弄复杂了。原理其实都是一样的。
warlock
    17
warlock  
   2016-12-26 18:14:00 +08:00
我也有个 golang 彩色的 log +1 https://github.com/dafengge0913/golog
liyu4
    18
liyu4  
OP
   2016-12-26 18:52:17 +08:00
@warlock 厉害的,一定是有女朋友的人。
aiyo218
    19
aiyo218  
   2016-12-26 19:39:25 +08:00
好有爱~!
liyu4
    20
liyu4  
OP
   2016-12-26 21:05:25 +08:00
@aiyo218 谢谢喜欢!
FreeDog
    21
FreeDog  
   2016-12-26 21:21:10 +08:00
还有的代码会闪光等等,当时用 for 循环把排列组合打印了一屏幕
jerseyjerk
    22
jerseyjerk  
   2016-12-27 06:41:46 +08:00
很棒的作品。支持。但是有一些小瑕疵。
彩色代码很大程序上依赖于 shell 的实现。所以兼容性不是特别好。
另外,这个可以作为程序中需要用到 printf 的地方的替代品,但是将其命名为 log ,略微言过其实。先抛开 logger 的各种特性不表,通常并不倾向于在 log 中打印这些字符,因为解析日志的时候会徒增麻烦。
liyu4
    23
liyu4  
OP
   2016-12-27 09:06:15 +08:00 via iPhone
@jerseyjerk 是依赖于终端模拟器的实现,但是这是 ansi 标准的实现,所以对于多数的系统都是适用的,例外可能是 window 的控制台。不过在文档中提到了 window10 之后这个问题应该改善了。以上这段是基于理论说的,毕竟只在我的 mac 上测试过。后半段你提到的点很中肯,学习了。
liyu4
    24
liyu4  
OP
   2016-12-27 09:07:50 +08:00 via iPhone
@FreeDog 组合这些效果应该是可以实现的, cool !能把代码贴上来吗?
FreeDog
    25
FreeDog  
   2016-12-27 14:02:19 +08:00
@liyu4

代码很简单,就是 echo -e 之类的多层 for 循环,数字用 i 和 j 代替而已。不过需要在 Linux 真正的文本模式( Ctrl + Alt + 1 ~ 6 )下运行,虚拟终端 console 下很多效果显示不出来。貌似个别代码还有声音。
liyu4
    27
liyu4  
OP
   2017-01-19 15:32:47 +08:00
@FreeDog 厉害的
eager7
    28
eager7  
   2018-01-08 17:20:26 +08:00
我修改了一下代码,楼主看看能否使用?
const (
colorRed = iota + 91
colorGreen
colorYellow
colorBlue
colorMagenta
)

var (
debug = "\x1b[" + strconv.Itoa(colorBlue) + "m[DEBUG]\x1b[0m"
info = "\x1b[" + strconv.Itoa(colorYellow) + "m[INFO]\x1b[0m"
warn = "\x1b[" + strconv.Itoa(colorMagenta) + "m[WARN]\x1b[0m"
Err = "\x1b[" + strconv.Itoa(colorRed) + "m[ERROR]\x1b[0m"
success = "\x1b[" + strconv.Itoa(colorGreen) + "m[SUCCESS]\x1b[0m"
)

var Debug = Logger{log.New(os.Stdout, debug, log.LstdFlags)}
var Info = Logger{log.New(os.Stdout, info, log.LstdFlags)}
var Warn = Logger{log.New(os.Stdout, warn, log.LstdFlags)}
var Error = Logger{log.New(os.Stdout, Err, log.LstdFlags)}
var Success = Logger{log.New(os.Stdout, success, log.LstdFlags)}

type Logger struct {
*log.Logger
}

func (l *Logger) Disable() {
l.SetOutput(ioutil.Discard)
}

func (l *Logger) Enable() {
l.SetOutput(os.Stdout)
}
关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1276 人在线   最高记录 6679       Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 36ms UTC 17:21 PVG 01:21 LAX 10:21 JFK 13:21
Do have faith in what you're doing.
ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86