pax_global_header00006660000000000000000000000064133164457700014524gustar00rootroot0000000000000052 comment=941dea75d3ebfbdd905a5d8b7b232965c5e5c684 log-1.1.0/000077500000000000000000000000001331644577000123045ustar00rootroot00000000000000log-1.1.0/LICENSE000066400000000000000000000021121331644577000133050ustar00rootroot00000000000000(The MIT License) Copyright (c) 2015 TJ Holowaychuk tj@tjholowaychuk.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. log-1.1.0/Makefile000066400000000000000000000000431331644577000137410ustar00rootroot00000000000000 include github.com/tj/make/golang log-1.1.0/Readme.md000066400000000000000000000023611331644577000140250ustar00rootroot00000000000000 ![Structured logging for golang](assets/title.png) Package log implements a simple structured logging API inspired by Logrus, designed with centralization in mind. Read more on [Medium](https://medium.com/@tjholowaychuk/apex-log-e8d9627f4a9a#.rav8yhkud). ## Handlers - __cli__ – human-friendly CLI output - __discard__ – discards all logs - __es__ – Elasticsearch handler - __graylog__ – Graylog handler - __json__ – JSON output handler - __kinesis__ – AWS Kinesis handler - __level__ – level filter handler - __logfmt__ – logfmt plain-text formatter - __memory__ – in-memory handler for tests - __multi__ – fan-out to multiple handlers - __papertrail__ – Papertrail handler - __text__ – human-friendly colored output - __delta__ – outputs the delta between log calls and spinner --- [![Build Status](https://semaphoreci.com/api/v1/projects/d8a8b1c0-45b0-4b89-b066-99d788d0b94c/642077/badge.svg)](https://semaphoreci.com/tj/log) [![GoDoc](https://godoc.org/github.com/apex/log?status.svg)](https://godoc.org/github.com/apex/log) ![](https://img.shields.io/badge/license-MIT-blue.svg) ![](https://img.shields.io/badge/status-stable-green.svg) log-1.1.0/_examples/000077500000000000000000000000001331644577000142615ustar00rootroot00000000000000log-1.1.0/_examples/cli/000077500000000000000000000000001331644577000150305ustar00rootroot00000000000000log-1.1.0/_examples/cli/cli.go000066400000000000000000000013431331644577000161270ustar00rootroot00000000000000package main import ( "errors" "time" "github.com/apex/log" "github.com/apex/log/handlers/cli" ) func main() { log.SetHandler(cli.Default) log.SetLevel(log.DebugLevel) ctx := log.WithFields(log.Fields{ "file": "something.png", "type": "image/png", "user": "tobi", }) go func() { for range time.Tick(time.Second) { ctx.Debug("doing stuff") } }() go func() { for range time.Tick(100 * time.Millisecond) { ctx.Info("uploading") ctx.Info("upload complete") } }() go func() { for range time.Tick(time.Second) { ctx.Warn("upload slow") } }() go func() { for range time.Tick(2 * time.Second) { err := errors.New("boom") ctx.WithError(err).Error("upload failed") } }() select {} } log-1.1.0/_examples/default/000077500000000000000000000000001331644577000157055ustar00rootroot00000000000000log-1.1.0/_examples/default/default.go000066400000000000000000000006621331644577000176640ustar00rootroot00000000000000package main import ( "errors" "time" "github.com/apex/log" ) func main() { ctx := log.WithFields(log.Fields{ "file": "something.png", "type": "image/png", "user": "tobi", }) for range time.Tick(time.Millisecond * 200) { ctx.Info("upload") ctx.Info("upload complete") ctx.Warn("upload retry") ctx.WithError(errors.New("unauthorized")).Error("upload failed") ctx.Errorf("failed to upload %s", "img.png") } } log-1.1.0/_examples/delta/000077500000000000000000000000001331644577000153525ustar00rootroot00000000000000log-1.1.0/_examples/delta/delta.go000066400000000000000000000013471331644577000167770ustar00rootroot00000000000000package main import ( "errors" "time" "github.com/apex/log" "github.com/apex/log/handlers/delta" ) func main() { log.SetHandler(delta.Default) log.SetLevel(log.DebugLevel) ctx := log.WithFields(log.Fields{ "file": "something.png", "type": "image/png", "user": "tobi", }) go func() { for range time.Tick(time.Second) { ctx.Debug("doing stuff") } }() go func() { for range time.Tick(100 * time.Millisecond) { ctx.Info("uploading") ctx.Info("upload complete") } }() go func() { for range time.Tick(time.Second) { ctx.Warn("upload slow") } }() go func() { for range time.Tick(2 * time.Second) { err := errors.New("boom") ctx.WithError(err).Error("upload failed") } }() select {} } log-1.1.0/_examples/es/000077500000000000000000000000001331644577000146705ustar00rootroot00000000000000log-1.1.0/_examples/es/es.go000066400000000000000000000017211331644577000156270ustar00rootroot00000000000000package main import ( "errors" "net/http" "os" "time" "github.com/apex/log" "github.com/apex/log/handlers/es" "github.com/apex/log/handlers/multi" "github.com/apex/log/handlers/text" "github.com/tj/go-elastic" ) func main() { esClient := elastic.New("http://192.168.99.101:9200") esClient.HTTPClient = &http.Client{ Timeout: 5 * time.Second, } e := es.New(&es.Config{ Client: esClient, BufferSize: 100, }) t := text.New(os.Stderr) log.SetHandler(multi.New(e, t)) ctx := log.WithFields(log.Fields{ "file": "something.png", "type": "image/png", "user": "tobi", }) go func() { for range time.Tick(time.Millisecond * 200) { ctx.Info("upload") ctx.Info("upload complete") ctx.Warn("upload retry") ctx.WithError(errors.New("unauthorized")).Error("upload failed") ctx.Errorf("failed to upload %s", "img.png") } }() go func() { for range time.Tick(time.Millisecond * 25) { ctx.Info("upload") } }() select {} } log-1.1.0/_examples/json/000077500000000000000000000000001331644577000152325ustar00rootroot00000000000000log-1.1.0/_examples/json/json.go000066400000000000000000000007241331644577000165350ustar00rootroot00000000000000package main import ( "errors" "os" "time" "github.com/apex/log" "github.com/apex/log/handlers/json" ) func main() { log.SetHandler(json.New(os.Stderr)) ctx := log.WithFields(log.Fields{ "file": "something.png", "type": "image/png", "user": "tobi", }) for range time.Tick(time.Millisecond * 200) { ctx.Info("upload") ctx.Info("upload complete") ctx.Warn("upload retry") ctx.WithError(errors.New("unauthorized")).Error("upload failed") } } log-1.1.0/_examples/kinesis/000077500000000000000000000000001331644577000157265ustar00rootroot00000000000000log-1.1.0/_examples/kinesis/kinesis.go000066400000000000000000000007421331644577000177250ustar00rootroot00000000000000package main import ( "os" "time" "github.com/apex/log" "github.com/apex/log/handlers/kinesis" "github.com/apex/log/handlers/multi" "github.com/apex/log/handlers/text" ) func main() { log.SetHandler(multi.New( text.New(os.Stderr), kinesis.New("logs"), )) ctx := log.WithFields(log.Fields{ "file": "something.png", "type": "image/png", "user": "tobi", }) for range time.Tick(time.Millisecond * 100) { ctx.Info("upload") ctx.Info("upload complete") } } log-1.1.0/_examples/logfmt/000077500000000000000000000000001331644577000155515ustar00rootroot00000000000000log-1.1.0/_examples/logfmt/logfmt.go000066400000000000000000000007301331644577000173700ustar00rootroot00000000000000package main import ( "errors" "os" "time" "github.com/apex/log" "github.com/apex/log/handlers/logfmt" ) func main() { log.SetHandler(logfmt.New(os.Stderr)) ctx := log.WithFields(log.Fields{ "file": "something.png", "type": "image/png", "user": "tobi", }) for range time.Tick(time.Millisecond * 200) { ctx.Info("upload") ctx.Info("upload complete") ctx.Warn("upload retry") ctx.WithError(errors.New("unauthorized")).Error("upload failed") } } log-1.1.0/_examples/multi/000077500000000000000000000000001331644577000154135ustar00rootroot00000000000000log-1.1.0/_examples/multi/multi.go000066400000000000000000000011071331644577000170730ustar00rootroot00000000000000package main import ( "errors" "os" "time" "github.com/apex/log" "github.com/apex/log/handlers/json" "github.com/apex/log/handlers/multi" "github.com/apex/log/handlers/text" ) func main() { log.SetHandler(multi.New( text.New(os.Stderr), json.New(os.Stderr), )) ctx := log.WithFields(log.Fields{ "file": "something.png", "type": "image/png", "user": "tobi", }) for range time.Tick(time.Millisecond * 200) { ctx.Info("upload") ctx.Info("upload complete") ctx.Warn("upload retry") ctx.WithError(errors.New("unauthorized")).Error("upload failed") } } log-1.1.0/_examples/stack/000077500000000000000000000000001331644577000153665ustar00rootroot00000000000000log-1.1.0/_examples/stack/stack.go000066400000000000000000000011751331644577000170260ustar00rootroot00000000000000package main import ( "os" "github.com/pkg/errors" "github.com/apex/log" "github.com/apex/log/handlers/logfmt" ) func main() { log.SetHandler(logfmt.New(os.Stderr)) filename := "something.png" body := []byte("whatever") ctx := log.WithField("filename", filename) err := upload(filename, body) if err != nil { ctx.WithError(err).Error("upload failed") } } // Faux upload. func upload(name string, b []byte) error { err := put("/images/"+name, b) if err != nil { return errors.Wrap(err, "uploading to s3") } return nil } // Faux PUT. func put(key string, b []byte) error { return errors.New("unauthorized") } log-1.1.0/_examples/text/000077500000000000000000000000001331644577000152455ustar00rootroot00000000000000log-1.1.0/_examples/text/text.go000066400000000000000000000010031331644577000165520ustar00rootroot00000000000000package main import ( "errors" "os" "time" "github.com/apex/log" "github.com/apex/log/handlers/text" ) func main() { log.SetHandler(text.New(os.Stderr)) ctx := log.WithFields(log.Fields{ "file": "something.png", "type": "image/png", "user": "tobi", }) for range time.Tick(time.Millisecond * 200) { ctx.Info("upload") ctx.Info("upload complete") ctx.Warn("upload retry") ctx.WithError(errors.New("unauthorized")).Error("upload failed") ctx.Errorf("failed to upload %s", "img.png") } } log-1.1.0/_examples/trace/000077500000000000000000000000001331644577000153575ustar00rootroot00000000000000log-1.1.0/_examples/trace/trace.go000066400000000000000000000006771331644577000170160ustar00rootroot00000000000000package main import ( "os" "time" "github.com/apex/log" "github.com/apex/log/handlers/text" ) func work(ctx log.Interface) (err error) { path := "Readme.md" defer ctx.WithField("path", path).Trace("opening").Stop(&err) _, err = os.Open(path) return } func main() { log.SetHandler(text.New(os.Stderr)) ctx := log.WithFields(log.Fields{ "app": "myapp", "env": "prod", }) for range time.Tick(time.Second) { _ = work(ctx) } } log-1.1.0/assets/000077500000000000000000000000001331644577000136065ustar00rootroot00000000000000log-1.1.0/assets/title.png000066400000000000000000001050231331644577000154360ustar00rootroot00000000000000PNG  IHDRvOOasRGB@IDATx U7`A1,"Y*,D\aDaA\qAYGAEQpEY 2̌ ʀ|]@"0d~jo{owt/Ϩk3my_:9gyS)9Y,KlJ?n/S s⋳Tu:Nwd=U%*| ?iʔ)kΝ;yOY_qis̙3ٱvikn9|; as}` 9ssm8]_~K6SOKĉϼk֏ 馛NzꩧvH6ɻ~oY:uEӪԹKܤto݃wukvem喫̞={c9穤?S<_%uHqǧ/Ͻm*ֹf5-԰?M Rح<;ozֳnmp*hS72\IٝVrݿvRv,;)w]9U^UuX @L@nd~&@@?¾\`2ػȠnu:y$,M?LKp O`}Y攵KҏivUW_}F {ӗ2vu^@|'gp*Zg0;~YgplsW@=\ WSZUeJeׇ2`=Jd 3\ڽIw(m0&.1I7UӨҿ;q_Gb/!fzymin+|7!;_HF[J}7/m9kW~k.rY WM3tH}*WL=پ?ά+n?뇲zZ'iiɷH8u6Ḻ\5樂uJ-pu=z~h˱sl-}eur?;wf]W# xQڕri}C֫g,Z.9mV: Y4eޙ7zϟ?޸eO_҅q胥F}:տU-m>z\)τ󳾠su1 @;/  @=6m +|2ezhmR֧2`T1amVN g .|R ,}e cɫf1w~ƼcM3x[ 3 5kW:}3LGƨiߚUk!罽--;ͧf; h>-8]'?7-+Nnz0-͎fkH =ycJڵc?*lvY/H|?~vjV[=fS}k6':3.9hl[q@:}/A ?^m#h]z9UmtBL=G)6%[f\/YOy6vdefq9(fw5M{o|od7?;wڑmy]lKCYDO߇uSKS/\vm .Cr'uwZܽk_ @ ׭ @\uU%8XBfʾ"3zz s3pxF۷$$ ^^%qTKp<}3Z4vmC=4ovHc2H.{VX+51cYޖot7*_0ƴJZj::Ľޑ ]Dzm13?oSm['Xxҫs W^SZa I+m_lW73k8$&za{^dge" ɻFz4)gX]Ml wsJ}{E&e=|>˯җrQڞ6.;=Qf?I@Eym}ZWy#R-I ǯicL9סORȵ.6]n%nG Zm}# @!ACAf:L;,`򖰕&tsޜ Ұt7myw O6=9М[KO,*T] v guqRk/ۤm%6(xs?jwISw3fZ5yux)ZmwI{"N/Ih Um/Af\ [oOW.κK]=X LJ;>Ҟڐ<}Tf^;TX_Vn%8=s$wX͘llcym6ϯ:y/?sobrJcާՖcefcYlSޭ~NQ'fSvz׮n  @'71SE 0m.|2bo8a$d0uA)u^eK6 ,Z~Җ)jL`jf w%S`e!?6eW%V}.HVzM6:j/l\f}ڧqCYb{nor0 9Y/(Ɍr۞ ;2r~5Sz]r{'y3g|Y?1YUZRVsuxq1'k8/=qVu/+Jh՞zz'^!{oy\?>yzW3WGw[6=ϩӛ8YY҆feF-@IDATwyi$o$ms. @@kjuG @z*P.d[?8_V?8jʳ5QTקdtlÉGgwC-W_"6<|kR#9o=E~鹞/I7v,ܽ*VOo3ch=miKOwΌՖڕg@{}Mm|f\zt[βG|2hof)SgқR9y۲s~9e^ާ#m6$=A̹aϘP^lvvH V@-  @ =2yW^X~*-x3s k2[f:gg&hVKf?lԿ0˫AUA3@_eHߢkIcu ת~=u?(k*̙X L0Tbc%5y=\yJf]0FҷԷuGGRβtX^Ng ^{fJwz^/=}ϢG$dVn ]jur/ӑUGϧ|HH\ @;tNG  @F] ڿ >W\'Uz,XES6I_XOk}Kc%!*ݕidW2k'm~f@۠%ggzS֫o ՟*O^;Zq_1T~|n))ruM? `nå: WG}5 ~)soև_Li/O;V/u['q}o O^ڃv(e=b-r@9z:a„KoZfzy&.vY92F|Z7">)&ZncQjgGoHϿKkp @:s 0m݊=lɫNL2{]jovH}_m@`Nho]vn.3Ö[ybfcmToozs}7kyʀO(yiMOndZKmW;25ߑrt)4#:wqӆ/׿CvַM*$6>'qu/ eh,Y<.Fuau]mVJK;^X}|[.ϯR]ws+Ӷ߿ʫ^| wA'eyﻚ8AMp{&~>|w[w߈; @p-i @VuZ28waf;[k 3Xxalƪ$: fqFU??Njl07vT՟gWnHsirʲTpI+ޖwd9}?n8vq ߔSY"m8fq^O~M'u=}+NjQʨ;>pʩoy&>x~7iο0c6muVNG 8VHwuԷJ-m7ƭuܹs wSb9 @t!  @Ȁ!2xW Ӫj^9 WZm?h+~ .Ϡ޿w3(+37%SNR:i2ۤ RW3-h+[fvw>_?ӿSO 59iγʿg͚xݲlf|'vw8GפKӦM+lw0~Ӻ纮-4 *jqtݴvTG*(G75S>RfY-.,Ru4h<:lV+2;mIedv~/IncyN_퐏 @{͜A@fyj ~(e4x_9ޟ+l)*ebq) M%7禾ҖuuCQ5k[F&hz~)e գ7$_=mü쥙Aqu=1e6t0ezeu;CL>W^,%Ppw6d,XpIYNv0kecͲZoe71ߡ7WfSM3miY̡k2Aյa^1Yc<6nL_ieПjV3{yRo󽟞{7<;ff:-~A> @:Ku @P;t㭎ewq#wOS23!߯)Yv=m3:h%2ɪ-ַZӱiۜv?&oykѧ-b&;-?=u|kqYw횐~Tysr:m,o +^{h\uxZ_r眓ҏ|չOGVUzq:3oʿ 贞^u @ @ |(o+ M?k$߅esX3`xQeVϙ3sko]uUYzeq-gѓi?n՟>\Zs3x_ҧղ=#\p_yYNzꩧf ?+Ϫ2@[~g 5ݔOKYogS lؐ-,֪#r%Yn.:sm>z+䙔kPy͛Ws{C+K%Ϛs=`dt C?~|ylb4BˠcڽJ-}?bh2:^˫GQ̊^Mo7Ӟ2m{˶|LRf5 >mZXᶿIQ=OwTrw܎=37^uy @ @a 䕊+djVKPϟAW$RSf4ԒmҬQ L-8IzlW1 iU>Y% ]c_kJzj{u/euY=T9ğ_Ì2^/~zl @+JI @J=gw2p9/g uԩS/+NIUWf2K'[+yN74?/`\$pr/ {i?grF֯y \'wDoHeNڒ_A;U%P3'{S S7s̋'f-zxO }~3wyIcJH .GuJ 9AC Ӷ=nզ_JrqNK[ҧ$YNw66PMw瞙s51QϯG.]fϞ=='UIH] b#}HKv=/Yv]U9]I(nGi]dwUaהzi7^˗kSyu/gC%u߯8r?n<3rM>wޞ_>.>嚽8ms%'>'fG>hPi*5Rw7rlf{A˿C~ڵ?H?X(D+/H7үvK Й\gNr @FE f^֏2H=qeO-cȀ{Dn[<0K+jl^!wYs O_s|Ҙ2{vf0ҧq rbWuI?&2H{zBJjV@αfq%1{zգ>Z^o{ ד3(TN:şZ:Ƚ\Z՗{IIqZwmQ>jgYE*Ϙ\'oqͦr=up.iY3W%_yk}u50+kgv3V eժ 7z  @ @ @`l%ۅpcvK @r+ @ @ @@gX @ @ @UpU#@ @ @h]@  @ @ @*b @ @ @ Za @ @ @TUV @ @ @u\3? @ @ @܊ @ @ @. k}G @ @ PU@W[1 @ @ @pϰ @ @ @ r+F @ @ к @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @N_ IDAT @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ />t }IENDB`log-1.1.0/default.go000066400000000000000000000014321331644577000142570ustar00rootroot00000000000000package log import ( "bytes" "fmt" "log" "sort" ) // field used for sorting. type field struct { Name string Value interface{} } // by sorts fields by name. type byName []field func (a byName) Len() int { return len(a) } func (a byName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a byName) Less(i, j int) bool { return a[i].Name < a[j].Name } // handleStdLog outpouts to the stlib log. func handleStdLog(e *Entry) error { level := levelNames[e.Level] var fields []field for k, v := range e.Fields { fields = append(fields, field{k, v}) } sort.Sort(byName(fields)) var b bytes.Buffer fmt.Fprintf(&b, "%5s %-25s", level, e.Message) for _, f := range fields { fmt.Fprintf(&b, " %s=%v", f.Name, f.Value) } log.Println(b.String()) return nil } log-1.1.0/doc.go000066400000000000000000000006561331644577000134070ustar00rootroot00000000000000/* Package log implements a simple structured logging API designed with few assumptions. Designed for centralized logging solutions such as Kinesis which require encoding and decoding before fanning-out to handlers. You may use this package with inline handlers, much like Logrus, however a centralized solution is recommended so that apps do not need to be re-deployed to add or remove logging service providers. */ package log log-1.1.0/entry.go000066400000000000000000000075621331644577000140060ustar00rootroot00000000000000package log import ( "fmt" "os" "strings" "time" ) // assert interface compliance. var _ Interface = (*Entry)(nil) // Now returns the current time. var Now = time.Now // Entry represents a single log entry. type Entry struct { Logger *Logger `json:"-"` Fields Fields `json:"fields"` Level Level `json:"level"` Timestamp time.Time `json:"timestamp"` Message string `json:"message"` start time.Time fields []Fields } // NewEntry returns a new entry for `log`. func NewEntry(log *Logger) *Entry { return &Entry{ Logger: log, } } // WithFields returns a new entry with `fields` set. func (e *Entry) WithFields(fields Fielder) *Entry { f := []Fields{} f = append(f, e.fields...) f = append(f, fields.Fields()) return &Entry{ Logger: e.Logger, fields: f, } } // WithField returns a new entry with the `key` and `value` set. func (e *Entry) WithField(key string, value interface{}) *Entry { return e.WithFields(Fields{key: value}) } // WithError returns a new entry with the "error" set to `err`. // // The given error may implement .Fielder, if it does the method // will add all its `.Fields()` into the returned entry. func (e *Entry) WithError(err error) *Entry { ctx := e.WithField("error", err.Error()) if s, ok := err.(stackTracer); ok { frame := s.StackTrace()[0] name := fmt.Sprintf("%n", frame) file := fmt.Sprintf("%+s", frame) line := fmt.Sprintf("%d", frame) parts := strings.Split(file, "\n\t") if len(parts) > 1 { file = parts[1] } ctx = ctx.WithField("source", fmt.Sprintf("%s: %s:%s", name, file, line)) } if f, ok := err.(Fielder); ok { ctx = ctx.WithFields(f.Fields()) } return ctx } // Debug level message. func (e *Entry) Debug(msg string) { e.Logger.log(DebugLevel, e, msg) } // Info level message. func (e *Entry) Info(msg string) { e.Logger.log(InfoLevel, e, msg) } // Warn level message. func (e *Entry) Warn(msg string) { e.Logger.log(WarnLevel, e, msg) } // Error level message. func (e *Entry) Error(msg string) { e.Logger.log(ErrorLevel, e, msg) } // Fatal level message, followed by an exit. func (e *Entry) Fatal(msg string) { e.Logger.log(FatalLevel, e, msg) os.Exit(1) } // Debugf level formatted message. func (e *Entry) Debugf(msg string, v ...interface{}) { e.Debug(fmt.Sprintf(msg, v...)) } // Infof level formatted message. func (e *Entry) Infof(msg string, v ...interface{}) { e.Info(fmt.Sprintf(msg, v...)) } // Warnf level formatted message. func (e *Entry) Warnf(msg string, v ...interface{}) { e.Warn(fmt.Sprintf(msg, v...)) } // Errorf level formatted message. func (e *Entry) Errorf(msg string, v ...interface{}) { e.Error(fmt.Sprintf(msg, v...)) } // Fatalf level formatted message, followed by an exit. func (e *Entry) Fatalf(msg string, v ...interface{}) { e.Fatal(fmt.Sprintf(msg, v...)) } // Trace returns a new entry with a Stop method to fire off // a corresponding completion log, useful with defer. func (e *Entry) Trace(msg string) *Entry { e.Info(msg) v := e.WithFields(e.Fields) v.Message = msg v.start = time.Now() return v } // Stop should be used with Trace, to fire off the completion message. When // an `err` is passed the "error" field is set, and the log level is error. func (e *Entry) Stop(err *error) { if err == nil || *err == nil { e.WithField("duration", time.Since(e.start)).Info(e.Message) } else { e.WithField("duration", time.Since(e.start)).WithError(*err).Error(e.Message) } } // mergedFields returns the fields list collapsed into a single map. func (e *Entry) mergedFields() Fields { f := Fields{} for _, fields := range e.fields { for k, v := range fields { f[k] = v } } return f } // finalize returns a copy of the Entry with Fields merged. func (e *Entry) finalize(level Level, msg string) *Entry { return &Entry{ Logger: e.Logger, Fields: e.mergedFields(), Level: level, Message: msg, Timestamp: Now(), } } log-1.1.0/entry_test.go000066400000000000000000000025631331644577000150410ustar00rootroot00000000000000package log import ( "fmt" "testing" "github.com/stretchr/testify/assert" ) func TestEntry_WithFields(t *testing.T) { a := NewEntry(nil) assert.Nil(t, a.Fields) b := a.WithFields(Fields{"foo": "bar"}) assert.Equal(t, Fields{}, a.mergedFields()) assert.Equal(t, Fields{"foo": "bar"}, b.mergedFields()) c := a.WithFields(Fields{"foo": "hello", "bar": "world"}) e := c.finalize(InfoLevel, "upload") assert.Equal(t, e.Message, "upload") assert.Equal(t, e.Fields, Fields{"foo": "hello", "bar": "world"}) assert.Equal(t, e.Level, InfoLevel) assert.NotEmpty(t, e.Timestamp) } func TestEntry_WithField(t *testing.T) { a := NewEntry(nil) b := a.WithField("foo", "bar") assert.Equal(t, Fields{}, a.mergedFields()) assert.Equal(t, Fields{"foo": "bar"}, b.mergedFields()) } func TestEntry_WithError(t *testing.T) { a := NewEntry(nil) b := a.WithError(fmt.Errorf("boom")) assert.Equal(t, Fields{}, a.mergedFields()) assert.Equal(t, Fields{"error": "boom"}, b.mergedFields()) } func TestEntry_WithErrorFields(t *testing.T) { a := NewEntry(nil) b := a.WithError(errFields("boom")) assert.Equal(t, Fields{}, a.mergedFields()) assert.Equal(t, Fields{ "error": "boom", "reason": "timeout", }, b.mergedFields()) } type errFields string func (ef errFields) Error() string { return string(ef) } func (ef errFields) Fields() Fields { return Fields{"reason": "timeout"} } log-1.1.0/handlers/000077500000000000000000000000001331644577000141045ustar00rootroot00000000000000log-1.1.0/handlers/cli/000077500000000000000000000000001331644577000146535ustar00rootroot00000000000000log-1.1.0/handlers/cli/cli.go000066400000000000000000000031211331644577000157460ustar00rootroot00000000000000// Package cli implements a colored text handler suitable for command-line interfaces. package cli import ( "fmt" "io" "os" "sync" "time" "github.com/apex/log" "github.com/fatih/color" colorable "github.com/mattn/go-colorable" ) // Default handler outputting to stderr. var Default = New(os.Stderr) // start time. var start = time.Now() var bold = color.New(color.Bold) // Colors mapping. var Colors = [...]*color.Color{ log.DebugLevel: color.New(color.FgWhite), log.InfoLevel: color.New(color.FgBlue), log.WarnLevel: color.New(color.FgYellow), log.ErrorLevel: color.New(color.FgRed), log.FatalLevel: color.New(color.FgRed), } // Strings mapping. var Strings = [...]string{ log.DebugLevel: "•", log.InfoLevel: "•", log.WarnLevel: "•", log.ErrorLevel: "⨯", log.FatalLevel: "⨯", } // Handler implementation. type Handler struct { mu sync.Mutex Writer io.Writer Padding int } // New handler. func New(w io.Writer) *Handler { if f, ok := w.(*os.File); ok { return &Handler{ Writer: colorable.NewColorable(f), Padding: 3, } } return &Handler{ Writer: w, Padding: 3, } } // HandleLog implements log.Handler. func (h *Handler) HandleLog(e *log.Entry) error { color := Colors[e.Level] level := Strings[e.Level] names := e.Fields.Names() h.mu.Lock() defer h.mu.Unlock() color.Fprintf(h.Writer, "%s %-25s", bold.Sprintf("%*s", h.Padding+1, level), e.Message) for _, name := range names { if name == "source" { continue } fmt.Fprintf(h.Writer, " %s=%v", color.Sprint(name), e.Fields.Get(name)) } fmt.Fprintln(h.Writer) return nil } log-1.1.0/handlers/delta/000077500000000000000000000000001331644577000151755ustar00rootroot00000000000000log-1.1.0/handlers/delta/delta.go000066400000000000000000000057701331644577000166260ustar00rootroot00000000000000// Package delta provides a log handler which times the delta // between each log call, useful for debug output for command-line // programs. package delta import ( "fmt" "io" "os" "time" "github.com/apex/log" "github.com/aybabtme/rgbterm" "github.com/tj/go-spin" ) // TODO: move colors and share in text handler etc // color function. type colorFunc func(string) string // gray string. func gray(s string) string { return rgbterm.FgString(s, 150, 150, 150) } // blue string. func blue(s string) string { return rgbterm.FgString(s, 77, 173, 247) } // cyan string. func cyan(s string) string { return rgbterm.FgString(s, 34, 184, 207) } // green string. func green(s string) string { return rgbterm.FgString(s, 0, 200, 255) } // red string. func red(s string) string { return rgbterm.FgString(s, 194, 37, 92) } // yellow string. func yellow(s string) string { return rgbterm.FgString(s, 252, 196, 25) } // Colors mapping. var Colors = [...]colorFunc{ log.DebugLevel: gray, log.InfoLevel: blue, log.WarnLevel: yellow, log.ErrorLevel: red, log.FatalLevel: red, } // Strings mapping. var Strings = [...]string{ log.DebugLevel: "DEBU", log.InfoLevel: "INFO", log.WarnLevel: "WARN", log.ErrorLevel: "ERRO", log.FatalLevel: "FATA", } // Default handler. var Default = New(os.Stderr) // Handler implementation. type Handler struct { entries chan *log.Entry start time.Time spin *spin.Spinner prev *log.Entry done chan struct{} w io.Writer } // New handler. func New(w io.Writer) *Handler { h := &Handler{ entries: make(chan *log.Entry), done: make(chan struct{}), start: time.Now(), spin: spin.New(), w: w, } go h.loop() return h } // Close the handler. func (h *Handler) Close() error { h.done <- struct{}{} close(h.done) close(h.entries) return nil } // loop for rendering. func (h *Handler) loop() { ticker := time.NewTicker(100 * time.Millisecond) for { select { case e := <-h.entries: if h.prev != nil { h.render(h.prev, true) } h.render(e, false) h.prev = e case <-ticker.C: if h.prev != nil { h.render(h.prev, false) } h.spin.Next() case <-h.done: ticker.Stop() if h.prev != nil { h.render(h.prev, true) } return } } } func (h *Handler) render(e *log.Entry, done bool) { color := Colors[e.Level] level := Strings[e.Level] names := e.Fields.Names() // delta and spinner if done { fmt.Fprintf(h.w, "\r %-7s", time.Since(h.start).Round(time.Millisecond)) } else { fmt.Fprintf(h.w, "\r %s %-7s", h.spin.Current(), time.Since(h.start).Round(time.Millisecond)) } // message fmt.Fprintf(h.w, " %s %s", color(level), color(e.Message)) // fields for _, name := range names { v := e.Fields.Get(name) if v == "" { continue } fmt.Fprintf(h.w, " %s%s%v", color(name), gray("="), v) } // newline if done { fmt.Fprintf(h.w, "\n") h.start = time.Now() } } // HandleLog implements log.Handler. func (h *Handler) HandleLog(e *log.Entry) error { h.entries <- e return nil } log-1.1.0/handlers/discard/000077500000000000000000000000001331644577000155155ustar00rootroot00000000000000log-1.1.0/handlers/discard/discard.go000066400000000000000000000005771331644577000174660ustar00rootroot00000000000000// Package discard implements a no-op handler useful for benchmarks and tests. package discard import ( "github.com/apex/log" ) // Default handler. var Default = New() // Handler implementation. type Handler struct{} // New handler. func New() *Handler { return &Handler{} } // HandleLog implements log.Handler. func (h *Handler) HandleLog(e *log.Entry) error { return nil } log-1.1.0/handlers/es/000077500000000000000000000000001331644577000145135ustar00rootroot00000000000000log-1.1.0/handlers/es/es.go000066400000000000000000000037111331644577000154530ustar00rootroot00000000000000// Package es implements an Elasticsearch batch handler. Currently this implementation // assumes the index format of "logs-YY-MM-DD". package es import ( "io" stdlog "log" "sync" "time" "github.com/tj/go-elastic/batch" "github.com/apex/log" ) // TODO(tj): allow dumping logs to stderr on timeout // TODO(tj): allow custom format that does not include .fields etc // TODO(tj): allow interval flushes // TODO(tj): allow explicit Flush() (for Lambda where you have to flush at the end of function) // Elasticsearch interface. type Elasticsearch interface { Bulk(io.Reader) error } // Config for handler. type Config struct { BufferSize int // BufferSize is the number of logs to buffer before flush (default: 100) Format string // Format for index Client Elasticsearch // Client for ES } // defaults applies defaults to the config. func (c *Config) defaults() { if c.BufferSize == 0 { c.BufferSize = 100 } if c.Format == "" { c.Format = "logs-06-01-02" } } // Handler implementation. type Handler struct { *Config mu sync.Mutex batch *batch.Batch } // New handler with BufferSize func New(config *Config) *Handler { config.defaults() return &Handler{ Config: config, } } // HandleLog implements log.Handler. func (h *Handler) HandleLog(e *log.Entry) error { h.mu.Lock() defer h.mu.Unlock() if h.batch == nil { h.batch = &batch.Batch{ Index: time.Now().Format(h.Config.Format), Elastic: h.Client, Type: "log", } } h.batch.Add(e) if h.batch.Size() >= h.BufferSize { go h.flush(h.batch) h.batch = nil } return nil } // flush the given `batch` asynchronously. func (h *Handler) flush(batch *batch.Batch) { size := batch.Size() start := time.Now() stdlog.Printf("log/elastic: flushing %d logs", size) if err := batch.Flush(); err != nil { stdlog.Printf("log/elastic: failed to flush %d logs: %s", size, err) } stdlog.Printf("log/elastic: flushed %d logs in %s", size, time.Since(start)) } log-1.1.0/handlers/graylog/000077500000000000000000000000001331644577000155505ustar00rootroot00000000000000log-1.1.0/handlers/graylog/graylog.go000066400000000000000000000023041331644577000175420ustar00rootroot00000000000000// Package implements a Graylog-backed handler. package graylog import ( "github.com/apex/log" "github.com/aphistic/golf" ) // Handler implementation. type Handler struct { logger *golf.Logger client *golf.Client } // New handler. // Connection string should be in format "udp://:". // Server should have GELF input enabled on that port. func New(url string) (*Handler, error) { c, err := golf.NewClient() if err != nil { return nil, err } err = c.Dial(url) if err != nil { return nil, err } l, err := c.NewLogger() if err != nil { return nil, err } return &Handler{ logger: l, client: c, }, nil } // HandleLog implements log.Handler. func (h *Handler) HandleLog(e *log.Entry) error { switch e.Level { case log.DebugLevel: return h.logger.Dbgm(e.Fields, e.Message) case log.InfoLevel: return h.logger.Infom(e.Fields, e.Message) case log.WarnLevel: return h.logger.Warnm(e.Fields, e.Message) case log.ErrorLevel: return h.logger.Errm(e.Fields, e.Message) case log.FatalLevel: return h.logger.Critm(e.Fields, e.Message) } return nil } // Closes connection to server, flushing message queue. func (h *Handler) Close() error { return h.client.Close() } log-1.1.0/handlers/json/000077500000000000000000000000001331644577000150555ustar00rootroot00000000000000log-1.1.0/handlers/json/json.go000066400000000000000000000010261331644577000163540ustar00rootroot00000000000000// Package json implements a JSON handler. package json import ( j "encoding/json" "io" "os" "sync" "github.com/apex/log" ) // Default handler outputting to stderr. var Default = New(os.Stderr) // Handler implementation. type Handler struct { *j.Encoder mu sync.Mutex } // New handler. func New(w io.Writer) *Handler { return &Handler{ Encoder: j.NewEncoder(w), } } // HandleLog implements log.Handler. func (h *Handler) HandleLog(e *log.Entry) error { h.mu.Lock() defer h.mu.Unlock() return h.Encoder.Encode(e) } log-1.1.0/handlers/json/json_test.go000066400000000000000000000013621331644577000174160ustar00rootroot00000000000000package json_test import ( "bytes" "testing" "time" "github.com/stretchr/testify/assert" "github.com/apex/log" "github.com/apex/log/handlers/json" ) func init() { log.Now = func() time.Time { return time.Unix(0, 0).UTC() } } func Test(t *testing.T) { var buf bytes.Buffer log.SetHandler(json.New(&buf)) log.WithField("user", "tj").WithField("id", "123").Info("hello") log.Info("world") log.Error("boom") expected := `{"fields":{"id":"123","user":"tj"},"level":"info","timestamp":"1970-01-01T00:00:00Z","message":"hello"} {"fields":{},"level":"info","timestamp":"1970-01-01T00:00:00Z","message":"world"} {"fields":{},"level":"error","timestamp":"1970-01-01T00:00:00Z","message":"boom"} ` assert.Equal(t, expected, buf.String()) } log-1.1.0/handlers/kinesis/000077500000000000000000000000001331644577000155515ustar00rootroot00000000000000log-1.1.0/handlers/kinesis/kinesis.go000066400000000000000000000024011331644577000175420ustar00rootroot00000000000000package kinesis import ( "encoding/base64" "encoding/json" "github.com/apex/log" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/kinesis" "github.com/rogpeppe/fastuuid" k "github.com/tj/go-kinesis" ) // Handler implementation. type Handler struct { appName string producer *k.Producer gen *fastuuid.Generator } // New handler sending logs to Kinesis. To configure producer options or pass your // own AWS Kinesis client use NewConfig instead. func New(stream string) *Handler { return NewConfig(k.Config{ StreamName: stream, Client: kinesis.New(session.New(aws.NewConfig())), }) } // NewConfig handler sending logs to Kinesis. The `config` given is passed to the batch // Kinesis producer, and a random value is used as the partition key for even distribution. func NewConfig(config k.Config) *Handler { producer := k.New(config) producer.Start() return &Handler{ producer: producer, gen: fastuuid.MustNewGenerator(), } } // HandleLog implements log.Handler. func (h *Handler) HandleLog(e *log.Entry) error { b, err := json.Marshal(e) if err != nil { return err } uuid := h.gen.Next() key := base64.StdEncoding.EncodeToString(uuid[:]) return h.producer.Put(b, key) } log-1.1.0/handlers/level/000077500000000000000000000000001331644577000152135ustar00rootroot00000000000000log-1.1.0/handlers/level/level.go000066400000000000000000000007321331644577000166530ustar00rootroot00000000000000// Package level implements a level filter handler. package level import "github.com/apex/log" // Handler implementation. type Handler struct { Level log.Level Handler log.Handler } // New handler. func New(h log.Handler, level log.Level) *Handler { return &Handler{ Level: level, Handler: h, } } // HandleLog implements log.Handler. func (h *Handler) HandleLog(e *log.Entry) error { if e.Level < h.Level { return nil } return h.Handler.HandleLog(e) } log-1.1.0/handlers/level/level_test.go000066400000000000000000000007101331644577000177060ustar00rootroot00000000000000package level_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/apex/log" "github.com/apex/log/handlers/level" "github.com/apex/log/handlers/memory" ) func Test(t *testing.T) { h := memory.New() ctx := log.Logger{ Handler: level.New(h, log.ErrorLevel), Level: log.InfoLevel, } ctx.Info("hello") ctx.Info("world") ctx.Error("boom") assert.Len(t, h.Entries, 1) assert.Equal(t, h.Entries[0].Message, "boom") } log-1.1.0/handlers/logfmt/000077500000000000000000000000001331644577000153745ustar00rootroot00000000000000log-1.1.0/handlers/logfmt/logfmt.go000066400000000000000000000014661331644577000172220ustar00rootroot00000000000000// Package logfmt implements a "logfmt" format handler. package logfmt import ( "io" "os" "sync" "github.com/apex/log" "github.com/go-logfmt/logfmt" ) // Default handler outputting to stderr. var Default = New(os.Stderr) // Handler implementation. type Handler struct { mu sync.Mutex enc *logfmt.Encoder } // New handler. func New(w io.Writer) *Handler { return &Handler{ enc: logfmt.NewEncoder(w), } } // HandleLog implements log.Handler. func (h *Handler) HandleLog(e *log.Entry) error { names := e.Fields.Names() h.mu.Lock() defer h.mu.Unlock() h.enc.EncodeKeyval("timestamp", e.Timestamp) h.enc.EncodeKeyval("level", e.Level.String()) h.enc.EncodeKeyval("message", e.Message) for _, name := range names { h.enc.EncodeKeyval(name, e.Fields.Get(name)) } h.enc.EndRecord() return nil } log-1.1.0/handlers/logfmt/logfmt_test.go000066400000000000000000000015551331644577000202600ustar00rootroot00000000000000package logfmt_test import ( "bytes" "io/ioutil" "testing" "time" "github.com/stretchr/testify/assert" "github.com/apex/log" "github.com/apex/log/handlers/logfmt" ) func init() { log.Now = func() time.Time { return time.Unix(0, 0).UTC() } } func Test(t *testing.T) { var buf bytes.Buffer log.SetHandler(logfmt.New(&buf)) log.WithField("user", "tj").WithField("id", "123").Info("hello") log.Info("world") log.Error("boom") expected := `timestamp=1970-01-01T00:00:00Z level=info message=hello id=123 user=tj timestamp=1970-01-01T00:00:00Z level=info message=world timestamp=1970-01-01T00:00:00Z level=error message=boom ` assert.Equal(t, expected, buf.String()) } func Benchmark(b *testing.B) { log.SetHandler(logfmt.New(ioutil.Discard)) ctx := log.WithField("user", "tj").WithField("id", "123") for i := 0; i < b.N; i++ { ctx.Info("hello") } } log-1.1.0/handlers/memory/000077500000000000000000000000001331644577000154145ustar00rootroot00000000000000log-1.1.0/handlers/memory/memory.go000066400000000000000000000007651331644577000172630ustar00rootroot00000000000000// Package memory implements an in-memory handler useful for testing, as the // entries can be accessed after writes. package memory import ( "sync" "github.com/apex/log" ) // Handler implementation. type Handler struct { mu sync.Mutex Entries []*log.Entry } // New handler. func New() *Handler { return &Handler{} } // HandleLog implements log.Handler. func (h *Handler) HandleLog(e *log.Entry) error { h.mu.Lock() defer h.mu.Unlock() h.Entries = append(h.Entries, e) return nil } log-1.1.0/handlers/multi/000077500000000000000000000000001331644577000152365ustar00rootroot00000000000000log-1.1.0/handlers/multi/multi.go000066400000000000000000000012131331644577000167140ustar00rootroot00000000000000// Package multi implements a handler which invokes a number of handlers. package multi import ( "github.com/apex/log" ) // Handler implementation. type Handler struct { Handlers []log.Handler } // New handler. func New(h ...log.Handler) *Handler { return &Handler{ Handlers: h, } } // HandleLog implements log.Handler. func (h *Handler) HandleLog(e *log.Entry) error { for _, handler := range h.Handlers { // TODO(tj): maybe just write to stderr here, definitely not ideal // to miss out logging to a more critical handler if something // goes wrong if err := handler.HandleLog(e); err != nil { return err } } return nil } log-1.1.0/handlers/multi/multi_test.go000066400000000000000000000010211331644577000177500ustar00rootroot00000000000000package multi_test import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/apex/log" "github.com/apex/log/handlers/memory" "github.com/apex/log/handlers/multi" ) func init() { log.Now = func() time.Time { return time.Unix(0, 0) } } func Test(t *testing.T) { a := memory.New() b := memory.New() log.SetHandler(multi.New(a, b)) log.WithField("user", "tj").WithField("id", "123").Info("hello") log.Info("world") log.Error("boom") assert.Len(t, a.Entries, 3) assert.Len(t, b.Entries, 3) } log-1.1.0/handlers/papertrail/000077500000000000000000000000001331644577000162475ustar00rootroot00000000000000log-1.1.0/handlers/papertrail/papertrail.go000066400000000000000000000026601331644577000207450ustar00rootroot00000000000000// Package papertrail implements a papertrail logfmt format handler. package papertrail import ( "bytes" "fmt" "log/syslog" "net" "os" "sync" "time" "github.com/apex/log" "github.com/go-logfmt/logfmt" ) // TODO: syslog portion is ad-hoc for my serverless use-case, // I don't really need hostnames etc, but this should be improved // Config for Papertrail. type Config struct { // Papertrail settings. Host string // Host subdomain such as "logs4" Port int // Port number // Application settings Hostname string // Hostname value Tag string // Tag value } // Handler implementation. type Handler struct { *Config mu sync.Mutex conn net.Conn } // New handler. func New(config *Config) *Handler { conn, err := net.Dial("udp", fmt.Sprintf("%s.papertrailapp.com:%d", config.Host, config.Port)) if err != nil { panic(err) } return &Handler{ Config: config, conn: conn, } } // HandleLog implements log.Handler. func (h *Handler) HandleLog(e *log.Entry) error { ts := time.Now().Format(time.Stamp) var buf bytes.Buffer enc := logfmt.NewEncoder(&buf) enc.EncodeKeyval("level", e.Level.String()) enc.EncodeKeyval("message", e.Message) for k, v := range e.Fields { enc.EncodeKeyval(k, v) } enc.EndRecord() msg := []byte(fmt.Sprintf("<%d>%s %s %s[%d]: %s\n", syslog.LOG_KERN, ts, h.Hostname, h.Tag, os.Getpid(), buf.String())) h.mu.Lock() _, err := h.conn.Write(msg) h.mu.Unlock() return err } log-1.1.0/handlers/text/000077500000000000000000000000001331644577000150705ustar00rootroot00000000000000log-1.1.0/handlers/text/text.go000066400000000000000000000025161331644577000164070ustar00rootroot00000000000000// Package text implements a development-friendly textual handler. package text import ( "fmt" "io" "os" "sync" "time" "github.com/apex/log" ) // Default handler outputting to stderr. var Default = New(os.Stderr) // start time. var start = time.Now() // colors. const ( none = 0 red = 31 green = 32 yellow = 33 blue = 34 gray = 37 ) // Colors mapping. var Colors = [...]int{ log.DebugLevel: gray, log.InfoLevel: blue, log.WarnLevel: yellow, log.ErrorLevel: red, log.FatalLevel: red, } // Strings mapping. var Strings = [...]string{ log.DebugLevel: "DEBUG", log.InfoLevel: "INFO", log.WarnLevel: "WARN", log.ErrorLevel: "ERROR", log.FatalLevel: "FATAL", } // Handler implementation. type Handler struct { mu sync.Mutex Writer io.Writer } // New handler. func New(w io.Writer) *Handler { return &Handler{ Writer: w, } } // HandleLog implements log.Handler. func (h *Handler) HandleLog(e *log.Entry) error { color := Colors[e.Level] level := Strings[e.Level] names := e.Fields.Names() h.mu.Lock() defer h.mu.Unlock() ts := time.Since(start) / time.Second fmt.Fprintf(h.Writer, "\033[%dm%6s\033[0m[%04d] %-25s", color, level, ts, e.Message) for _, name := range names { fmt.Fprintf(h.Writer, " \033[%dm%s\033[0m=%v", color, name, e.Fields.Get(name)) } fmt.Fprintln(h.Writer) return nil } log-1.1.0/handlers/text/text_test.go000066400000000000000000000014201331644577000174370ustar00rootroot00000000000000package text_test import ( "bytes" "testing" "time" "github.com/stretchr/testify/assert" "github.com/apex/log" "github.com/apex/log/handlers/text" ) func init() { log.Now = func() time.Time { return time.Unix(0, 0) } } func Test(t *testing.T) { var buf bytes.Buffer log.SetHandler(text.New(&buf)) log.WithField("user", "tj").WithField("id", "123").Info("hello") log.WithField("user", "tj").Info("world") log.WithField("user", "tj").Error("boom") expected := "\x1b[34m INFO\x1b[0m[0000] hello \x1b[34mid\x1b[0m=123 \x1b[34muser\x1b[0m=tj\n\x1b[34m INFO\x1b[0m[0000] world \x1b[34muser\x1b[0m=tj\n\x1b[31m ERROR\x1b[0m[0000] boom \x1b[31muser\x1b[0m=tj\n" assert.Equal(t, expected, buf.String()) } log-1.1.0/interface.go000066400000000000000000000010101331644577000145630ustar00rootroot00000000000000package log // Interface represents the API of both Logger and Entry. type Interface interface { WithFields(fields Fielder) *Entry WithField(key string, value interface{}) *Entry WithError(err error) *Entry Debug(msg string) Info(msg string) Warn(msg string) Error(msg string) Fatal(msg string) Debugf(msg string, v ...interface{}) Infof(msg string, v ...interface{}) Warnf(msg string, v ...interface{}) Errorf(msg string, v ...interface{}) Fatalf(msg string, v ...interface{}) Trace(msg string) *Entry } log-1.1.0/levels.go000066400000000000000000000026011331644577000141240ustar00rootroot00000000000000package log import ( "bytes" "errors" "strings" ) // ErrInvalidLevel is returned if the severity level is invalid. var ErrInvalidLevel = errors.New("invalid level") // Level of severity. type Level int // Log levels. const ( InvalidLevel Level = iota - 1 DebugLevel InfoLevel WarnLevel ErrorLevel FatalLevel ) var levelNames = [...]string{ DebugLevel: "debug", InfoLevel: "info", WarnLevel: "warn", ErrorLevel: "error", FatalLevel: "fatal", } var levelStrings = map[string]Level{ "debug": DebugLevel, "info": InfoLevel, "warn": WarnLevel, "warning": WarnLevel, "error": ErrorLevel, "fatal": FatalLevel, } // String implementation. func (l Level) String() string { return levelNames[l] } // MarshalJSON implementation. func (l Level) MarshalJSON() ([]byte, error) { return []byte(`"` + l.String() + `"`), nil } // UnmarshalJSON implementation. func (l *Level) UnmarshalJSON(b []byte) error { v, err := ParseLevel(string(bytes.Trim(b, `"`))) if err != nil { return err } *l = v return nil } // ParseLevel parses level string. func ParseLevel(s string) (Level, error) { l, ok := levelStrings[strings.ToLower(s)] if !ok { return InvalidLevel, ErrInvalidLevel } return l, nil } // MustParseLevel parses level string or panics. func MustParseLevel(s string) Level { l, err := ParseLevel(s) if err != nil { panic("invalid log level") } return l } log-1.1.0/levels_test.go000066400000000000000000000023541331644577000151700ustar00rootroot00000000000000package log import ( "encoding/json" "testing" "github.com/stretchr/testify/assert" ) func TestParseLevel(t *testing.T) { cases := []struct { String string Level Level Num int }{ {"debug", DebugLevel, 0}, {"info", InfoLevel, 1}, {"warn", WarnLevel, 2}, {"warning", WarnLevel, 3}, {"error", ErrorLevel, 4}, {"fatal", FatalLevel, 5}, } for _, c := range cases { t.Run(c.String, func(t *testing.T) { l, err := ParseLevel(c.String) assert.NoError(t, err, "parse") assert.Equal(t, c.Level, l) }) } t.Run("invalid", func(t *testing.T) { l, err := ParseLevel("something") assert.Equal(t, ErrInvalidLevel, err) assert.Equal(t, InvalidLevel, l) }) } func TestLevel_MarshalJSON(t *testing.T) { e := Entry{ Level: InfoLevel, Message: "hello", Fields: Fields{}, } expect := `{"fields":{},"level":"info","timestamp":"0001-01-01T00:00:00Z","message":"hello"}` b, err := json.Marshal(e) assert.NoError(t, err) assert.Equal(t, expect, string(b)) } func TestLevel_UnmarshalJSON(t *testing.T) { s := `{"fields":{},"level":"info","timestamp":"0001-01-01T00:00:00Z","message":"hello"}` e := new(Entry) err := json.Unmarshal([]byte(s), e) assert.NoError(t, err) assert.Equal(t, InfoLevel, e.Level) } log-1.1.0/logger.go000066400000000000000000000067761331644577000141320ustar00rootroot00000000000000package log import ( stdlog "log" "sort" ) // assert interface compliance. var _ Interface = (*Logger)(nil) // Fielder is an interface for providing fields to custom types. type Fielder interface { Fields() Fields } // Fields represents a map of entry level data used for structured logging. type Fields map[string]interface{} // Fields implements Fielder. func (f Fields) Fields() Fields { return f } // Get field value by name. func (f Fields) Get(name string) interface{} { return f[name] } // Names returns field names sorted. func (f Fields) Names() (v []string) { for k := range f { v = append(v, k) } sort.Strings(v) return } // The HandlerFunc type is an adapter to allow the use of ordinary functions as // log handlers. If f is a function with the appropriate signature, // HandlerFunc(f) is a Handler object that calls f. type HandlerFunc func(*Entry) error // HandleLog calls f(e). func (f HandlerFunc) HandleLog(e *Entry) error { return f(e) } // Handler is used to handle log events, outputting them to // stdio or sending them to remote services. See the "handlers" // directory for implementations. // // It is left up to Handlers to implement thread-safety. type Handler interface { HandleLog(*Entry) error } // Logger represents a logger with configurable Level and Handler. type Logger struct { Handler Handler Level Level } // WithFields returns a new entry with `fields` set. func (l *Logger) WithFields(fields Fielder) *Entry { return NewEntry(l).WithFields(fields.Fields()) } // WithField returns a new entry with the `key` and `value` set. // // Note that the `key` should not have spaces in it - use camel // case or underscores func (l *Logger) WithField(key string, value interface{}) *Entry { return NewEntry(l).WithField(key, value) } // WithError returns a new entry with the "error" set to `err`. func (l *Logger) WithError(err error) *Entry { return NewEntry(l).WithError(err) } // Debug level message. func (l *Logger) Debug(msg string) { NewEntry(l).Debug(msg) } // Info level message. func (l *Logger) Info(msg string) { NewEntry(l).Info(msg) } // Warn level message. func (l *Logger) Warn(msg string) { NewEntry(l).Warn(msg) } // Error level message. func (l *Logger) Error(msg string) { NewEntry(l).Error(msg) } // Fatal level message, followed by an exit. func (l *Logger) Fatal(msg string) { NewEntry(l).Fatal(msg) } // Debugf level formatted message. func (l *Logger) Debugf(msg string, v ...interface{}) { NewEntry(l).Debugf(msg, v...) } // Infof level formatted message. func (l *Logger) Infof(msg string, v ...interface{}) { NewEntry(l).Infof(msg, v...) } // Warnf level formatted message. func (l *Logger) Warnf(msg string, v ...interface{}) { NewEntry(l).Warnf(msg, v...) } // Errorf level formatted message. func (l *Logger) Errorf(msg string, v ...interface{}) { NewEntry(l).Errorf(msg, v...) } // Fatalf level formatted message, followed by an exit. func (l *Logger) Fatalf(msg string, v ...interface{}) { NewEntry(l).Fatalf(msg, v...) } // Trace returns a new entry with a Stop method to fire off // a corresponding completion log, useful with defer. func (l *Logger) Trace(msg string) *Entry { return NewEntry(l).Trace(msg) } // log the message, invoking the handler. We clone the entry here // to bypass the overhead in Entry methods when the level is not // met. func (l *Logger) log(level Level, e *Entry, msg string) { if level < l.Level { return } if err := l.Handler.HandleLog(e.finalize(level, msg)); err != nil { stdlog.Printf("error logging: %s", err) } } log-1.1.0/logger_test.go000066400000000000000000000115631331644577000151570ustar00rootroot00000000000000package log_test import ( "fmt" "testing" "time" "github.com/apex/log" "github.com/apex/log/handlers/discard" "github.com/apex/log/handlers/memory" "github.com/stretchr/testify/assert" ) func TestLogger_printf(t *testing.T) { h := memory.New() l := &log.Logger{ Handler: h, Level: log.InfoLevel, } l.Infof("logged in %s", "Tobi") e := h.Entries[0] assert.Equal(t, e.Message, "logged in Tobi") assert.Equal(t, e.Level, log.InfoLevel) } func TestLogger_levels(t *testing.T) { h := memory.New() l := &log.Logger{ Handler: h, Level: log.InfoLevel, } l.Debug("uploading") l.Info("upload complete") assert.Equal(t, 1, len(h.Entries)) e := h.Entries[0] assert.Equal(t, e.Message, "upload complete") assert.Equal(t, e.Level, log.InfoLevel) } func TestLogger_WithFields(t *testing.T) { h := memory.New() l := &log.Logger{ Handler: h, Level: log.InfoLevel, } ctx := l.WithFields(log.Fields{"file": "sloth.png"}) ctx.Debug("uploading") ctx.Info("upload complete") assert.Equal(t, 1, len(h.Entries)) e := h.Entries[0] assert.Equal(t, e.Message, "upload complete") assert.Equal(t, e.Level, log.InfoLevel) assert.Equal(t, log.Fields{"file": "sloth.png"}, e.Fields) } func TestLogger_WithField(t *testing.T) { h := memory.New() l := &log.Logger{ Handler: h, Level: log.InfoLevel, } ctx := l.WithField("file", "sloth.png").WithField("user", "Tobi") ctx.Debug("uploading") ctx.Info("upload complete") assert.Equal(t, 1, len(h.Entries)) e := h.Entries[0] assert.Equal(t, e.Message, "upload complete") assert.Equal(t, e.Level, log.InfoLevel) assert.Equal(t, log.Fields{"file": "sloth.png", "user": "Tobi"}, e.Fields) } func TestLogger_Trace_info(t *testing.T) { h := memory.New() l := &log.Logger{ Handler: h, Level: log.InfoLevel, } func() (err error) { defer l.WithField("file", "sloth.png").Trace("upload").Stop(&err) return nil }() assert.Equal(t, 2, len(h.Entries)) { e := h.Entries[0] assert.Equal(t, e.Message, "upload") assert.Equal(t, e.Level, log.InfoLevel) assert.Equal(t, log.Fields{"file": "sloth.png"}, e.Fields) } { e := h.Entries[1] assert.Equal(t, e.Message, "upload") assert.Equal(t, e.Level, log.InfoLevel) assert.Equal(t, "sloth.png", e.Fields["file"]) assert.IsType(t, time.Duration(0), e.Fields["duration"]) } } func TestLogger_Trace_error(t *testing.T) { h := memory.New() l := &log.Logger{ Handler: h, Level: log.InfoLevel, } func() (err error) { defer l.WithField("file", "sloth.png").Trace("upload").Stop(&err) return fmt.Errorf("boom") }() assert.Equal(t, 2, len(h.Entries)) { e := h.Entries[0] assert.Equal(t, e.Message, "upload") assert.Equal(t, e.Level, log.InfoLevel) assert.Equal(t, "sloth.png", e.Fields["file"]) } { e := h.Entries[1] assert.Equal(t, e.Message, "upload") assert.Equal(t, e.Level, log.ErrorLevel) assert.Equal(t, "sloth.png", e.Fields["file"]) assert.Equal(t, "boom", e.Fields["error"]) assert.IsType(t, time.Duration(0), e.Fields["duration"]) } } func TestLogger_Trace_nil(t *testing.T) { h := memory.New() l := &log.Logger{ Handler: h, Level: log.InfoLevel, } func() { defer l.WithField("file", "sloth.png").Trace("upload").Stop(nil) }() assert.Equal(t, 2, len(h.Entries)) { e := h.Entries[0] assert.Equal(t, e.Message, "upload") assert.Equal(t, e.Level, log.InfoLevel) assert.Equal(t, log.Fields{"file": "sloth.png"}, e.Fields) } { e := h.Entries[1] assert.Equal(t, e.Message, "upload") assert.Equal(t, e.Level, log.InfoLevel) assert.Equal(t, "sloth.png", e.Fields["file"]) assert.IsType(t, time.Duration(0), e.Fields["duration"]) } } func TestLogger_HandlerFunc(t *testing.T) { h := memory.New() f := func(e *log.Entry) error { return h.HandleLog(e) } l := &log.Logger{ Handler: log.HandlerFunc(f), Level: log.InfoLevel, } l.Infof("logged in %s", "Tobi") e := h.Entries[0] assert.Equal(t, e.Message, "logged in Tobi") assert.Equal(t, e.Level, log.InfoLevel) } func BenchmarkLogger_small(b *testing.B) { l := &log.Logger{ Handler: discard.New(), Level: log.InfoLevel, } for i := 0; i < b.N; i++ { l.Info("login") } } func BenchmarkLogger_medium(b *testing.B) { l := &log.Logger{ Handler: discard.New(), Level: log.InfoLevel, } for i := 0; i < b.N; i++ { l.WithFields(log.Fields{ "file": "sloth.png", "type": "image/png", "size": 1 << 20, }).Info("upload") } } func BenchmarkLogger_large(b *testing.B) { l := &log.Logger{ Handler: discard.New(), Level: log.InfoLevel, } err := fmt.Errorf("boom") for i := 0; i < b.N; i++ { l.WithFields(log.Fields{ "file": "sloth.png", "type": "image/png", "size": 1 << 20, }). WithFields(log.Fields{ "some": "more", "data": "here", "whatever": "blah blah", "more": "stuff", "context": "such useful", "much": "fun", }). WithError(err).Error("upload failed") } } log-1.1.0/pkg.go000066400000000000000000000041671331644577000134240ustar00rootroot00000000000000package log // singletons ftw? var Log Interface = &Logger{ Handler: HandlerFunc(handleStdLog), Level: InfoLevel, } // SetHandler sets the handler. This is not thread-safe. // The default handler outputs to the stdlib log. func SetHandler(h Handler) { if logger, ok := Log.(*Logger); ok { logger.Handler = h } } // SetLevel sets the log level. This is not thread-safe. func SetLevel(l Level) { if logger, ok := Log.(*Logger); ok { logger.Level = l } } // SetLevelFromString sets the log level from a string, panicing when invalid. This is not thread-safe. func SetLevelFromString(s string) { if logger, ok := Log.(*Logger); ok { logger.Level = MustParseLevel(s) } } // WithFields returns a new entry with `fields` set. func WithFields(fields Fielder) *Entry { return Log.WithFields(fields) } // WithField returns a new entry with the `key` and `value` set. func WithField(key string, value interface{}) *Entry { return Log.WithField(key, value) } // WithError returns a new entry with the "error" set to `err`. func WithError(err error) *Entry { return Log.WithError(err) } // Debug level message. func Debug(msg string) { Log.Debug(msg) } // Info level message. func Info(msg string) { Log.Info(msg) } // Warn level message. func Warn(msg string) { Log.Warn(msg) } // Error level message. func Error(msg string) { Log.Error(msg) } // Fatal level message, followed by an exit. func Fatal(msg string) { Log.Fatal(msg) } // Debugf level formatted message. func Debugf(msg string, v ...interface{}) { Log.Debugf(msg, v...) } // Infof level formatted message. func Infof(msg string, v ...interface{}) { Log.Infof(msg, v...) } // Warnf level formatted message. func Warnf(msg string, v ...interface{}) { Log.Warnf(msg, v...) } // Errorf level formatted message. func Errorf(msg string, v ...interface{}) { Log.Errorf(msg, v...) } // Fatalf level formatted message, followed by an exit. func Fatalf(msg string, v ...interface{}) { Log.Fatalf(msg, v...) } // Trace returns a new entry with a Stop method to fire off // a corresponding completion log, useful with defer. func Trace(msg string) *Entry { return Log.Trace(msg) } log-1.1.0/pkg_test.go000066400000000000000000000031461331644577000144570ustar00rootroot00000000000000package log_test import ( "errors" "testing" "github.com/apex/log" "github.com/apex/log/handlers/memory" "github.com/stretchr/testify/assert" ) type Pet struct { Name string Age int } func (p *Pet) Fields() log.Fields { return log.Fields{ "name": p.Name, "age": p.Age, } } func TestInfo(t *testing.T) { h := memory.New() log.SetHandler(h) log.Infof("logged in %s", "Tobi") e := h.Entries[0] assert.Equal(t, e.Message, "logged in Tobi") assert.Equal(t, e.Level, log.InfoLevel) } func TestFielder(t *testing.T) { h := memory.New() log.SetHandler(h) pet := &Pet{"Tobi", 3} log.WithFields(pet).Info("add pet") e := h.Entries[0] assert.Equal(t, log.Fields{"name": "Tobi", "age": 3}, e.Fields) } // Unstructured logging is supported, but not recommended since it is hard to query. func Example_unstructured() { log.Infof("%s logged in", "Tobi") } // Structured logging is supported with fields, and is recommended over the formatted message variants. func Example_structured() { log.WithField("user", "Tobo").Info("logged in") } // Errors are passed to WithError(), populating the "error" field. func Example_errors() { err := errors.New("boom") log.WithError(err).Error("upload failed") } // Multiple fields can be set, via chaining, or WithFields(). func Example_multipleFields() { log.WithFields(log.Fields{ "user": "Tobi", "file": "sloth.png", "type": "image/png", }).Info("upload") } // Trace can be used to simplify logging of start and completion events, // for example an upload which may fail. func Example_trace() (err error) { defer log.Trace("upload").Stop(&err) return nil } log-1.1.0/stack.go000066400000000000000000000002061331644577000137360ustar00rootroot00000000000000package log import "github.com/pkg/errors" // stackTracer interface. type stackTracer interface { StackTrace() errors.StackTrace }