如何为 Go 设计一个通用的日志包
需求
一个通用的日志包,应该满足以下几个需求:
兼容 log.Logger
,标准库大量使用了 log.Logger
作为其错误内容的输出通道,比如 net/http.Server.ErrorLog,所以兼容 log.Logger
是首要的需求;
自定义配置,不同的运行环境,往往需要不同的日志输出配置,在不重新编译源码的情况下,就能改变配置也应该成为一项标配;
不同的输出类型,根据目前市面上大部分的日志库,我们把日志分为以下六种类型:INFO
、WARN
、DEBUG
、TRACE
、ERROR
和 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 设计一个通用的日志包的更多相关文章
- iOS开发:代码通用性以及其规范 第二篇(猜想iOS中实现TableView内部设计思路(附代码),以类似的思想实现一个通用的进度条)
在iOS开发中,经常是要用到UITableView的,我曾经思考过这样一个问题,为什么任何种类的model放到TableView和所需的cell里面,都可以正常显示?而我自己写的很多view却只是能放 ...
- 开源:通用的日志分析工具(LogViewer)
工具介绍 本工具最早是制作出来查看我的 FTL(Fast Trace Log) 二进制日志文件的, 后来因为去做Java后台,经常看 SpringBoot, Tomcat 等的日志, 就简单重构了一下 ...
- Linux下一个简单的日志系统的设计及其C代码实现
1.概述 在大型软件系统中,为了监测软件运行状况及排查软件故障,一般都会要求软件程序在运行的过程中产生日志文件.在日志文件中存放程序流程中的一些重要信息, 包括:变量名称及其值.消息结构定义.函数返回 ...
- 利用RBAC模型实现一个通用的权限管理系统
本文主要描述一个通用的权限系统实现思路与过程.也是对此次制作权限管理模块的总结. 制作此系统的初衷是为了让这个权限系统得以“通用”.就是生产一个web系统通过调用这个权限系统(生成的dll文件), 就 ...
- MySQL系列(十二)--如何设计一个关系型数据库(基本思路)
设计一个关系型数据库,也就是设计RDBMS(Relational Database Management System),这个问题考验的是对RDBMS各个模块的划分, 以及对数据库结构的了解.只要讲述 ...
- 如何从0到1设计一个MQ消息队列
消息队列作为系统解耦,流量控制的利器,成为分布式系统核心组件之一. 如果你对消息队列背后的实现原理关注不多,其实了解消息队列背后的实现非常重要. 不仅知其然还要知其所以然,这才是一个优秀的工程师需要具 ...
- 如何一步一步用DDD设计一个电商网站(十)—— 一个完整的购物车
阅读目录 前言 回顾 梳理 实现 结语 一.前言 之前的文章中已经涉及到了购买商品加入购物车,购物车内购物项的金额计算等功能.本篇准备把剩下的购物车的基本概念一次处理完. 二.回顾 在动手之前我对之 ...
- 如何一步一步用DDD设计一个电商网站(四)—— 把商品卖给用户
阅读目录 前言 怎么卖 领域服务的使用 回到现实 结语 一.前言 上篇中我们讲述了“把商品卖给用户”中的商品和用户的初步设计.现在把剩余的“卖”这个动作给做了.这里提醒一下,正常情况下,我们的每一步业 ...
- 如何一步一步用DDD设计一个电商网站(三)—— 初涉核心域
一.前言 结合我们本次系列的第一篇博文中提到的上下文映射图(传送门:如何一步一步用DDD设计一个电商网站(一)—— 先理解核心概念),得知我们这个电商网站的核心域就是销售子域.因为电子商务是以信息网络 ...
随机推荐
- xib下这种方式创建cell
这种方法在iOS5.0之前是不能够创建成功的. MEConvertListTableViewCell *cell = [tableView dequeueReusableCellWithIde ...
- Android-工作总结-LX-2018-08-20-setHint
问题的因素: 调试了一下午,我一直是以为是数据没有传递过来,然后一直在数据获取环节检查在检查,数据跟踪在跟踪,最后发现数据获取是OK
- myeclipse过期以后提示过期以后怎么办?!
昨天电脑上装的myeclipse到期了,不能进到工作空间里边,只有激活和退出选项,在网上百度了一下,有很多破解工具, 1.刚开始直接使用工具破解,没有成功,总是提示要么激活,要么退出 2.继续想办法, ...
- JQuery中DOM操作(一)
节点操作 1.插入节点,在JQuery中插入的节点方法很多,可以满足各种不同的节点插入的情况,根据不同的插入方式分为:增加环绕节点.节点内部插入和外部插入三种方式 今天我们要讲的是增加环绕节点,它有三 ...
- 2.C#WebAPI设置路由和参数1
1.当我们创建WebApi的时候我们的项目下的Contorls文件夹下的ValuesController文件下会出现这么几个方法: // GET http://程序ip:程序端口/api/values ...
- 热更新(一) 之Lua语法的学习
热更新 如热更新果需要更换UI显示,或者修改游戏的逻辑,这个时候,如果不使用热更新,就需要重新打包,然后让玩家重新下载(浪费流量和时间,体验不好).热更新可以在不重新下载客户端的情况下,更新游戏的内容 ...
- Core引用Jquery文件
静态文件放在wwwroot里边 不然浏览器会报错文件不存在.
- 使用RazorGenerator和预编译MVC引擎将Razor视图编译成DLL
Web开发中常常会有跨页面.跨站点.跨项目组的复用模块(界面),最常见的就是如下方所示的Web页面上用于显示登录或用户名的头部模块, 使用ASP.NET MVC开发中,常见的做法是写成部分视图,本文的 ...
- JAVA特性一:封装
封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式. 封装概念详解:封装是把过程和数据包围起来,对数据的访问只能通过已定义的接口. 面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完 ...
- spring利用注解方式实现Java读取properties属性值
1. 建立properties文件:我在resource下面建立一个config文件夹,config文件夹里面有mytest.properties文件,文件内容如下: sam.username=sam ...