需求

一个通用的日志包,应该满足以下几个需求:

兼容 log.Logger,标准库大量使用了 log.Logger 作为其错误内容的输出通道,比如 net/http.Server.ErrorLog,所以兼容 log.Logger 是首要的需求;

自定义配置,不同的运行环境,往往需要不同的日志输出配置,在不重新编译源码的情况下,就能改变配置也应该成为一项标配;

不同的输出类型,根据目前市面上大部分的日志库,我们把日志分为以下六种类型:INFOWARNDEBUGTRACEERROR 和 CRITICAL

对日志内容的自定义处理,比如将日志按时间段进行切割,或是重要内容发邮箱进行提醒等。

配置文件

根据以上的需求,我们可以拟定一个以下格式的 XML 配置文件:

<?xml version="1.0" encoding="utf-8"?>
<logs>
<info prefix="INFO" flag="log.lstdflag" >
<!-- 向控制台输出信息 -->
<console output="stderr" foreground="red" background="blue" />
</info> <error prefix="ERROR" flag="log.lstdflag" />
<!-- 向控制台输出信息 -->
<console output="stderr" foreground="red" background="blue" /> <!-- 输出到 /var/logs/debug 目录下,如果大于 5M,则新建文件 -->
<rotate dir="/var/logs/debug/" size="5M" />
</error> <critical prefix="CRITICAL" flag="log.lstdflag" >
<!-- 向指定的邮箱发送错误信息 -->
<smtp username="u"
password="p"
host="smtp.example.com"
subject="CRITICAL"
sendTo="u1@example.com;u2@example.com" />
</critical>
</logs>

顶级元素固定为 logs

二级元素为 INFO 和 WARN 等六种固定的类型,在加载时,将其初始为一个 *log.Logger 实例,初始化参数可以直接从 XML 的属性和子元素中获取;

二级以下的元素,统一初始化为 io.Writer 实例,当作 log.New() 的参数,用于初始化其父元素。用户可通过接口自定义这些元素,若有初始化参数,则从其 XML 属性值中获取。

这样就达到了一个自由度相对比较高的配置文件。

接口设计

根据以上要求,我们大致上可以确定下来接口的内容。

一个从配置文件进行初始化的接口:

func InitFromConfig(path string) error

以及各个输出类型的操作函数,最主要的就是获取 *log.Logger 的接口,以及一些常用的简化调用。以 INFO 类型为例,我们可以定义以下几个函数,其它类型也都相同,只是函数名不同。

func INFO() *log.Logger  // 返回与 INFO 类型对应的 *log.Logger 实例

// 针对 INFO 类型日志的一些常用操作进行封装
func Info(v ...interface{}) // 相当于 INFO().Println()
func Infof(format string, v...interface{}) // 相当于 INFO().Printf()

我们还需要公开一个注册接口,用于注册用户自定义的元素:

type Init func(args map[string]string)(io.Writer, error)

func Register(name string, fn Init)

使用

只要在 main() 中正确定加载了配置文件,之后随处都可以调用:

// main.go
func main() {
err := logs.InitFromConfig("./config/logs.xml")
if err !=nil {
panic(err)
} // do something
} // file1.go
func h1(w http.ResponseWriter, r *http.Request) {
if !auth() {
logs.Infof("鉴权失败")
w.WriteHeader(http.StatusUnauthorized)
return
}
} // file2.go
func getServer(port string, h http.Handler) *http.Server {
return &http.Server {
Addr: port,
Handler: h,
ErrorLog: logs.INFO(),
}
}

实现

完整的实现代码在:https://github.com/issue9/logs

