github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/log/doc.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 12:09:41</date> 10 //</624342646269153280> 11 12 /* 13 包log15为最佳实践日志提供了一个独立、简单的工具包,即 14 人和机器都可读。它是根据标准库的IO和NET/HTTP建模的 15 包装。 16 17 此包强制您只记录键/值对。键必须是字符串。值可能是 18 任何你喜欢的类型。默认输出格式为logfmt,但也可以选择使用 19 如果你觉得合适的话,就改为JSON。以下是您登录的方式: 20 21 log.info(“访问页面”,“路径”,r.url.path,“用户ID”,user.id) 22 23 这将输出如下行: 24 25 lvl=info t=2014-05-02t16:07:23-0700 msg=“page accessed”path=/org/71/profile user_id=9 26 27 入门 28 29 要开始,您需要导入库: 30 31 导入日志“github.com/inconschrevable/log15” 32 33 34 现在您可以开始记录了: 35 36 FUNC主体() 37 log.info(“程序启动”,“args”,os.args()) 38 } 39 40 41 公约 42 43 因为记录人类有意义的信息是很常见的,也是很好的实践,所以每个人的第一个论点 44 日志记录方法是*隐式*键'msg'的值。 45 46 此外,为消息选择的级别将自动添加键“lvl”,因此 47 将当前时间戳与键“t”一起使用。 48 49 您可以将任何附加上下文作为一组键/值对提供给日志函数。允许登录15 50 你喜欢简洁、有序和快速,而不是安全。这是一个合理的折衷 51 日志功能。您不需要显式地声明键/值,log15理解它们是交替的 52 在变量参数列表中: 53 54 log warn(“大小越界”,“低”,“低”,“高”,“高”,“val”,val) 55 56 如果您确实支持类型安全,则可以选择传递一个log.ctx: 57 58 log.warn(“大小越界”,log.ctx“低”:下限,“高”:上限,“val”:val) 59 60 61 上下文记录器 62 63 通常,您希望将上下文添加到记录器中,以便跟踪与之关联的操作。一个HTTP协议 64 请求就是一个很好的例子。您可以轻松地创建具有自动包含上下文的新记录器。 65 每条记录线: 66 67 请求记录器:=log.new(“path”,r.url.path) 68 69 /以后 70 requestlogger.debug(“db txn commit”,“duration”,txtimer.finish()) 71 72 这将输出一条日志行,其中包含连接到记录器的路径上下文: 73 74 lvl=dbug t=2014-05-02t16:07:23-0700 path=/repo/12/add25oke msg=“db txn commit”持续时间=0.12 75 76 77 处理程序 78 79 处理程序接口定义日志行的打印位置和格式。汉德勒是 80 受net/http处理程序接口启发的单个接口: 81 82 类型处理程序接口 83 日志(R*记录)错误 84 } 85 86 87 处理程序可以筛选记录、格式化它们,或者分派到多个其他处理程序。 88 此包为常见的日志记录模式实现了许多处理程序 89 易于组合以创建灵活的自定义日志结构。 90 91 下面是一个将logfmt输出打印到stdout的示例处理程序: 92 93 处理程序:=log.streamHandler(os.stdout,log.logfmtformat()) 94 95 下面是一个示例处理程序,它遵从其他两个处理程序。一个处理程序只打印记录 96 从logfmt中的rpc包到标准输出。其他打印错误级别的记录 97 json格式输出到文件/var/log/service.json中或更高版本 98 99 处理程序:=log.multihandler( 100 log.lvlfilterhandler(log.lvlerror,log.must.filehandler(“/var/log/service.json”,log.jsonformat()), 101 log.matchfilterhandler(“pkg”,“app/rpc”log.stdouthandler()) 102 ) 103 104 记录文件名和行号 105 106 此包实现了三个处理程序,将调试信息添加到 107 Context、CallerFileHandler、CallerFundler和CallerStackHandler。这里是 108 将每个日志记录调用的源文件和行号添加到 109 语境。 110 111 h:=log.CallerFileHandler(log.stdouthandler) 112 log.root().sethandler(h) 113 … 114 log.error(“打开文件”,“err”,err) 115 116 这将输出如下行: 117 118 lvl=eror t=2014-05-02t16:07:23-0700 msg=“open file”err=“file not found”caller=data.go:42 119 120 下面是一个记录调用堆栈而不仅仅是调用站点的示例。 121 122 h:=log.callerstackhandler(“%+v”,log.stdouthandler) 123 log.root().sethandler(h) 124 … 125 log.error(“打开文件”,“err”,err) 126 127 这将输出如下行: 128 129 lvl=eror t=2014-05-02t16:07:23-0700 msg=“open file”err=“找不到文件”stack=“[pkg/data.go:42 pkg/cmd/main.go]” 130 131 “%+v”格式指示处理程序包含源文件的路径 132 相对于编译时gopath。github.com/go-stack/stack包 133 记录可用的格式化谓词和修饰符的完整列表。 134 135 自定义处理程序 136 137 处理程序接口非常简单,编写自己的接口也很简单。让我们创建一个 138 尝试写入一个处理程序的示例处理程序,但如果失败,则返回到 139 写入另一个处理程序,并包括尝试写入时遇到的错误 140 去初选。当试图通过网络套接字登录时,这可能很有用,但如果是这样的话 141 无法将这些记录记录记录到磁盘上的文件中。 142 143 类型backuphandler结构 144 主处理机 145 辅助处理程序 146 } 147 148 func(h*backuphandler)日志(r*record)错误 149 错误:=h.primary.log(r) 150 如果犯错!= nIL{ 151 r.ctx=append(ctx,“主错误”,err) 152 返回H.secondary.log(r) 153 } 154 返回零 155 } 156 157 此模式非常有用,以至于处理任意数量处理程序的通用版本 158 包含在这个名为failhandler的库中。 159 160 记录昂贵的操作 161 162 有时,您希望记录计算非常昂贵的值,但不想支付 163 如果您没有将日志记录级别提高到较高的详细级别,计算它们的价格。 164 165 此包提供了一个简单的类型,用于注释要评估的日志记录操作。 166 懒惰地,就在它即将被记录时,这样就不会在上游处理程序 167 过滤掉它。只需将不带参数的任何函数与log.lazy类型一起包装。例如: 168 169 func factorrsakey()(factors[]int) 170 //返回一个非常大的数的因子 171 } 172 173 log.debug(“factors”,log.lazy factorsakey) 174 175 如果由于任何原因(如错误级别的日志记录)未记录此消息,则 176 从未对factorsakey进行过评估。 177 178 动态上下文值 179 180 可以使用相同的log.lazy机制将上下文附加到您希望成为的记录器上。 181 在记录消息时计算,但在创建记录器时不计算。例如,让我们想象一下 182 有玩家对象的游戏: 183 184 类型播放器结构 185 名称字符串 186 活布尔 187 日志记录器 188 } 189 190 你总是想记录一个玩家的名字,不管他们是活的还是死的,所以当你创建这个玩家时 191 对象,可以执行以下操作: 192 193 p:=&player name:name,alive:true 194 p.logger=log.new(“名称”,p.name,“活动”,p.live) 195 196 直到现在,即使玩家死了,日志记录程序仍会报告他们还活着,因为日志记录 197 创建记录器时将计算上下文。通过使用惰性包装器,我们可以推迟评估 198 玩家是否对每条日志消息都是活动的,这样日志记录将反映玩家的 199 当前状态,无论何时写入日志消息: 200 201 p:=&player name:name,alive:true 202 isalive:=func()bool返回p.live 203 player.logger=log.new(“名称”,p.name,“活动”,log.lazy isalive) 204 205 终端格式 206 207 如果log15检测到stdout是终端,它将配置默认值 208 它的处理程序(即log.stdouthandler)使用TerminalFormat。这种格式 209 为您的终端很好地记录,包括基于颜色编码的输出 210 在日志级别上。 211 212 错误处理 213 214 因为log15允许您绕过类型系统,所以有几种方法可以指定 215 日志函数的参数无效。例如,你可以包装一些不是 216 带log.lazy的零参数函数,或传递不是字符串的上下文键。因为日志记录库 217 通常是报告错误的机制,对于日志记录功能来说是很麻烦的 218 返回错误。相反,log15通过向您提供以下保证来处理错误: 219 220 -任何包含错误的日志记录仍将打印,并将错误解释为日志记录的一部分。 221 222 -任何包含错误的日志记录都将包含上下文键log15_错误,使您能够轻松 223 (如果您愿意,自动)检测您的任何日志记录调用是否传递了错误值。 224 225 了解这一点,您可能会想知道为什么处理程序接口可以在其日志方法中返回错误值。处理程序 226 只有当错误无法将其日志记录写入外部源时,才鼓励返回错误,例如 227 Syslog守护程序没有响应。这允许构造有用的处理程序来处理这些失败。 228 就像那个失败者。 229 230 图书馆使用 231 232 log15旨在对库作者有用,作为提供可配置的日志记录到 233 他们图书馆的用户。在库中使用的最佳实践是始终禁用记录器的所有输出 234 默认情况下,并提供库的使用者可以配置的公共记录器实例。像这样: 235 236 包装你的衣服 237 238 导入“github.com/inconschrevable/log15” 239 240 var log=log.new()。 241 242 函数() 243 log.setHandler(log.discardHandler()) 244 } 245 246 如果您的库用户愿意,可以启用它: 247 248 导入“github.com/inconschrevable/log15” 249 导入“example.com/yourlib” 250 251 FUNC主体() 252 处理程序:=//自定义处理程序设置 253 yourlib.log.sethandler(处理程序) 254 } 255 256 附加记录器上下文的最佳实践 257 258 将上下文附加到记录器的能力非常强大。你应该在哪里做,为什么? 259 我喜欢将一个记录器直接嵌入到我的应用程序中的任何持久对象中,并添加 260 唯一的跟踪上下文键。例如,假设我正在编写一个Web浏览器: 261 262 类型选项卡结构 263 URL字符串 264 渲染*渲染上下文 265 /… 266 267 记录器 268 } 269 270 func newtab(url字符串)*tab_ 271 返回和标签{ 272 /… 273 网址: 274 275 记录器:log.new(“url”,url), 276 } 277 } 278 279 当创建一个新的选项卡时,我会为它分配一个具有以下URL的记录器: 280 该选项卡作为上下文,以便通过日志轻松跟踪。 281 现在,每当我们对选项卡执行任何操作时,我们都将使用 282 嵌入式记录器,它将自动包含选项卡标题: 283 284 tab.debug(“移动位置”,“idx”,tab.idx) 285 286 只有一个问题。如果标签URL改变了怎么办?我们可以 287 使用log.lazy确保始终写入当前的URL,但是 288 这意味着我们无法追踪标签的整个生命周期 289 用户导航到新的URL后登录。 290 291 相反,考虑一下要附加到记录器上的值 292 与您考虑在SQL数据库模式中作为键使用的方法相同。 293 如果可以使用在 294 对象,这样做。但除此之外,log15的ext包有一个方便的randid 295 函数来生成可能称为“代理键”的内容 296 它们只是用于跟踪的随机十六进制标识符。回到我们的身边 297 例如,我们希望像这样设置记录器: 298 299 导入logext“github.com/inconschrevable/log15/ext” 300 301 T:=和Tab { 302 /… 303 网址: 304 } 305 306 t.logger=log.new(“id”,logext.randid(8),“url”,log.lazy_t.geturl) 307 返回T 308 309 现在,即使在加载新的URL时,我们也会有一个唯一的可跟踪标识符,但是 310 我们仍然可以在日志消息中看到选项卡的当前URL。 311 312 必须 313 314 对于所有可以返回错误的处理程序函数,都有一个版本 315 函数,它不会返回错误,但会在失败时出现恐慌。它们都有 316 在“必须”对象上。例如: 317 318 log.must.filehandler(“/path”,log.jsonformat) 319 log must.nethandler(“tcp”,“:1234”,log.jsonformat) 320 321 灵感与信用 322 323 以下所有优秀的项目都激发了这个图书馆的设计灵感: 324 325 code.google.com/p/log4go 326 327 github.com/op/go-logging 328 329 github.com/technoweenie/grohl公司 330 331 github.com/sirusen/logrus公司 332 333 github.com/kr/logfmt 334 335 github.com/spacemonkeygo/spacelog/空间日志 336 337 Golang的stdlib,特别是IO和net/http 338 339 名字 340 341 https://xkcd.com/927/ 342 343 **/ 344 345 package log 346