如何为 Go 设计一个通用的日志包的更多相关文章

  1. iOS开发:代码通用性以及其规范 第二篇(猜想iOS中实现TableView内部设计思路(附代码),以类似的思想实现一个通用的进度条)

    在iOS开发中,经常是要用到UITableView的,我曾经思考过这样一个问题,为什么任何种类的model放到TableView和所需的cell里面,都可以正常显示?而我自己写的很多view却只是能放 ...

  2. 开源:通用的日志分析工具(LogViewer)

    工具介绍 本工具最早是制作出来查看我的 FTL(Fast Trace Log) 二进制日志文件的, 后来因为去做Java后台,经常看 SpringBoot, Tomcat 等的日志, 就简单重构了一下 ...

  3. Linux下一个简单的日志系统的设计及其C代码实现

    1.概述 在大型软件系统中,为了监测软件运行状况及排查软件故障,一般都会要求软件程序在运行的过程中产生日志文件.在日志文件中存放程序流程中的一些重要信息, 包括:变量名称及其值.消息结构定义.函数返回 ...

  4. 利用RBAC模型实现一个通用的权限管理系统

    本文主要描述一个通用的权限系统实现思路与过程.也是对此次制作权限管理模块的总结. 制作此系统的初衷是为了让这个权限系统得以“通用”.就是生产一个web系统通过调用这个权限系统(生成的dll文件), 就 ...

  5. MySQL系列(十二)--如何设计一个关系型数据库(基本思路)

    设计一个关系型数据库,也就是设计RDBMS(Relational Database Management System),这个问题考验的是对RDBMS各个模块的划分, 以及对数据库结构的了解.只要讲述 ...

  6. 如何从0到1设计一个MQ消息队列

    消息队列作为系统解耦,流量控制的利器,成为分布式系统核心组件之一. 如果你对消息队列背后的实现原理关注不多,其实了解消息队列背后的实现非常重要. 不仅知其然还要知其所以然,这才是一个优秀的工程师需要具 ...

  7. 如何一步一步用DDD设计一个电商网站(十)—— 一个完整的购物车

     阅读目录 前言 回顾 梳理 实现 结语 一.前言 之前的文章中已经涉及到了购买商品加入购物车,购物车内购物项的金额计算等功能.本篇准备把剩下的购物车的基本概念一次处理完. 二.回顾 在动手之前我对之 ...

  8. 如何一步一步用DDD设计一个电商网站(四)—— 把商品卖给用户

    阅读目录 前言 怎么卖 领域服务的使用 回到现实 结语 一.前言 上篇中我们讲述了“把商品卖给用户”中的商品和用户的初步设计.现在把剩余的“卖”这个动作给做了.这里提醒一下,正常情况下,我们的每一步业 ...

  9. 如何一步一步用DDD设计一个电商网站(三)—— 初涉核心域

    一.前言 结合我们本次系列的第一篇博文中提到的上下文映射图(传送门:如何一步一步用DDD设计一个电商网站(一)—— 先理解核心概念),得知我们这个电商网站的核心域就是销售子域.因为电子商务是以信息网络 ...

随机推荐

  1. POJ1751 Highways 2017-04-14 15:46 70人阅读 评论(0) 收藏

    Highways Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 14819   Accepted: 4278   Speci ...

  2. CodeForces 572D Minimization(DP)

    题意翻译 给定数组AAA 和值kkk ,你可以重排AAA 中的元素,使得∑i=1n−k∣Ai−Ai+k∣\displaystyle\sum_{i=1}^{n-k} |A_i-A_{i+k}|i=1∑n ...

  3. rzdatetimepicker

    两项选为True时可显示今天和圆圈 此属性可使右边按钮下平滑 注意Format中的表示月份的MM一定要大写,不然修改时和分钟会联动.

  4. Sourcetree拉取推送问题

    以下sourcetree使用时出现的问题:可以暂时用删除passwd文件解决 拉取时账号密码输入错误 已经有一个远程仓库了,但是需要拉取另一个远程仓库时,提示URL 进入 C:\Users\用户名\A ...

  5. php的循环与引用的一个坑

    上代码 $arr = array( 'a'=> 'a11', 'b'=> 'b22', 'c'=> 'c33', ); foreach ($arr as $k=>&$v ...

  6. oracle 批量删除触发器

    --生成删除触发器的语句 select 'drop trigger "'||trigger_name||'";' from all_triggers  where TRIGGER_ ...

  7. SQL Server数据类型总结

    1.char char [(n)]存储固定长度的非Unicode字符串数据.n定义字符串长度,并且必须是1到8,000之间的值.存储大小为n个字节. 2.varchar varchar [(n | m ...

  8. h5页面宽度设置7.5rem

    function ready() { var u = navigator.userAgent; var winW = document.documentElement.clientWidth; if ...

  9. TypeScript白鹭引擎Egret防止按钮事件冒泡穿透

    this.addEventListener(egret.TouchEvent.TOUCH_BEGIN, (event) => { if (event.target!=this) return;/ ...

  10. WPF 按名称查找控件

    1FrameworkElement类FindName方法 使用过程 1.容器控件.RegisterName("Name",要注册的控件)   //注册控件 2.容器控件.FindN ...