From 079bdbacdfd63cd947b2fb75ece01824af07cfbc Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 11 Nov 2014 15:12:57 -0800 Subject: [PATCH 01/42] doc(peer) question --- peer/peerstore.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/peer/peerstore.go b/peer/peerstore.go index 9513c6a9332..dcdfd95d15d 100644 --- a/peer/peerstore.go +++ b/peer/peerstore.go @@ -47,6 +47,17 @@ func (p *peerstore) Get(i ID) (Peer, error) { // not found, construct it ourselves, add it to datastore, and return. case ds.ErrNotFound: + + // TODO(brian) kinda dangerous, no? If ID is invalid and doesn't + // correspond to an actual valid peer ID, this peerstore will return an + // instantiated peer value, allowing the error to propagate. It might + // be better to nip this at the bud by returning nil and making the + // client manually add a Peer. To keep the peerstore in control, this + // can even be a peerstore method that performs cursory validation. + // + // Potential bad case: Suppose values arrive from untrusted providers + // in the DHT. + peer := &peer{id: i} if err := p.peers.Put(k, peer); err != nil { return nil, err From 027de2847a068a0d7f64dc0b8e6f12060547bca7 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 11 Nov 2014 14:36:53 -0800 Subject: [PATCH 02/42] feat(log) time --- util/log/time.go | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 util/log/time.go diff --git a/util/log/time.go b/util/log/time.go new file mode 100644 index 00000000000..d66ebc3e48a --- /dev/null +++ b/util/log/time.go @@ -0,0 +1,8 @@ +package log + +import "time" + +// Time returns a nanosecond-precision timestamp in zone UTC +func Time() string { + return time.Now().UTC().Format(time.RFC3339Nano) +} From 1ed0850ce220c630b61d7890d4b587de930cc30c Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 11 Nov 2014 15:19:57 -0800 Subject: [PATCH 03/42] style(core) peer -> self --- core/core.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/core.go b/core/core.go index d3a0dcb857f..04810a42610 100644 --- a/core/core.go +++ b/core/core.go @@ -207,7 +207,7 @@ func initIdentity(cfg *config.Config, peers peer.Peerstore, online bool) (peer.P // get peer from peerstore (so it is constructed there) id := peer.ID(b58.Decode(cfg.Identity.PeerID)) - peer, err := peers.Get(id) + self, err := peers.Get(id) if err != nil { return nil, err } @@ -219,12 +219,12 @@ func initIdentity(cfg *config.Config, peers peer.Peerstore, online bool) (peer.P return nil, err } - if err := peer.LoadAndVerifyKeyPair(skb); err != nil { + if err := self.LoadAndVerifyKeyPair(skb); err != nil { return nil, err } } - return peer, nil + return self, nil } func initConnections(ctx context.Context, cfg *config.Config, pstore peer.Peerstore, route *dht.IpfsDHT) { From 05346dd927d7d5b8f2142ceb6b4da71d435c2f2f Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 11 Nov 2014 15:53:44 -0800 Subject: [PATCH 04/42] misc(core/identity) more specific function argument to aid in testing --- core/core.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/core.go b/core/core.go index 04810a42610..8a617015a5a 100644 --- a/core/core.go +++ b/core/core.go @@ -121,7 +121,7 @@ func NewIpfsNode(cfg *config.Config, online bool) (n *IpfsNode, err error) { // setup peerstore + local peer identity n.Peerstore = peer.NewPeerstore() - n.Identity, err = initIdentity(n.Config, n.Peerstore, online) + n.Identity, err = initIdentity(&n.Config.Identity, n.Peerstore, online) if err != nil { return nil, err } @@ -196,17 +196,17 @@ func (n *IpfsNode) OnlineMode() bool { return n.onlineMode } -func initIdentity(cfg *config.Config, peers peer.Peerstore, online bool) (peer.Peer, error) { - if cfg.Identity.PeerID == "" { +func initIdentity(cfg *config.Identity, peers peer.Peerstore, online bool) (peer.Peer, error) { + if cfg.PeerID == "" { return nil, errors.New("Identity was not set in config (was ipfs init run?)") } - if len(cfg.Identity.PeerID) == 0 { + if len(cfg.PeerID) == 0 { return nil, errors.New("No peer ID in config! (was ipfs init run?)") } // get peer from peerstore (so it is constructed there) - id := peer.ID(b58.Decode(cfg.Identity.PeerID)) + id := peer.ID(b58.Decode(cfg.PeerID)) self, err := peers.Get(id) if err != nil { return nil, err @@ -214,7 +214,7 @@ func initIdentity(cfg *config.Config, peers peer.Peerstore, online bool) (peer.P // when not online, don't need to parse private keys (yet) if online { - skb, err := base64.StdEncoding.DecodeString(cfg.Identity.PrivKey) + skb, err := base64.StdEncoding.DecodeString(cfg.PrivKey) if err != nil { return nil, err } From ac84515441d3a522a324320b3391f6428cb3e9c1 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 11 Nov 2014 16:29:04 -0800 Subject: [PATCH 05/42] style(core_test) move test variable --- core/core_test.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/core_test.go b/core/core_test.go index 60555c845af..fb632d33655 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -7,10 +7,7 @@ import ( ) func TestInitialization(t *testing.T) { - id := config.Identity{ - PeerID: "QmNgdzLieYi8tgfo2WfTUzNVH5hQK9oAYGVf6dxN12NrHt", - PrivKey: "CAASrRIwggkpAgEAAoICAQCwt67GTUQ8nlJhks6CgbLKOx7F5tl1r9zF4m3TUrG3Pe8h64vi+ILDRFd7QJxaJ/n8ux9RUDoxLjzftL4uTdtv5UXl2vaufCc/C0bhCRvDhuWPhVsD75/DZPbwLsepxocwVWTyq7/ZHsCfuWdoh/KNczfy+Gn33gVQbHCnip/uhTVxT7ARTiv8Qa3d7qmmxsR+1zdL/IRO0mic/iojcb3Oc/PRnYBTiAZFbZdUEit/99tnfSjMDg02wRayZaT5ikxa6gBTMZ16Yvienq7RwSELzMQq2jFA4i/TdiGhS9uKywltiN2LrNDBcQJSN02pK12DKoiIy+wuOCRgs2NTQEhU2sXCk091v7giTTOpFX2ij9ghmiRfoSiBFPJA5RGwiH6ansCHtWKY1K8BS5UORM0o3dYk87mTnKbCsdz4bYnGtOWafujYwzueGx8r+IWiys80IPQKDeehnLW6RgoyjszKgL/2XTyP54xMLSW+Qb3BPgDcPaPO0hmop1hW9upStxKsefW2A2d46Ds4HEpJEry7PkS5M4gKL/zCKHuxuXVk14+fZQ1rstMuvKjrekpAC2aVIKMI9VRA3awtnje8HImQMdj+r+bPmv0N8rTTr3eS4J8Yl7k12i95LLfK+fWnmUh22oTNzkRlaiERQrUDyE4XNCtJc0xs1oe1yXGqazCIAQIDAQABAoICAQCk1N/ftahlRmOfAXk//8wNl7FvdJD3le6+YSKBj0uWmN1ZbUSQk64chr12iGCOM2WY180xYjy1LOS44PTXaeW5bEiTSnb3b3SH+HPHaWCNM2EiSogHltYVQjKW+3tfH39vlOdQ9uQ+l9Gh6iTLOqsCRyszpYPqIBwi1NMLY2Ej8PpVU7ftnFWouHZ9YKS7nAEiMoowhTu/7cCIVwZlAy3AySTuKxPMVj9LORqC32PVvBHZaMPJ+X1Xyijqg6aq39WyoztkXg3+Xxx5j5eOrK6vO/Lp6ZUxaQilHDXoJkKEJjgIBDZpluss08UPfOgiWAGkW+L4fgUxY0qDLDAEMhyEBAn6KOKVL1JhGTX6GjhWziI94bddSpHKYOEIDzUy4H8BXnKhtnyQV6ELS65C2hj9D0IMBTj7edCF1poJy0QfdK0cuXgMvxHLeUO5uc2YWfbNosvKxqygB9rToy4b22YvNwsZUXsTY6Jt+p9V2OgXSKfB5VPeRbjTJL6xqvvUJpQytmII/C9JmSDUtCbYceHj6X9jgigLk20VV6nWHqCTj3utXD6NPAjoycVpLKDlnWEgfVELDIk0gobxUqqSm3jTPEKRPJgxkgPxbwxYumtw++1UY2y35w3WRDc2xYPaWKBCQeZy+mL6ByXp9bWlNvxS3Knb6oZp36/ovGnf2pGvdQKCAQEAyKpipz2lIUySDyE0avVWAmQb2tWGKXALPohzj7AwkcfEg2GuwoC6GyVE2sTJD1HRazIjOKn3yQORg2uOPeG7sx7EKHxSxCKDrbPawkvLCq8JYSy9TLvhqKUVVGYPqMBzu2POSLEA81QXas+aYjKOFWA2Zrjq26zV9ey3+6Lc6WULePgRQybU8+RHJc6fdjUCCfUxgOrUO2IQOuTJ+FsDpVnrMUGlokmWn23OjL4qTL9wGDnWGUs2pjSzNbj3qA0d8iqaiMUyHX/D/VS0wpeT1osNBSm8suvSibYBn+7wbIApbwXUxZaxMv2OHGz3empae4ckvNZs7r8wsI9UwFt8mwKCAQEA4XK6gZkv9t+3YCcSPw2ensLvL/xU7i2bkC9tfTGdjnQfzZXIf5KNdVuj/SerOl2S1s45NMs3ysJbADwRb4ahElD/V71nGzV8fpFTitC20ro9fuX4J0+twmBolHqeH9pmeGTjAeL1rvt6vxs4FkeG/yNft7GdXpXTtEGaObn8Mt0tPY+aB3UnKrnCQoQAlPyGHFrVRX0UEcp6wyyNGhJCNKeNOvqCHTFObhbhO+KWpWSN0MkVHnqaIBnIn1Te8FtvP/iTwXGnKc0YXJUG6+LM6LmOguW6tg8ZqiQeYyyR+e9eCFH4csLzkrTl1GxCxwEsoSLIMm7UDcjttW6tYEghkwKCAQEAmeCO5lCPYImnN5Lu71ZTLmI2OgmjaANTnBBnDbi+hgv61gUCToUIMejSdDCTPfwv61P3TmyIZs0luPGxkiKYHTNqmOE9Vspgz8Mr7fLRMNApESuNvloVIY32XVImj/GEzh4rAfM6F15U1sN8T/EUo6+0B/Glp+9R49QzAfRSE2g48/rGwgf1JVHYfVWFUtAzUA+GdqWdOixo5cCsYJbqpNHfWVZN/bUQnBFIYwUwysnC29D+LUdQEQQ4qOm+gFAOtrWU62zMkXJ4iLt8Ify6kbrvsRXgbhQIzzGS7WH9XDarj0eZciuslr15TLMC1Azadf+cXHLR9gMHA13mT9vYIQKCAQA/DjGv8cKCkAvf7s2hqROGYAs6Jp8yhrsN1tYOwAPLRhtnCs+rLrg17M2vDptLlcRuI/vIElamdTmylRpjUQpX7yObzLO73nfVhpwRJVMdGU394iBIDncQ+JoHfUwgqJskbUM40dvZdyjbrqc/Q/4z+hbZb+oN/GXb8sVKBATPzSDMKQ/xqgisYIw+wmDPStnPsHAaIWOtni47zIgilJzD0WEk78/YjmPbUrboYvWziK5JiRRJFA1rkQqV1c0M+OXixIm+/yS8AksgCeaHr0WUieGcJtjT9uE8vyFop5ykhRiNxy9wGaq6i7IEecsrkd6DqxDHWkwhFuO1bSE83q/VAoIBAEA+RX1i/SUi08p71ggUi9WFMqXmzELp1L3hiEjOc2AklHk2rPxsaTh9+G95BvjhP7fRa/Yga+yDtYuyjO99nedStdNNSg03aPXILl9gs3r2dPiQKUEXZJ3FrH6tkils/8BlpOIRfbkszrdZIKTO9GCdLWQ30dQITDACs8zV/1GFGrHFrqnnMe/NpIFHWNZJ0/WZMi8wgWO6Ik8jHEpQtVXRiXLqy7U6hk170pa4GHOzvftfPElOZZjy9qn7KjdAQqy6spIrAE94OEL+fBgbHQZGLpuTlj6w6YGbMtPU8uo7sXKoc6WOCb68JWft3tejGLDa1946HAWqVM9B/UcneNc=", - } + id := testIdentity good := []*config.Config{ &config.Config{ @@ -59,3 +56,8 @@ func TestInitialization(t *testing.T) { } } } + +var testIdentity = config.Identity{ + PeerID: "QmNgdzLieYi8tgfo2WfTUzNVH5hQK9oAYGVf6dxN12NrHt", + PrivKey: "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", +} From a11c59dcacfd2d7f601f9b126e3822987459fef9 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 11 Nov 2014 16:30:22 -0800 Subject: [PATCH 06/42] feat(peer) add Type (Unspecified, Local, Remote) --- core/core.go | 5 +++++ core/core_test.go | 17 +++++++++++++++++ peer/peer.go | 30 ++++++++++++++++++++++++++++++ peer/peer_test.go | 8 ++++++++ 4 files changed, 60 insertions(+) diff --git a/core/core.go b/core/core.go index 8a617015a5a..3c3da7ad6b2 100644 --- a/core/core.go +++ b/core/core.go @@ -211,6 +211,11 @@ func initIdentity(cfg *config.Identity, peers peer.Peerstore, online bool) (peer if err != nil { return nil, err } + self.SetType(peer.Local) + self, err = peers.Add(self) + if err != nil { + return nil, err + } // when not online, don't need to parse private keys (yet) if online { diff --git a/core/core_test.go b/core/core_test.go index fb632d33655..8c01b350aec 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -4,6 +4,7 @@ import ( "testing" config "github.com/jbenet/go-ipfs/config" + "github.com/jbenet/go-ipfs/peer" ) func TestInitialization(t *testing.T) { @@ -57,6 +58,22 @@ func TestInitialization(t *testing.T) { } } +func TestPeerIsLocal(t *testing.T) { + t.Log("Ensure that peer is Local after initializing identity") + + online := false + peers := peer.NewPeerstore() + + cfg := testIdentity + p, err := initIdentity(&cfg, peers, online) + if err != nil { + t.Fatal(err) + } + if p.GetType() != peer.Local { + t.Fail() + } +} + var testIdentity = config.Identity{ PeerID: "QmNgdzLieYi8tgfo2WfTUzNVH5hQK9oAYGVf6dxN12NrHt", PrivKey: "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", diff --git a/peer/peer.go b/peer/peer.go index 5496e610c59..fcaadce3ad5 100644 --- a/peer/peer.go +++ b/peer/peer.go @@ -95,10 +95,23 @@ type Peer interface { GetLatency() (out time.Duration) SetLatency(laten time.Duration) + // Get/SetType indicate whether this is a local or remote peer + GetType() Type + SetType(Type) + // Update with the data of another peer instance Update(Peer) error } +type Type uint8 + +const ( + // Unspecified indicates peer was created without specifying Type + Unspecified Type = iota + Local + Remote +) + type peer struct { id ID addresses []ma.Multiaddr @@ -111,6 +124,9 @@ type peer struct { // within that package, map from ID to latency value. latency time.Duration + // typ can be Local, Remote, or Unspecified (default) + typ Type + sync.RWMutex } @@ -222,6 +238,18 @@ func (p *peer) SetLatency(laten time.Duration) { p.Unlock() } +func (p *peer) SetType(t Type) { + p.Lock() + p.typ = t + defer p.Unlock() +} + +func (p *peer) GetType() Type { + p.Lock() + defer p.Unlock() + return p.typ +} + // LoadAndVerifyKeyPair unmarshalls, loads a private/public key pair. // Error if (a) unmarshalling fails, or (b) pubkey does not match id. func (p *peer) LoadAndVerifyKeyPair(marshalled []byte) error { @@ -306,6 +334,8 @@ func (p *peer) Update(other Peer) error { p.SetLatency(other.GetLatency()) + p.SetType(other.GetType()) + sk := other.PrivKey() pk := other.PubKey() p.Lock() diff --git a/peer/peer_test.go b/peer/peer_test.go index 5ca4bbd4b55..4a7aad9e80b 100644 --- a/peer/peer_test.go +++ b/peer/peer_test.go @@ -55,3 +55,11 @@ func TestStringMethodWithSmallId(t *testing.T) { } p1.String() } + +func TestDefaultType(t *testing.T) { + t.Log("Ensure that peers are initialized to Unspecified by default") + p := peer{} + if p.GetType() != Unspecified { + t.Fatalf("Peer's default type is was not `Unspecified`") + } +} From 0108968eb9ad7cdf90bda07ab077767295c58846 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 13 Nov 2014 18:18:13 -0800 Subject: [PATCH 07/42] feat(util/time) impl RFC3339Nano UTC utility Format/Parse functions test(time) expose time format var rename time format misc(util/time) don't need this anymore License: MIT Signed-off-by: Brian Tiger Chow --- util/log/time.go | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 util/log/time.go diff --git a/util/log/time.go b/util/log/time.go deleted file mode 100644 index d66ebc3e48a..00000000000 --- a/util/log/time.go +++ /dev/null @@ -1,8 +0,0 @@ -package log - -import "time" - -// Time returns a nanosecond-precision timestamp in zone UTC -func Time() string { - return time.Now().UTC().Format(time.RFC3339Nano) -} From 2c88e342dbfed3492c8c7a782279dcdfb902207f Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sat, 15 Nov 2014 16:08:11 -0800 Subject: [PATCH 08/42] feat(elog) implement event logger a wrapper around the util.Logger metadata is loggable License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/main.go | 4 +- util/elog/context.go | 35 +++++++++++++++ util/elog/context_test.go | 44 +++++++++++++++++++ util/elog/log.go | 75 ++++++++++++++++++++++++++++++++ util/elog/metadata.go | 87 ++++++++++++++++++++++++++++++++++++++ util/elog/metadata_test.go | 48 +++++++++++++++++++++ 6 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 util/elog/context.go create mode 100644 util/elog/context_test.go create mode 100644 util/elog/log.go create mode 100644 util/elog/metadata.go create mode 100644 util/elog/metadata_test.go diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 58ab8020e93..ca474c91119 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -9,6 +9,7 @@ import ( "runtime/pprof" "syscall" + // TODO rm direct reference to go-logging logging "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-logging" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net" @@ -22,10 +23,11 @@ import ( updates "github.com/jbenet/go-ipfs/updates" u "github.com/jbenet/go-ipfs/util" "github.com/jbenet/go-ipfs/util/debugerror" + elog "github.com/jbenet/go-ipfs/util/elog" ) // log is the command logger -var log = u.Logger("cmd/ipfs") +var log = elog.Logger("cmd/ipfs") // signal to output help var errHelpRequested = errors.New("Help Requested") diff --git a/util/elog/context.go b/util/elog/context.go new file mode 100644 index 00000000000..92e80ce8909 --- /dev/null +++ b/util/elog/context.go @@ -0,0 +1,35 @@ +package elog + +import ( + "errors" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" +) + +type key int + +const metadataKey key = 0 + +func ContextWithMetadata(ctx context.Context, l Loggable) context.Context { + existing, err := MetadataFromContext(ctx) + if err != nil { + // context does not contain meta. just set the new metadata + child := context.WithValue(ctx, metadataKey, l.Loggable()) + return child + } + + merged := DeepMerge(existing, l.Loggable()) + child := context.WithValue(ctx, metadataKey, merged) + return child +} + +func MetadataFromContext(ctx context.Context) (Metadata, error) { + value := ctx.Value(metadataKey) + if value != nil { + metadata, ok := value.(Metadata) + if ok { + return metadata, nil + } + } + return nil, errors.New("context contains no metadata") +} diff --git a/util/elog/context_test.go b/util/elog/context_test.go new file mode 100644 index 00000000000..c973a104b3e --- /dev/null +++ b/util/elog/context_test.go @@ -0,0 +1,44 @@ +package elog + +import ( + "testing" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" +) + +func TestContextContainsMetadata(t *testing.T) { + t.Parallel() + + m := Metadata{"foo": "bar"} + ctx := ContextWithMetadata(context.Background(), m) + got, err := MetadataFromContext(ctx) + if err != nil { + t.Fatal(err) + } + + _, exists := got["foo"] + if !exists { + t.Fail() + } +} + +func TestContextWithPreexistingMetadata(t *testing.T) { + t.Parallel() + + ctx := ContextWithMetadata(context.Background(), Metadata{"hello": "world"}) + ctx = ContextWithMetadata(ctx, Metadata{"goodbye": "earth"}) + + got, err := MetadataFromContext(ctx) + if err != nil { + t.Fatal(err) + } + + _, exists := got["hello"] + if !exists { + t.Fatal("original key not present") + } + _, exists = got["goodbye"] + if !exists { + t.Fatal("new key not present") + } +} diff --git a/util/elog/log.go b/util/elog/log.go new file mode 100644 index 00000000000..dbe1914a023 --- /dev/null +++ b/util/elog/log.go @@ -0,0 +1,75 @@ +package elog + +import ( + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + logging "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-logging" + "github.com/jbenet/go-ipfs/util" +) + +var eloggers = map[string]*logging.Logger{} + +func init() { + SetupLogging() +} + +type EventLogger interface { + StandardLogger + Event(ctx context.Context, event string, m ...Metadata) +} + +type StandardLogger interface { + Critical(args ...interface{}) + Criticalf(format string, args ...interface{}) + Debug(args ...interface{}) + Debugf(format string, args ...interface{}) + Error(args ...interface{}) + Errorf(format string, args ...interface{}) + Fatal(args ...interface{}) + Fatalf(format string, args ...interface{}) + Info(args ...interface{}) + Infof(format string, args ...interface{}) + Notice(args ...interface{}) + Noticef(format string, args ...interface{}) + Panic(args ...interface{}) + Panicf(format string, args ...interface{}) + Warning(args ...interface{}) + Warningf(format string, args ...interface{}) +} + +// Logger retrieves a particular event logger +func Logger(system string) EventLogger { + return &eventLogger{util.Logger(system)} +} + +// eventLogger implements the EventLogger and wraps a go-logging Logger +type eventLogger struct { + *logging.Logger +} + +func (el *eventLogger) Event(ctx context.Context, event string, metadata ...Metadata) { + existing, err := MetadataFromContext(ctx) + if err != nil { + existing = Metadata{} + } + accum := existing + for _, datum := range metadata { + accum = DeepMerge(accum, datum) + } + accum["event"] = event + + str, err := accum.JsonString() + if err != nil { + return + } + el.Logger.Info(str) +} + +// SetupLogging will initialize the logger backend and set the flags. +func SetupLogging() { + // fmt := logging.DefaultFormatter + + // f, err := os.Create("events.ipfslog") + // if err != nil { + // panic("failed to open file for event logger") + // } +} diff --git a/util/elog/metadata.go b/util/elog/metadata.go new file mode 100644 index 00000000000..22c8b2a2aa6 --- /dev/null +++ b/util/elog/metadata.go @@ -0,0 +1,87 @@ +package elog + +import ( + "encoding/json" + "errors" + "reflect" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid" +) + +// Metadata is a convenience type for generic maps +type Metadata map[string]interface{} + +// Loggable describes objects that can be marshalled into Metadata for logging +type Loggable interface { + Loggable() Metadata +} + +// UniqueEvent returns a Metadata with the string key and UUID value +func UniqueEvent(key string) Metadata { + return Metadata{ + key: uuid.New(), + } +} + +// DeepMerge merges the second Metadata parameter into the first. +// Nested Metadata are merged recursively. Primitives are over-written. +func DeepMerge(b, a Metadata) Metadata { + out := Metadata{} + for k, v := range b { + out[k] = v + } + for k, v := range a { + + maybe, err := Metadatify(v) + if err != nil { + // if the new value is not meta. just overwrite the dest vaue + out[k] = v + continue + } + + // it is meta. What about dest? + outv, exists := out[k] + if !exists { + // the new value is meta, but there's no dest value. just write it + out[k] = v + continue + } + + outMetadataValue, err := Metadatify(outv) + if err != nil { + // the new value is meta and there's a dest value, but the dest + // value isn't meta. just overwrite + out[k] = v + continue + } + + // both are meta. merge them. + out[k] = DeepMerge(outMetadataValue, maybe) + } + return out +} + +// Loggable implements the Loggable interface +func (m Metadata) Loggable() Metadata { + // NB: method defined on value to avoid de-referencing nil Metadata + return m +} + +func (m Metadata) JsonString() (string, error) { + // NB: method defined on value + b, err := json.Marshal(m) + return string(b), err +} + +// Metadatify converts maps into Metadata +func Metadatify(i interface{}) (Metadata, error) { + value := reflect.ValueOf(i) + if value.Kind() == reflect.Map { + m := map[string]interface{}{} + for _, k := range value.MapKeys() { + m[k.String()] = value.MapIndex(k).Interface() + } + return Metadata(m), nil + } + return nil, errors.New("is not a map") +} diff --git a/util/elog/metadata_test.go b/util/elog/metadata_test.go new file mode 100644 index 00000000000..2abfc6d7ee5 --- /dev/null +++ b/util/elog/metadata_test.go @@ -0,0 +1,48 @@ +package elog + +import "testing" + +func TestOverwrite(t *testing.T) { + t.Parallel() + + under := Metadata{ + "a": Metadata{ + "b": Metadata{ + "c": Metadata{ + "d": "the original value", + "other": "SURVIVE", + }, + }, + }, + } + over := Metadata{ + "a": Metadata{ + "b": Metadata{ + "c": Metadata{ + "d": "a new value", + }, + }, + }, + } + + out := DeepMerge(under, over) + + dval := out["a"].(Metadata)["b"].(Metadata)["c"].(Metadata)["d"].(string) + if dval != "a new value" { + t.Fatal(dval) + } + surv := out["a"].(Metadata)["b"].(Metadata)["c"].(Metadata)["other"].(string) + if surv != "SURVIVE" { + t.Fatal(surv) + } +} + +func TestMarshalJSON(t *testing.T) { + bs, _ := Metadata{"a": "b"}.JsonString() + t.Log(bs) +} + +func TestMetadataIsLoggable(t *testing.T) { + func(l Loggable) { + }(Metadata{}) +} From 4b1607819f3cb9723c6b7d06beef2ed363fb68e1 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sat, 15 Nov 2014 18:31:06 -0800 Subject: [PATCH 09/42] log(dht) log a couple events to demonstrate API License: MIT Signed-off-by: Brian Tiger Chow --- peer/peer.go | 22 ++++++++++++++++++++++ routing/dht/dht.go | 5 ++++- routing/dht/pb/message.go | 8 ++++++++ util/elog/log.go | 6 +++--- util/elog/metadata.go | 2 +- 5 files changed, 38 insertions(+), 5 deletions(-) diff --git a/peer/peer.go b/peer/peer.go index fcaadce3ad5..f38a2df211d 100644 --- a/peer/peer.go +++ b/peer/peer.go @@ -101,6 +101,8 @@ type Peer interface { // Update with the data of another peer instance Update(Peer) error + + Loggable() map[string]interface{} } type Type uint8 @@ -112,6 +114,17 @@ const ( Remote ) +func (t Type) String() string { + switch t { + case Local: + return "localPeer" + case Remote: + return "remotePeer" + default: + } + return "unspecifiedPeer" +} + type peer struct { id ID addresses []ma.Multiaddr @@ -145,6 +158,15 @@ func (p *peer) String() string { return "[Peer " + pid[:maxRunes] + "]" } +func (p *peer) Loggable() map[string]interface{} { + return map[string]interface{}{ + p.GetType().String(): map[string]interface{}{ + "id": p.ID(), + "latency": p.GetLatency(), + }, + } +} + // Key returns the ID as a Key (string) for maps. func (p *peer) Key() u.Key { return u.Key(p.id) diff --git a/routing/dht/dht.go b/routing/dht/dht.go index efe457c6585..1af4a29bd9e 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -18,6 +18,7 @@ import ( kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" ctxc "github.com/jbenet/go-ipfs/util/ctxcloser" + "github.com/jbenet/go-ipfs/util/elog" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" @@ -25,7 +26,7 @@ import ( "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ) -var log = u.Logger("dht") +var log = elog.Logger("dht") const doPinging = false @@ -152,6 +153,7 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) msg.N dht.Update(mPeer) // Print out diagnostic + log.Event(ctx, "foo", dht.self, mPeer, pmes) log.Debugf("%s got message type: '%s' from %s", dht.self, pb.Message_MessageType_name[int32(pmes.GetType())], mPeer) @@ -197,6 +199,7 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.Peer, pmes *pb.Messa start := time.Now() // Print out diagnostic + log.Event(ctx, "sentMessage", dht.self, p, pmes) log.Debugf("Sent message type: '%s' to %s", pb.Message_MessageType_name[int32(pmes.GetType())], p) diff --git a/routing/dht/pb/message.go b/routing/dht/pb/message.go index a77a5b9176d..6ea98d4cdba 100644 --- a/routing/dht/pb/message.go +++ b/routing/dht/pb/message.go @@ -65,3 +65,11 @@ func (m *Message) SetClusterLevel(level int) { lvl := int32(level) m.ClusterLevelRaw = &lvl } + +func (m *Message) Loggable() map[string]interface{} { + return map[string]interface{}{ + "message": map[string]string{ + "type": m.Type.String(), + }, + } +} diff --git a/util/elog/log.go b/util/elog/log.go index dbe1914a023..27d76c2c569 100644 --- a/util/elog/log.go +++ b/util/elog/log.go @@ -14,7 +14,7 @@ func init() { type EventLogger interface { StandardLogger - Event(ctx context.Context, event string, m ...Metadata) + Event(ctx context.Context, event string, m ...Loggable) } type StandardLogger interface { @@ -46,14 +46,14 @@ type eventLogger struct { *logging.Logger } -func (el *eventLogger) Event(ctx context.Context, event string, metadata ...Metadata) { +func (el *eventLogger) Event(ctx context.Context, event string, metadata ...Loggable) { existing, err := MetadataFromContext(ctx) if err != nil { existing = Metadata{} } accum := existing for _, datum := range metadata { - accum = DeepMerge(accum, datum) + accum = DeepMerge(accum, datum.Loggable()) } accum["event"] = event diff --git a/util/elog/metadata.go b/util/elog/metadata.go index 22c8b2a2aa6..19357e76a84 100644 --- a/util/elog/metadata.go +++ b/util/elog/metadata.go @@ -13,7 +13,7 @@ type Metadata map[string]interface{} // Loggable describes objects that can be marshalled into Metadata for logging type Loggable interface { - Loggable() Metadata + Loggable() map[string]interface{} } // UniqueEvent returns a Metadata with the string key and UUID value From 55896a86d669a54ba76bbc9c883c7b36ffbf67c7 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sat, 15 Nov 2014 18:31:52 -0800 Subject: [PATCH 10/42] logs(2/main) place log statements approppriately License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/main.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index ca474c91119..7b316faf1b1 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -353,13 +353,15 @@ func commandShouldRunOnDaemon(details cmdDetails, req cmds.Request, root *cmds.C return false, nil } + log.Info("looking for running daemon...") // at this point need to know whether daemon is running. we defer // to this point so that some commands dont open files unnecessarily. daemonLocked := daemon.Locked(req.Context().ConfigRoot) - log.Info("Daemon is running.") if daemonLocked { + log.Info("a daemon is running...") + if details.cannotRunOnDaemon { e := "ipfs daemon is running. please stop it to run this command" return false, cmds.ClientError(e) From 6245f0c67ee5c0cfbdc8d98445fd4e4a8498befb Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sat, 15 Nov 2014 18:40:26 -0800 Subject: [PATCH 11/42] fix(elog) Metadata implements Loggable License: MIT Signed-off-by: Brian Tiger Chow --- util/elog/metadata.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/elog/metadata.go b/util/elog/metadata.go index 19357e76a84..6b6d58ccd0a 100644 --- a/util/elog/metadata.go +++ b/util/elog/metadata.go @@ -62,7 +62,7 @@ func DeepMerge(b, a Metadata) Metadata { } // Loggable implements the Loggable interface -func (m Metadata) Loggable() Metadata { +func (m Metadata) Loggable() map[string]interface{} { // NB: method defined on value to avoid de-referencing nil Metadata return m } From a84ddcf93289660e31bf891c312dcae12e9d5157 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sat, 15 Nov 2014 19:12:29 -0800 Subject: [PATCH 12/42] docs(elog/Event) License: MIT Signed-off-by: Brian Tiger Chow --- util/elog/log.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/util/elog/log.go b/util/elog/log.go index 27d76c2c569..ea9ccbf22d0 100644 --- a/util/elog/log.go +++ b/util/elog/log.go @@ -12,8 +12,24 @@ func init() { SetupLogging() } +// EventLogger extends the StandardLogger interface to allow for log items +// containing structured metadata type EventLogger interface { StandardLogger + + // Event merges structured data from the provided inputs into a single + // machine-readable log event. + // + // If the context contains metadata, a copy of this is used as the base + // metadata accumulator. + // + // If one or more loggable objects are provided, these are deep-merged into base blob. + // + // Next, the event name is added to the blob under the key "event". If + // the key "event" already exists, it will be over-written. + // + // Finally the timestamp and package name are added to the accumulator and + // the metadata is logged. Event(ctx context.Context, event string, m ...Loggable) } From 60af68bb995699f363d9a63c83f1be48e7be6224 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sat, 15 Nov 2014 19:12:58 -0800 Subject: [PATCH 13/42] rm dead code License: MIT Signed-off-by: Brian Tiger Chow --- util/elog/log.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/util/elog/log.go b/util/elog/log.go index ea9ccbf22d0..5ff3e0dfd0f 100644 --- a/util/elog/log.go +++ b/util/elog/log.go @@ -6,12 +6,6 @@ import ( "github.com/jbenet/go-ipfs/util" ) -var eloggers = map[string]*logging.Logger{} - -func init() { - SetupLogging() -} - // EventLogger extends the StandardLogger interface to allow for log items // containing structured metadata type EventLogger interface { @@ -79,13 +73,3 @@ func (el *eventLogger) Event(ctx context.Context, event string, metadata ...Logg } el.Logger.Info(str) } - -// SetupLogging will initialize the logger backend and set the flags. -func SetupLogging() { - // fmt := logging.DefaultFormatter - - // f, err := os.Create("events.ipfslog") - // if err != nil { - // panic("failed to open file for event logger") - // } -} From 15f850f08c408c4cb319c1c857a4a727f80235ea Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 00:36:26 -0800 Subject: [PATCH 14/42] refactor(2/main) helper methods for details object License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/ipfs.go | 9 +++++---- cmd/ipfs2/main.go | 3 +-- util/elog/metadata.go | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd/ipfs2/ipfs.go b/cmd/ipfs2/ipfs.go index 9cd5e372dd1..908bb23d3a3 100644 --- a/cmd/ipfs2/ipfs.go +++ b/cmd/ipfs2/ipfs.go @@ -76,10 +76,11 @@ func (d *cmdDetails) String() string { d.canRunOnClient(), d.canRunOnDaemon(), d.usesRepo()) } -func (d *cmdDetails) usesConfigAsInput() bool { return !d.doesNotUseConfigAsInput } -func (d *cmdDetails) canRunOnClient() bool { return !d.cannotRunOnClient } -func (d *cmdDetails) canRunOnDaemon() bool { return !d.cannotRunOnDaemon } -func (d *cmdDetails) usesRepo() bool { return !d.doesNotUseRepo } +func (d *cmdDetails) usesConfigAsInput() bool { return !d.doesNotUseConfigAsInput } +func (d *cmdDetails) doesNotPreemptAutoUpdate() bool { return !d.preemptsAutoUpdate } +func (d *cmdDetails) canRunOnClient() bool { return !d.cannotRunOnClient } +func (d *cmdDetails) canRunOnDaemon() bool { return !d.cannotRunOnDaemon } +func (d *cmdDetails) usesRepo() bool { return !d.doesNotUseRepo } // "What is this madness!?" you ask. Our commands have the unfortunate problem of // not being able to run on all the same contexts. This map describes these diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 7b316faf1b1..f52204af3cb 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -219,7 +219,7 @@ func callPreCommandHooks(details cmdDetails, req cmds.Request, root *cmds.Comman // check for updates when 1) commands is going to be run locally, 2) the // command does not initialize the config, and 3) the command does not // pre-empt updates - if !daemon && details.usesConfigAsInput() && !details.preemptsAutoUpdate { + if !daemon && details.usesConfigAsInput() && details.doesNotPreemptAutoUpdate() { log.Debug("Calling hook: Check for updates") @@ -328,7 +328,6 @@ func commandDetails(path []string, root *cmds.Command) (*cmdDetails, error) { details = cmdDetails } } - log.Debugf("cmd perms for +%v: %s", path, details.String()) return &details, nil } diff --git a/util/elog/metadata.go b/util/elog/metadata.go index 6b6d58ccd0a..a6408d98464 100644 --- a/util/elog/metadata.go +++ b/util/elog/metadata.go @@ -16,8 +16,8 @@ type Loggable interface { Loggable() map[string]interface{} } -// UniqueEvent returns a Metadata with the string key and UUID value -func UniqueEvent(key string) Metadata { +// Uuid returns a Metadata with the string key and UUID value +func Uuid(key string) Metadata { return Metadata{ key: uuid.New(), } From 4cadbc16738811e0cab641d32677cf8d381876cd Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 00:37:33 -0800 Subject: [PATCH 15/42] style rename for clarity License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/main.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index f52204af3cb..58133c4f05c 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -206,12 +206,12 @@ func (i *cmdInvocation) requestedHelp() (short bool, long bool, err error) { return longHelp, shortHelp, nil } -func callPreCommandHooks(details cmdDetails, req cmds.Request, root *cmds.Command) error { +func callPreCommandHooks(command cmdDetails, req cmds.Request, root *cmds.Command) error { log.Debug("Calling pre-command hooks...") // some hooks only run when the command is executed locally - daemon, err := commandShouldRunOnDaemon(details, req, root) + daemon, err := commandShouldRunOnDaemon(command, req, root) if err != nil { return err } @@ -219,7 +219,7 @@ func callPreCommandHooks(details cmdDetails, req cmds.Request, root *cmds.Comman // check for updates when 1) commands is going to be run locally, 2) the // command does not initialize the config, and 3) the command does not // pre-empt updates - if !daemon && details.usesConfigAsInput() && details.doesNotPreemptAutoUpdate() { + if !daemon && command.usesConfigAsInput() && command.doesNotPreemptAutoUpdate() { log.Debug("Calling hook: Check for updates") From eb46fbed82bf126851b2c0720d758152269c8a08 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 00:57:09 -0800 Subject: [PATCH 16/42] perform some swarm logging License: MIT Signed-off-by: Brian Tiger Chow --- net/swarm/addrs.go | 9 +++++++++ net/swarm/swarm.go | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/net/swarm/addrs.go b/net/swarm/addrs.go index ab86f278fda..a7627ee925c 100644 --- a/net/swarm/addrs.go +++ b/net/swarm/addrs.go @@ -1,7 +1,9 @@ package swarm import ( + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net" + "github.com/jbenet/go-ipfs/util/elog" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" ) @@ -52,6 +54,13 @@ func resolveUnspecifiedAddresses(unspecifiedAddrs []ma.Multiaddr) ([]ma.Multiadd } } + log.Event(context.TODO(), "interfaceListenAddresses", func() elog.Loggable { + var addrs []string + for _, addr := range outputAddrs { + addrs = append(addrs, addr.String()) + } + return elog.Metadata{"addresses": addrs} + }()) log.Info("InterfaceListenAddresses:", outputAddrs) return outputAddrs, nil } diff --git a/net/swarm/swarm.go b/net/swarm/swarm.go index 6a50ecd00cc..2768f326b6d 100644 --- a/net/swarm/swarm.go +++ b/net/swarm/swarm.go @@ -13,12 +13,13 @@ import ( peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" ctxc "github.com/jbenet/go-ipfs/util/ctxcloser" + "github.com/jbenet/go-ipfs/util/elog" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" ) -var log = u.Logger("swarm") +var log = elog.Logger("swarm") // ErrAlreadyOpen signals that a connection to a peer is already open. var ErrAlreadyOpen = errors.New("Error: Connection to this peer already open.") From 9a38b28416916e0ecf9df9cffac4d846fca8323c Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 01:00:02 -0800 Subject: [PATCH 17/42] deps(logging) vendor logrus (MIT) Experimenting with LD JSON logging License: MIT Signed-off-by: Brian Tiger Chow --- Godeps/Godeps.json | 7 +- .../src/github.com/Sirupsen/logrus/.gitignore | 1 + .../github.com/Sirupsen/logrus/.travis.yml | 9 + .../src/github.com/Sirupsen/logrus/LICENSE | 21 ++ .../src/github.com/Sirupsen/logrus/README.md | 342 ++++++++++++++++++ .../src/github.com/Sirupsen/logrus/entry.go | 248 +++++++++++++ .../github.com/Sirupsen/logrus/entry_test.go | 53 +++ .../Sirupsen/logrus/examples/basic/basic.go | 40 ++ .../Sirupsen/logrus/examples/hook/hook.go | 35 ++ .../github.com/Sirupsen/logrus/exported.go | 177 +++++++++ .../github.com/Sirupsen/logrus/formatter.go | 44 +++ .../Sirupsen/logrus/formatter_bench_test.go | 88 +++++ .../github.com/Sirupsen/logrus/hook_test.go | 122 +++++++ .../src/github.com/Sirupsen/logrus/hooks.go | 34 ++ .../logrus/hooks/airbrake/airbrake.go | 54 +++ .../logrus/hooks/papertrail/README.md | 28 ++ .../logrus/hooks/papertrail/papertrail.go | 54 +++ .../hooks/papertrail/papertrail_test.go | 26 ++ .../Sirupsen/logrus/hooks/syslog/README.md | 20 + .../Sirupsen/logrus/hooks/syslog/syslog.go | 59 +++ .../logrus/hooks/syslog/syslog_test.go | 26 ++ .../Sirupsen/logrus/json_formatter.go | 22 ++ .../src/github.com/Sirupsen/logrus/logger.go | 161 +++++++++ .../src/github.com/Sirupsen/logrus/logrus.go | 94 +++++ .../github.com/Sirupsen/logrus/logrus_test.go | 247 +++++++++++++ .../Sirupsen/logrus/terminal_darwin.go | 12 + .../Sirupsen/logrus/terminal_freebsd.go | 20 + .../Sirupsen/logrus/terminal_linux.go | 12 + .../Sirupsen/logrus/terminal_notwindows.go | 21 ++ .../Sirupsen/logrus/terminal_windows.go | 27 ++ .../Sirupsen/logrus/text_formatter.go | 95 +++++ util/elog/log.go | 25 +- 32 files changed, 2216 insertions(+), 8 deletions(-) create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/.gitignore create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/.travis.yml create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/LICENSE create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/README.md create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/entry.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/entry_test.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/basic/basic.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/hook/hook.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/exported.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter_bench_test.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/hook_test.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/README.md create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail_test.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/README.md create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/logger.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus_test.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_darwin.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_freebsd.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_linux.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_notwindows.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_windows.go create mode 100644 Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 0a6bb4fe8db..c586c7ac84e 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,6 +1,6 @@ { "ImportPath": "github.com/jbenet/go-ipfs", - "GoVersion": "go1.3", + "GoVersion": "go1.3.3", "Packages": [ "./..." ], @@ -48,6 +48,11 @@ "Comment": "null-15", "Rev": "12e4b4183793ac4b061921e7980845e750679fd0" }, + { + "ImportPath": "github.com/Sirupsen/logrus", + "Comment": "v0.6.0-5-gf92b795", + "Rev": "f92b7950b372b1db80bd3527e4d40e42555fe6c2" + }, { "ImportPath": "github.com/braintree/manners", "Comment": "0.3.1-2-g5280e25", diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/.gitignore b/Godeps/_workspace/src/github.com/Sirupsen/logrus/.gitignore new file mode 100644 index 00000000000..66be63a0057 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/.gitignore @@ -0,0 +1 @@ +logrus diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/.travis.yml b/Godeps/_workspace/src/github.com/Sirupsen/logrus/.travis.yml new file mode 100644 index 00000000000..d5a559f8403 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/.travis.yml @@ -0,0 +1,9 @@ +language: go +go: + - 1.2 + - 1.3 + - tip +install: + - go get github.com/stretchr/testify + - go get github.com/stvp/go-udp-testing + - go get github.com/tobi/airbrake-go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/LICENSE b/Godeps/_workspace/src/github.com/Sirupsen/logrus/LICENSE new file mode 100644 index 00000000000..f090cb42f37 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Simon Eskildsen + +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. diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/README.md b/Godeps/_workspace/src/github.com/Sirupsen/logrus/README.md new file mode 100644 index 00000000000..01769c723f7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/README.md @@ -0,0 +1,342 @@ +# Logrus :walrus: [![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus) + +Logrus is a structured logger for Go (golang), completely API compatible with +the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not +yet stable (pre 1.0), the core API is unlikely change much but please version +control your Logrus to make sure you aren't fetching latest `master` on every +build.** + +Nicely color-coded in development (when a TTY is attached, otherwise just +plain text): + +![Colored](http://i.imgur.com/PY7qMwd.png) + +With `log.Formatter = new(logrus.JSONFormatter)`, for easy parsing by logstash +or Splunk: + +```json +{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the +ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"} + +{"level":"warning","msg":"The group's number increased tremendously!", +"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"} + +{"animal":"walrus","level":"info","msg":"A giant walrus appears!", +"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"} + +{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.", +"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"} + +{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true, +"time":"2014-03-10 19:57:38.562543128 -0400 EDT"} +``` + +With the default `log.Formatter = new(logrus.TextFormatter)` when a TTY is not +attached, the output is compatible with the +[l2met](http://r.32k.io/l2met-introduction) format: + +```text +time="2014-04-20 15:36:23.830442383 -0400 EDT" level="info" msg="A group of walrus emerges from the ocean" animal="walrus" size=10 +time="2014-04-20 15:36:23.830584199 -0400 EDT" level="warning" msg="The group's number increased tremendously!" omg=true number=122 +time="2014-04-20 15:36:23.830596521 -0400 EDT" level="info" msg="A giant walrus appears!" animal="walrus" size=10 +time="2014-04-20 15:36:23.830611837 -0400 EDT" level="info" msg="Tremendously sized cow enters the ocean." animal="walrus" size=9 +time="2014-04-20 15:36:23.830626464 -0400 EDT" level="fatal" msg="The ice breaks!" omg=true number=100 +``` + +#### Example + +The simplest way to use Logrus is simply the package-level exported logger: + +```go +package main + +import ( + log "github.com/Sirupsen/logrus" +) + +func main() { + log.WithFields(log.Fields{ + "animal": "walrus", + }).Info("A walrus appears") +} +``` + +Note that it's completely api-compatible with the stdlib logger, so you can +replace your `log` imports everywhere with `log "github.com/Sirupsen/logrus"` +and you'll now have the flexibility of Logrus. You can customize it all you +want: + +```go +package main + +import ( + "os" + log "github.com/Sirupsen/logrus" + "github.com/Sirupsen/logrus/hooks/airbrake" +) + +func init() { + // Log as JSON instead of the default ASCII formatter. + log.SetFormatter(&log.JSONFormatter{}) + + // Use the Airbrake hook to report errors that have Error severity or above to + // an exception tracker. You can create custom hooks, see the Hooks section. + log.AddHook(&logrus_airbrake.AirbrakeHook{}) + + // Output to stderr instead of stdout, could also be a file. + log.SetOutput(os.Stderr) + + // Only log the warning severity or above. + log.SetLevel(log.WarnLevel) +} + +func main() { + log.WithFields(log.Fields{ + "animal": "walrus", + "size": 10, + }).Info("A group of walrus emerges from the ocean") + + log.WithFields(log.Fields{ + "omg": true, + "number": 122, + }).Warn("The group's number increased tremendously!") + + log.WithFields(log.Fields{ + "omg": true, + "number": 100, + }).Fatal("The ice breaks!") +} +``` + +For more advanced usage such as logging to multiple locations from the same +application, you can also create an instance of the `logrus` Logger: + +```go +package main + +import ( + "github.com/Sirupsen/logrus" +) + +// Create a new instance of the logger. You can have any number of instances. +var log = logrus.New() + +func main() { + // The API for setting attributes is a little different than the package level + // exported logger. See Godoc. + log.Out = os.Stderr + + log.WithFields(logrus.Fields{ + "animal": "walrus", + "size": 10, + }).Info("A group of walrus emerges from the ocean") +} +``` + +#### Fields + +Logrus encourages careful, structured logging though logging fields instead of +long, unparseable error messages. For example, instead of: `log.Fatalf("Failed +to send event %s to topic %s with key %d")`, you should log the much more +discoverable: + +```go +log.WithFields(log.Fields{ + "event": event, + "topic": topic, + "key": key, +}).Fatal("Failed to send event") +``` + +We've found this API forces you to think about logging in a way that produces +much more useful logging messages. We've been in countless situations where just +a single added field to a log statement that was already there would've saved us +hours. The `WithFields` call is optional. + +In general, with Logrus using any of the `printf`-family functions should be +seen as a hint you should add a field, however, you can still use the +`printf`-family functions with Logrus. + +#### Hooks + +You can add hooks for logging levels. For example to send errors to an exception +tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to +multiple places simultaneously, e.g. syslog. + +```go +// Not the real implementation of the Airbrake hook. Just a simple sample. +import ( + log "github.com/Sirupsen/logrus" +) + +func init() { + log.AddHook(new(AirbrakeHook)) +} + +type AirbrakeHook struct{} + +// `Fire()` takes the entry that the hook is fired for. `entry.Data[]` contains +// the fields for the entry. See the Fields section of the README. +func (hook *AirbrakeHook) Fire(entry *logrus.Entry) error { + err := airbrake.Notify(entry.Data["error"].(error)) + if err != nil { + log.WithFields(log.Fields{ + "source": "airbrake", + "endpoint": airbrake.Endpoint, + }).Info("Failed to send error to Airbrake") + } + + return nil +} + +// `Levels()` returns a slice of `Levels` the hook is fired for. +func (hook *AirbrakeHook) Levels() []log.Level { + return []log.Level{ + log.ErrorLevel, + log.FatalLevel, + log.PanicLevel, + } +} +``` + +Logrus comes with built-in hooks. Add those, or your custom hook, in `init`: + +```go +import ( + log "github.com/Sirupsen/logrus" + "github.com/Sirupsen/logrus/hooks/airbrake" + "github.com/Sirupsen/logrus/hooks/syslog" +) + +func init() { + log.AddHook(new(logrus_airbrake.AirbrakeHook)) + log.AddHook(logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")) +} +``` + +* [`github.com/Sirupsen/logrus/hooks/airbrake`](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go) + Send errors to an exception tracking service compatible with the Airbrake API. + Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. + +* [`github.com/Sirupsen/logrus/hooks/papertrail`](https://github.com/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go) + Send errors to the Papertrail hosted logging service via UDP. + +* [`github.com/Sirupsen/logrus/hooks/syslog`](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) + Send errors to remote syslog server. + Uses standard library `log/syslog` behind the scenes. + +* [`github.com/nubo/hiprus`](https://github.com/nubo/hiprus) + Send errors to a channel in hipchat. + +#### Level logging + +Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic. + +```go +log.Debug("Useful debugging information.") +log.Info("Something noteworthy happened!") +log.Warn("You should probably take a look at this.") +log.Error("Something failed but I'm not quitting.") +// Calls os.Exit(1) after logging +log.Fatal("Bye.") +// Calls panic() after logging +log.Panic("I'm bailing.") +``` + +You can set the logging level on a `Logger`, then it will only log entries with +that severity or anything above it: + +```go +// Will log anything that is info or above (warn, error, fatal, panic). Default. +log.SetLevel(log.InfoLevel) +``` + +It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose +environment if your application has that. + +#### Entries + +Besides the fields added with `WithField` or `WithFields` some fields are +automatically added to all logging events: + +1. `time`. The timestamp when the entry was created. +2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after + the `AddFields` call. E.g. `Failed to send event.` +3. `level`. The logging level. E.g. `info`. + +#### Environments + +Logrus has no notion of environment. + +If you wish for hooks and formatters to only be used in specific environments, +you should handle that yourself. For example, if your application has a global +variable `Environment`, which is a string representation of the environment you +could do: + +```go +import ( + log "github.com/Sirupsen/logrus" +) + +init() { + // do something here to set environment depending on an environment variable + // or command-line flag + if Environment == "production" { + log.SetFormatter(logrus.JSONFormatter) + } else { + // The TextFormatter is default, you don't actually have to do this. + log.SetFormatter(logrus.TextFormatter) + } +} +``` + +This configuration is how `logrus` was intended to be used, but JSON in +production is mostly only useful if you do log aggregation with tools like +Splunk or Logstash. + +#### Formatters + +The built-in logging formatters are: + +* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise + without colors. + * *Note:* to force colored output when there is no TTY, set the `ForceColors` + field to `true`. To force no colored output even if there is a TTY set the + `DisableColors` field to `true` +* `logrus.JSONFormatter`. Logs fields as JSON. + +Third party logging formatters: + +* [`zalgo`](https://github.com/aybabtme/logzalgo): invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦. + +You can define your formatter by implementing the `Formatter` interface, +requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a +`Fields` type (`map[string]interface{}`) with all your fields as well as the +default ones (see Entries section above): + +```go +type MyJSONFormatter struct { +} + +log.SetFormatter(new(MyJSONFormatter)) + +func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { + // Note this doesn't include Time, Level and Message which are available on + // the Entry. Consult `godoc` on information about those fields or read the + // source of the official loggers. + serialized, err := json.Marshal(entry.Data) + if err != nil { + return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) + } + return append(serialized, '\n'), nil +} +``` + +#### Rotation + +Log rotation is not provided with Logrus. Log rotation should be done by an +external program (like `logrotated(8)`) that can compress and delete old log +entries. It should not be a feature of the application-level logger. + + +[godoc]: https://godoc.org/github.com/Sirupsen/logrus diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/entry.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/entry.go new file mode 100644 index 00000000000..e164eecb5f3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/entry.go @@ -0,0 +1,248 @@ +package logrus + +import ( + "bytes" + "fmt" + "io" + "os" + "time" +) + +// An entry is the final or intermediate Logrus logging entry. It contains all +// the fields passed with WithField{,s}. It's finally logged when Debug, Info, +// Warn, Error, Fatal or Panic is called on it. These objects can be reused and +// passed around as much as you wish to avoid field duplication. +type Entry struct { + Logger *Logger + + // Contains all the fields set by the user. + Data Fields + + // Time at which the log entry was created + Time time.Time + + // Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic + Level Level + + // Message passed to Debug, Info, Warn, Error, Fatal or Panic + Message string +} + +func NewEntry(logger *Logger) *Entry { + return &Entry{ + Logger: logger, + // Default is three fields, give a little extra room + Data: make(Fields, 5), + } +} + +// Returns a reader for the entry, which is a proxy to the formatter. +func (entry *Entry) Reader() (*bytes.Buffer, error) { + serialized, err := entry.Logger.Formatter.Format(entry) + return bytes.NewBuffer(serialized), err +} + +// Returns the string representation from the reader and ultimately the +// formatter. +func (entry *Entry) String() (string, error) { + reader, err := entry.Reader() + if err != nil { + return "", err + } + + return reader.String(), err +} + +// Add a single field to the Entry. +func (entry *Entry) WithField(key string, value interface{}) *Entry { + return entry.WithFields(Fields{key: value}) +} + +// Add a map of fields to the Entry. +func (entry *Entry) WithFields(fields Fields) *Entry { + data := Fields{} + for k, v := range entry.Data { + data[k] = v + } + for k, v := range fields { + data[k] = v + } + return &Entry{Logger: entry.Logger, Data: data} +} + +func (entry *Entry) log(level Level, msg string) { + entry.Time = time.Now() + entry.Level = level + entry.Message = msg + + if err := entry.Logger.Hooks.Fire(level, entry); err != nil { + entry.Logger.mu.Lock() + fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) + entry.Logger.mu.Unlock() + } + + reader, err := entry.Reader() + if err != nil { + entry.Logger.mu.Lock() + fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) + entry.Logger.mu.Unlock() + } + + entry.Logger.mu.Lock() + defer entry.Logger.mu.Unlock() + + _, err = io.Copy(entry.Logger.Out, reader) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) + } + + // To avoid Entry#log() returning a value that only would make sense for + // panic() to use in Entry#Panic(), we avoid the allocation by checking + // directly here. + if level <= PanicLevel { + panic(entry) + } +} + +func (entry *Entry) Debug(args ...interface{}) { + if entry.Logger.Level >= DebugLevel { + entry.log(DebugLevel, fmt.Sprint(args...)) + } +} + +func (entry *Entry) Print(args ...interface{}) { + entry.Info(args...) +} + +func (entry *Entry) Info(args ...interface{}) { + if entry.Logger.Level >= InfoLevel { + entry.log(InfoLevel, fmt.Sprint(args...)) + } +} + +func (entry *Entry) Warn(args ...interface{}) { + if entry.Logger.Level >= WarnLevel { + entry.log(WarnLevel, fmt.Sprint(args...)) + } +} + +func (entry *Entry) Error(args ...interface{}) { + if entry.Logger.Level >= ErrorLevel { + entry.log(ErrorLevel, fmt.Sprint(args...)) + } +} + +func (entry *Entry) Fatal(args ...interface{}) { + if entry.Logger.Level >= FatalLevel { + entry.log(FatalLevel, fmt.Sprint(args...)) + } + os.Exit(1) +} + +func (entry *Entry) Panic(args ...interface{}) { + if entry.Logger.Level >= PanicLevel { + entry.log(PanicLevel, fmt.Sprint(args...)) + } + panic(fmt.Sprint(args...)) +} + +// Entry Printf family functions + +func (entry *Entry) Debugf(format string, args ...interface{}) { + if entry.Logger.Level >= DebugLevel { + entry.Debug(fmt.Sprintf(format, args...)) + } +} + +func (entry *Entry) Infof(format string, args ...interface{}) { + if entry.Logger.Level >= InfoLevel { + entry.Info(fmt.Sprintf(format, args...)) + } +} + +func (entry *Entry) Printf(format string, args ...interface{}) { + entry.Infof(format, args...) +} + +func (entry *Entry) Warnf(format string, args ...interface{}) { + if entry.Logger.Level >= WarnLevel { + entry.Warn(fmt.Sprintf(format, args...)) + } +} + +func (entry *Entry) Warningf(format string, args ...interface{}) { + entry.Warnf(format, args...) +} + +func (entry *Entry) Errorf(format string, args ...interface{}) { + if entry.Logger.Level >= ErrorLevel { + entry.Error(fmt.Sprintf(format, args...)) + } +} + +func (entry *Entry) Fatalf(format string, args ...interface{}) { + if entry.Logger.Level >= FatalLevel { + entry.Fatal(fmt.Sprintf(format, args...)) + } +} + +func (entry *Entry) Panicf(format string, args ...interface{}) { + if entry.Logger.Level >= PanicLevel { + entry.Panic(fmt.Sprintf(format, args...)) + } +} + +// Entry Println family functions + +func (entry *Entry) Debugln(args ...interface{}) { + if entry.Logger.Level >= DebugLevel { + entry.Debug(entry.sprintlnn(args...)) + } +} + +func (entry *Entry) Infoln(args ...interface{}) { + if entry.Logger.Level >= InfoLevel { + entry.Info(entry.sprintlnn(args...)) + } +} + +func (entry *Entry) Println(args ...interface{}) { + entry.Infoln(args...) +} + +func (entry *Entry) Warnln(args ...interface{}) { + if entry.Logger.Level >= WarnLevel { + entry.Warn(entry.sprintlnn(args...)) + } +} + +func (entry *Entry) Warningln(args ...interface{}) { + entry.Warnln(args...) +} + +func (entry *Entry) Errorln(args ...interface{}) { + if entry.Logger.Level >= ErrorLevel { + entry.Error(entry.sprintlnn(args...)) + } +} + +func (entry *Entry) Fatalln(args ...interface{}) { + if entry.Logger.Level >= FatalLevel { + entry.Fatal(entry.sprintlnn(args...)) + } +} + +func (entry *Entry) Panicln(args ...interface{}) { + if entry.Logger.Level >= PanicLevel { + entry.Panic(entry.sprintlnn(args...)) + } +} + +// Sprintlnn => Sprint no newline. This is to get the behavior of how +// fmt.Sprintln where spaces are always added between operands, regardless of +// their type. Instead of vendoring the Sprintln implementation to spare a +// string allocation, we do the simplest thing. +func (entry *Entry) sprintlnn(args ...interface{}) string { + msg := fmt.Sprintln(args...) + return msg[:len(msg)-1] +} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/entry_test.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/entry_test.go new file mode 100644 index 00000000000..98717df4901 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/entry_test.go @@ -0,0 +1,53 @@ +package logrus + +import ( + "bytes" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestEntryPanicln(t *testing.T) { + errBoom := fmt.Errorf("boom time") + + defer func() { + p := recover() + assert.NotNil(t, p) + + switch pVal := p.(type) { + case *Entry: + assert.Equal(t, "kaboom", pVal.Message) + assert.Equal(t, errBoom, pVal.Data["err"]) + default: + t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal) + } + }() + + logger := New() + logger.Out = &bytes.Buffer{} + entry := NewEntry(logger) + entry.WithField("err", errBoom).Panicln("kaboom") +} + +func TestEntryPanicf(t *testing.T) { + errBoom := fmt.Errorf("boom again") + + defer func() { + p := recover() + assert.NotNil(t, p) + + switch pVal := p.(type) { + case *Entry: + assert.Equal(t, "kaboom true", pVal.Message) + assert.Equal(t, errBoom, pVal.Data["err"]) + default: + t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal) + } + }() + + logger := New() + logger.Out = &bytes.Buffer{} + entry := NewEntry(logger) + entry.WithField("err", errBoom).Panicf("kaboom %v", true) +} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/basic/basic.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/basic/basic.go new file mode 100644 index 00000000000..19422a212e3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/basic/basic.go @@ -0,0 +1,40 @@ +package main + +import ( + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus" +) + +var log = logrus.New() + +func init() { + log.Formatter = new(logrus.JSONFormatter) + log.Formatter = new(logrus.TextFormatter) // default +} + +func main() { + defer func() { + err := recover() + if err != nil { + log.WithFields(logrus.Fields{ + "omg": true, + "err": err, + "number": 100, + }).Fatal("The ice breaks!") + } + }() + + log.WithFields(logrus.Fields{ + "animal": "walrus", + "size": 10, + }).Info("A group of walrus emerges from the ocean") + + log.WithFields(logrus.Fields{ + "omg": true, + "number": 122, + }).Warn("The group's number increased tremendously!") + + log.WithFields(logrus.Fields{ + "animal": "orca", + "size": 9009, + }).Panic("It's over 9000!") +} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/hook/hook.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/hook/hook.go new file mode 100644 index 00000000000..d6d43d4860e --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/hook/hook.go @@ -0,0 +1,35 @@ +package main + +import ( + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake" + "github.com/tobi/airbrake-go" +) + +var log = logrus.New() + +func init() { + log.Formatter = new(logrus.TextFormatter) // default + log.Hooks.Add(new(logrus_airbrake.AirbrakeHook)) +} + +func main() { + airbrake.Endpoint = "https://exceptions.whatever.com/notifier_api/v2/notices.xml" + airbrake.ApiKey = "whatever" + airbrake.Environment = "production" + + log.WithFields(logrus.Fields{ + "animal": "walrus", + "size": 10, + }).Info("A group of walrus emerges from the ocean") + + log.WithFields(logrus.Fields{ + "omg": true, + "number": 122, + }).Warn("The group's number increased tremendously!") + + log.WithFields(logrus.Fields{ + "omg": true, + "number": 100, + }).Fatal("The ice breaks!") +} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/exported.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/exported.go new file mode 100644 index 00000000000..0e2d59f19a7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/exported.go @@ -0,0 +1,177 @@ +package logrus + +import ( + "io" +) + +var ( + // std is the name of the standard logger in stdlib `log` + std = New() +) + +// SetOutput sets the standard logger output. +func SetOutput(out io.Writer) { + std.mu.Lock() + defer std.mu.Unlock() + std.Out = out +} + +// SetFormatter sets the standard logger formatter. +func SetFormatter(formatter Formatter) { + std.mu.Lock() + defer std.mu.Unlock() + std.Formatter = formatter +} + +// SetLevel sets the standard logger level. +func SetLevel(level Level) { + std.mu.Lock() + defer std.mu.Unlock() + std.Level = level +} + +// AddHook adds a hook to the standard logger hooks. +func AddHook(hook Hook) { + std.mu.Lock() + defer std.mu.Unlock() + std.Hooks.Add(hook) +} + +// WithField creates an entry from the standard logger and adds a field to +// it. If you want multiple fields, use `WithFields`. +// +// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal +// or Panic on the Entry it returns. +func WithField(key string, value interface{}) *Entry { + return std.WithField(key, value) +} + +// WithFields creates an entry from the standard logger and adds multiple +// fields to it. This is simply a helper for `WithField`, invoking it +// once for each field. +// +// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal +// or Panic on the Entry it returns. +func WithFields(fields Fields) *Entry { + return std.WithFields(fields) +} + +// Debug logs a message at level Debug on the standard logger. +func Debug(args ...interface{}) { + std.Debug(args...) +} + +// Print logs a message at level Info on the standard logger. +func Print(args ...interface{}) { + std.Print(args...) +} + +// Info logs a message at level Info on the standard logger. +func Info(args ...interface{}) { + std.Info(args...) +} + +// Warn logs a message at level Warn on the standard logger. +func Warn(args ...interface{}) { + std.Warn(args...) +} + +// Warning logs a message at level Warn on the standard logger. +func Warning(args ...interface{}) { + std.Warning(args...) +} + +// Error logs a message at level Error on the standard logger. +func Error(args ...interface{}) { + std.Error(args...) +} + +// Panic logs a message at level Panic on the standard logger. +func Panic(args ...interface{}) { + std.Panic(args...) +} + +// Fatal logs a message at level Fatal on the standard logger. +func Fatal(args ...interface{}) { + std.Fatal(args...) +} + +// Debugf logs a message at level Debug on the standard logger. +func Debugf(format string, args ...interface{}) { + std.Debugf(format, args...) +} + +// Printf logs a message at level Info on the standard logger. +func Printf(format string, args ...interface{}) { + std.Printf(format, args...) +} + +// Infof logs a message at level Info on the standard logger. +func Infof(format string, args ...interface{}) { + std.Infof(format, args...) +} + +// Warnf logs a message at level Warn on the standard logger. +func Warnf(format string, args ...interface{}) { + std.Warnf(format, args...) +} + +// Warningf logs a message at level Warn on the standard logger. +func Warningf(format string, args ...interface{}) { + std.Warningf(format, args...) +} + +// Errorf logs a message at level Error on the standard logger. +func Errorf(format string, args ...interface{}) { + std.Errorf(format, args...) +} + +// Panicf logs a message at level Panic on the standard logger. +func Panicf(format string, args ...interface{}) { + std.Panicf(format, args...) +} + +// Fatalf logs a message at level Fatal on the standard logger. +func Fatalf(format string, args ...interface{}) { + std.Fatalf(format, args...) +} + +// Debugln logs a message at level Debug on the standard logger. +func Debugln(args ...interface{}) { + std.Debugln(args...) +} + +// Println logs a message at level Info on the standard logger. +func Println(args ...interface{}) { + std.Println(args...) +} + +// Infoln logs a message at level Info on the standard logger. +func Infoln(args ...interface{}) { + std.Infoln(args...) +} + +// Warnln logs a message at level Warn on the standard logger. +func Warnln(args ...interface{}) { + std.Warnln(args...) +} + +// Warningln logs a message at level Warn on the standard logger. +func Warningln(args ...interface{}) { + std.Warningln(args...) +} + +// Errorln logs a message at level Error on the standard logger. +func Errorln(args ...interface{}) { + std.Errorln(args...) +} + +// Panicln logs a message at level Panic on the standard logger. +func Panicln(args ...interface{}) { + std.Panicln(args...) +} + +// Fatalln logs a message at level Fatal on the standard logger. +func Fatalln(args ...interface{}) { + std.Fatalln(args...) +} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter.go new file mode 100644 index 00000000000..74c49a0e0e4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter.go @@ -0,0 +1,44 @@ +package logrus + +// The Formatter interface is used to implement a custom Formatter. It takes an +// `Entry`. It exposes all the fields, including the default ones: +// +// * `entry.Data["msg"]`. The message passed from Info, Warn, Error .. +// * `entry.Data["time"]`. The timestamp. +// * `entry.Data["level"]. The level the entry was logged at. +// +// Any additional fields added with `WithField` or `WithFields` are also in +// `entry.Data`. Format is expected to return an array of bytes which are then +// logged to `logger.Out`. +type Formatter interface { + Format(*Entry) ([]byte, error) +} + +// This is to not silently overwrite `time`, `msg` and `level` fields when +// dumping it. If this code wasn't there doing: +// +// logrus.WithField("level", 1).Info("hello") +// +// Would just silently drop the user provided level. Instead with this code +// it'll logged as: +// +// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."} +// +// It's not exported because it's still using Data in an opinionated way. It's to +// avoid code duplication between the two default formatters. +func prefixFieldClashes(entry *Entry) { + _, ok := entry.Data["time"] + if ok { + entry.Data["fields.time"] = entry.Data["time"] + } + + _, ok = entry.Data["msg"] + if ok { + entry.Data["fields.msg"] = entry.Data["msg"] + } + + _, ok = entry.Data["level"] + if ok { + entry.Data["fields.level"] = entry.Data["level"] + } +} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter_bench_test.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter_bench_test.go new file mode 100644 index 00000000000..77989da6297 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter_bench_test.go @@ -0,0 +1,88 @@ +package logrus + +import ( + "testing" + "time" +) + +// smallFields is a small size data set for benchmarking +var smallFields = Fields{ + "foo": "bar", + "baz": "qux", + "one": "two", + "three": "four", +} + +// largeFields is a large size data set for benchmarking +var largeFields = Fields{ + "foo": "bar", + "baz": "qux", + "one": "two", + "three": "four", + "five": "six", + "seven": "eight", + "nine": "ten", + "eleven": "twelve", + "thirteen": "fourteen", + "fifteen": "sixteen", + "seventeen": "eighteen", + "nineteen": "twenty", + "a": "b", + "c": "d", + "e": "f", + "g": "h", + "i": "j", + "k": "l", + "m": "n", + "o": "p", + "q": "r", + "s": "t", + "u": "v", + "w": "x", + "y": "z", + "this": "will", + "make": "thirty", + "entries": "yeah", +} + +func BenchmarkSmallTextFormatter(b *testing.B) { + doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields) +} + +func BenchmarkLargeTextFormatter(b *testing.B) { + doBenchmark(b, &TextFormatter{DisableColors: true}, largeFields) +} + +func BenchmarkSmallColoredTextFormatter(b *testing.B) { + doBenchmark(b, &TextFormatter{ForceColors: true}, smallFields) +} + +func BenchmarkLargeColoredTextFormatter(b *testing.B) { + doBenchmark(b, &TextFormatter{ForceColors: true}, largeFields) +} + +func BenchmarkSmallJSONFormatter(b *testing.B) { + doBenchmark(b, &JSONFormatter{}, smallFields) +} + +func BenchmarkLargeJSONFormatter(b *testing.B) { + doBenchmark(b, &JSONFormatter{}, largeFields) +} + +func doBenchmark(b *testing.B, formatter Formatter, fields Fields) { + entry := &Entry{ + Time: time.Time{}, + Level: InfoLevel, + Message: "message", + Data: fields, + } + var d []byte + var err error + for i := 0; i < b.N; i++ { + d, err = formatter.Format(entry) + if err != nil { + b.Fatal(err) + } + b.SetBytes(int64(len(d))) + } +} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hook_test.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hook_test.go new file mode 100644 index 00000000000..13f34cb6f81 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hook_test.go @@ -0,0 +1,122 @@ +package logrus + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +type TestHook struct { + Fired bool +} + +func (hook *TestHook) Fire(entry *Entry) error { + hook.Fired = true + return nil +} + +func (hook *TestHook) Levels() []Level { + return []Level{ + DebugLevel, + InfoLevel, + WarnLevel, + ErrorLevel, + FatalLevel, + PanicLevel, + } +} + +func TestHookFires(t *testing.T) { + hook := new(TestHook) + + LogAndAssertJSON(t, func(log *Logger) { + log.Hooks.Add(hook) + assert.Equal(t, hook.Fired, false) + + log.Print("test") + }, func(fields Fields) { + assert.Equal(t, hook.Fired, true) + }) +} + +type ModifyHook struct { +} + +func (hook *ModifyHook) Fire(entry *Entry) error { + entry.Data["wow"] = "whale" + return nil +} + +func (hook *ModifyHook) Levels() []Level { + return []Level{ + DebugLevel, + InfoLevel, + WarnLevel, + ErrorLevel, + FatalLevel, + PanicLevel, + } +} + +func TestHookCanModifyEntry(t *testing.T) { + hook := new(ModifyHook) + + LogAndAssertJSON(t, func(log *Logger) { + log.Hooks.Add(hook) + log.WithField("wow", "elephant").Print("test") + }, func(fields Fields) { + assert.Equal(t, fields["wow"], "whale") + }) +} + +func TestCanFireMultipleHooks(t *testing.T) { + hook1 := new(ModifyHook) + hook2 := new(TestHook) + + LogAndAssertJSON(t, func(log *Logger) { + log.Hooks.Add(hook1) + log.Hooks.Add(hook2) + + log.WithField("wow", "elephant").Print("test") + }, func(fields Fields) { + assert.Equal(t, fields["wow"], "whale") + assert.Equal(t, hook2.Fired, true) + }) +} + +type ErrorHook struct { + Fired bool +} + +func (hook *ErrorHook) Fire(entry *Entry) error { + hook.Fired = true + return nil +} + +func (hook *ErrorHook) Levels() []Level { + return []Level{ + ErrorLevel, + } +} + +func TestErrorHookShouldntFireOnInfo(t *testing.T) { + hook := new(ErrorHook) + + LogAndAssertJSON(t, func(log *Logger) { + log.Hooks.Add(hook) + log.Info("test") + }, func(fields Fields) { + assert.Equal(t, hook.Fired, false) + }) +} + +func TestErrorHookShouldFireOnError(t *testing.T) { + hook := new(ErrorHook) + + LogAndAssertJSON(t, func(log *Logger) { + log.Hooks.Add(hook) + log.Error("test") + }, func(fields Fields) { + assert.Equal(t, hook.Fired, true) + }) +} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks.go new file mode 100644 index 00000000000..0da2b3653f5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks.go @@ -0,0 +1,34 @@ +package logrus + +// A hook to be fired when logging on the logging levels returned from +// `Levels()` on your implementation of the interface. Note that this is not +// fired in a goroutine or a channel with workers, you should handle such +// functionality yourself if your call is non-blocking and you don't wish for +// the logging calls for levels returned from `Levels()` to block. +type Hook interface { + Levels() []Level + Fire(*Entry) error +} + +// Internal type for storing the hooks on a logger instance. +type levelHooks map[Level][]Hook + +// Add a hook to an instance of logger. This is called with +// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface. +func (hooks levelHooks) Add(hook Hook) { + for _, level := range hook.Levels() { + hooks[level] = append(hooks[level], hook) + } +} + +// Fire all the hooks for the passed level. Used by `entry.log` to fire +// appropriate hooks for a log entry. +func (hooks levelHooks) Fire(level Level, entry *Entry) error { + for _, hook := range hooks[level] { + if err := hook.Fire(entry); err != nil { + return err + } + } + + return nil +} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake.go new file mode 100644 index 00000000000..0162ddf73c3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake.go @@ -0,0 +1,54 @@ +package logrus_airbrake + +import ( + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus" + "github.com/tobi/airbrake-go" +) + +// AirbrakeHook to send exceptions to an exception-tracking service compatible +// with the Airbrake API. You must set: +// * airbrake.Endpoint +// * airbrake.ApiKey +// * airbrake.Environment (only sends exceptions when set to "production") +// +// Before using this hook, to send an error. Entries that trigger an Error, +// Fatal or Panic should now include an "error" field to send to Airbrake. +type AirbrakeHook struct{} + +func (hook *AirbrakeHook) Fire(entry *logrus.Entry) error { + if entry.Data["error"] == nil { + entry.Logger.WithFields(logrus.Fields{ + "source": "airbrake", + "endpoint": airbrake.Endpoint, + }).Warn("Exceptions sent to Airbrake must have an 'error' key with the error") + return nil + } + + err, ok := entry.Data["error"].(error) + if !ok { + entry.Logger.WithFields(logrus.Fields{ + "source": "airbrake", + "endpoint": airbrake.Endpoint, + }).Warn("Exceptions sent to Airbrake must have an `error` key of type `error`") + return nil + } + + airErr := airbrake.Notify(err) + if airErr != nil { + entry.Logger.WithFields(logrus.Fields{ + "source": "airbrake", + "endpoint": airbrake.Endpoint, + "error": airErr, + }).Warn("Failed to send error to Airbrake") + } + + return nil +} + +func (hook *AirbrakeHook) Levels() []logrus.Level { + return []logrus.Level{ + logrus.ErrorLevel, + logrus.FatalLevel, + logrus.PanicLevel, + } +} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/README.md b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/README.md new file mode 100644 index 00000000000..ae61e9229ab --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/README.md @@ -0,0 +1,28 @@ +# Papertrail Hook for Logrus :walrus: + +[Papertrail](https://papertrailapp.com) provides hosted log management. Once stored in Papertrail, you can [group](http://help.papertrailapp.com/kb/how-it-works/groups/) your logs on various dimensions, [search](http://help.papertrailapp.com/kb/how-it-works/search-syntax) them, and trigger [alerts](http://help.papertrailapp.com/kb/how-it-works/alerts). + +In most deployments, you'll want to send logs to Papertrail via their [remote_syslog](http://help.papertrailapp.com/kb/configuration/configuring-centralized-logging-from-text-log-files-in-unix/) daemon, which requires no application-specific configuration. This hook is intended for relatively low-volume logging, likely in managed cloud hosting deployments where installing `remote_syslog` is not possible. + +## Usage + +You can find your Papertrail UDP port on your [Papertrail account page](https://papertrailapp.com/account/destinations). Substitute it below for `YOUR_PAPERTRAIL_UDP_PORT`. + +For `YOUR_APP_NAME`, substitute a short string that will readily identify your application or service in the logs. + +```go +import ( + "log/syslog" + "github.com/Sirupsen/logrus" + "github.com/Sirupsen/logrus/hooks/papertrail" +) + +func main() { + log := logrus.New() + hook, err := logrus_papertrail.NewPapertrailHook("logs.papertrailapp.com", YOUR_PAPERTRAIL_UDP_PORT, YOUR_APP_NAME) + + if err == nil { + log.Hooks.Add(hook) + } +} +``` diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go new file mode 100644 index 00000000000..4d1765649ec --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go @@ -0,0 +1,54 @@ +package logrus_papertrail + +import ( + "fmt" + "net" + "os" + "time" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus" +) + +const ( + format = "Jan 2 15:04:05" +) + +// PapertrailHook to send logs to a logging service compatible with the Papertrail API. +type PapertrailHook struct { + Host string + Port int + AppName string + UDPConn net.Conn +} + +// NewPapertrailHook creates a hook to be added to an instance of logger. +func NewPapertrailHook(host string, port int, appName string) (*PapertrailHook, error) { + conn, err := net.Dial("udp", fmt.Sprintf("%s:%d", host, port)) + return &PapertrailHook{host, port, appName, conn}, err +} + +// Fire is called when a log event is fired. +func (hook *PapertrailHook) Fire(entry *logrus.Entry) error { + date := time.Now().Format(format) + payload := fmt.Sprintf("<22> %s %s: [%s] %s", date, hook.AppName, entry.Data["level"], entry.Message) + + bytesWritten, err := hook.UDPConn.Write([]byte(payload)) + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to send log line to Papertrail via UDP. Wrote %d bytes before error: %v", bytesWritten, err) + return err + } + + return nil +} + +// Levels returns the available logging levels. +func (hook *PapertrailHook) Levels() []logrus.Level { + return []logrus.Level{ + logrus.PanicLevel, + logrus.FatalLevel, + logrus.ErrorLevel, + logrus.WarnLevel, + logrus.InfoLevel, + logrus.DebugLevel, + } +} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail_test.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail_test.go new file mode 100644 index 00000000000..f8146fda58c --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail_test.go @@ -0,0 +1,26 @@ +package logrus_papertrail + +import ( + "fmt" + "testing" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus" + "github.com/stvp/go-udp-testing" +) + +func TestWritingToUDP(t *testing.T) { + port := 16661 + udp.SetAddr(fmt.Sprintf(":%d", port)) + + hook, err := NewPapertrailHook("localhost", port, "test") + if err != nil { + t.Errorf("Unable to connect to local UDP server.") + } + + log := logrus.New() + log.Hooks.Add(hook) + + udp.ShouldReceive(t, "foo", func() { + log.Info("foo") + }) +} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/README.md b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/README.md new file mode 100644 index 00000000000..cd706bc1b17 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/README.md @@ -0,0 +1,20 @@ +# Syslog Hooks for Logrus :walrus: + +## Usage + +```go +import ( + "log/syslog" + "github.com/Sirupsen/logrus" + "github.com/Sirupsen/logrus/hooks/syslog" +) + +func main() { + log := logrus.New() + hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "") + + if err == nil { + log.Hooks.Add(hook) + } +} +``` \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go new file mode 100644 index 00000000000..fcc67bbf70a --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go @@ -0,0 +1,59 @@ +package logrus_syslog + +import ( + "fmt" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus" + "log/syslog" + "os" +) + +// SyslogHook to send logs via syslog. +type SyslogHook struct { + Writer *syslog.Writer + SyslogNetwork string + SyslogRaddr string +} + +// Creates a hook to be added to an instance of logger. This is called with +// `hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_DEBUG, "")` +// `if err == nil { log.Hooks.Add(hook) }` +func NewSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*SyslogHook, error) { + w, err := syslog.Dial(network, raddr, priority, tag) + return &SyslogHook{w, network, raddr}, err +} + +func (hook *SyslogHook) Fire(entry *logrus.Entry) error { + line, err := entry.String() + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err) + return err + } + + switch entry.Data["level"] { + case "panic": + return hook.Writer.Crit(line) + case "fatal": + return hook.Writer.Crit(line) + case "error": + return hook.Writer.Err(line) + case "warn": + return hook.Writer.Warning(line) + case "info": + return hook.Writer.Info(line) + case "debug": + return hook.Writer.Debug(line) + default: + return nil + } +} + +func (hook *SyslogHook) Levels() []logrus.Level { + return []logrus.Level{ + logrus.PanicLevel, + logrus.FatalLevel, + logrus.ErrorLevel, + logrus.WarnLevel, + logrus.InfoLevel, + logrus.DebugLevel, + } +} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go new file mode 100644 index 00000000000..f26c87be82e --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go @@ -0,0 +1,26 @@ +package logrus_syslog + +import ( + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus" + "log/syslog" + "testing" +) + +func TestLocalhostAddAndPrint(t *testing.T) { + log := logrus.New() + hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "") + + if err != nil { + t.Errorf("Unable to connect to local syslog.") + } + + log.Hooks.Add(hook) + + for _, level := range hook.Levels() { + if len(log.Hooks[level]) != 1 { + t.Errorf("SyslogHook was not added. The length of log.Hooks[%v]: %v", level, len(log.Hooks[level])) + } + } + + log.Info("Congratulations!") +} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter.go new file mode 100644 index 00000000000..9d11b642d46 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter.go @@ -0,0 +1,22 @@ +package logrus + +import ( + "encoding/json" + "fmt" + "time" +) + +type JSONFormatter struct{} + +func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { + prefixFieldClashes(entry) + entry.Data["time"] = entry.Time.Format(time.RFC3339) + entry.Data["msg"] = entry.Message + entry.Data["level"] = entry.Level.String() + + serialized, err := json.Marshal(entry.Data) + if err != nil { + return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) + } + return append(serialized, '\n'), nil +} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/logger.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/logger.go new file mode 100644 index 00000000000..7374fe365d3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/logger.go @@ -0,0 +1,161 @@ +package logrus + +import ( + "io" + "os" + "sync" +) + +type Logger struct { + // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a + // file, or leave it default which is `os.Stdout`. You can also set this to + // something more adventorous, such as logging to Kafka. + Out io.Writer + // Hooks for the logger instance. These allow firing events based on logging + // levels and log entries. For example, to send errors to an error tracking + // service, log to StatsD or dump the core on fatal errors. + Hooks levelHooks + // All log entries pass through the formatter before logged to Out. The + // included formatters are `TextFormatter` and `JSONFormatter` for which + // TextFormatter is the default. In development (when a TTY is attached) it + // logs with colors, but to a file it wouldn't. You can easily implement your + // own that implements the `Formatter` interface, see the `README` or included + // formatters for examples. + Formatter Formatter + // The logging level the logger should log at. This is typically (and defaults + // to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be + // logged. `logrus.Debug` is useful in + Level Level + // Used to sync writing to the log. + mu sync.Mutex +} + +// Creates a new logger. Configuration should be set by changing `Formatter`, +// `Out` and `Hooks` directly on the default logger instance. You can also just +// instantiate your own: +// +// var log = &Logger{ +// Out: os.Stderr, +// Formatter: new(JSONFormatter), +// Hooks: make(levelHooks), +// Level: logrus.Debug, +// } +// +// It's recommended to make this a global instance called `log`. +func New() *Logger { + return &Logger{ + Out: os.Stdout, + Formatter: new(TextFormatter), + Hooks: make(levelHooks), + Level: InfoLevel, + } +} + +// Adds a field to the log entry, note that you it doesn't log until you call +// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry. +// Ff you want multiple fields, use `WithFields`. +func (logger *Logger) WithField(key string, value interface{}) *Entry { + return NewEntry(logger).WithField(key, value) +} + +// Adds a struct of fields to the log entry. All it does is call `WithField` for +// each `Field`. +func (logger *Logger) WithFields(fields Fields) *Entry { + return NewEntry(logger).WithFields(fields) +} + +func (logger *Logger) Debugf(format string, args ...interface{}) { + NewEntry(logger).Debugf(format, args...) +} + +func (logger *Logger) Infof(format string, args ...interface{}) { + NewEntry(logger).Infof(format, args...) +} + +func (logger *Logger) Printf(format string, args ...interface{}) { + NewEntry(logger).Printf(format, args...) +} + +func (logger *Logger) Warnf(format string, args ...interface{}) { + NewEntry(logger).Warnf(format, args...) +} + +func (logger *Logger) Warningf(format string, args ...interface{}) { + NewEntry(logger).Warnf(format, args...) +} + +func (logger *Logger) Errorf(format string, args ...interface{}) { + NewEntry(logger).Errorf(format, args...) +} + +func (logger *Logger) Fatalf(format string, args ...interface{}) { + NewEntry(logger).Fatalf(format, args...) +} + +func (logger *Logger) Panicf(format string, args ...interface{}) { + NewEntry(logger).Panicf(format, args...) +} + +func (logger *Logger) Debug(args ...interface{}) { + NewEntry(logger).Debug(args...) +} + +func (logger *Logger) Info(args ...interface{}) { + NewEntry(logger).Info(args...) +} + +func (logger *Logger) Print(args ...interface{}) { + NewEntry(logger).Info(args...) +} + +func (logger *Logger) Warn(args ...interface{}) { + NewEntry(logger).Warn(args...) +} + +func (logger *Logger) Warning(args ...interface{}) { + NewEntry(logger).Warn(args...) +} + +func (logger *Logger) Error(args ...interface{}) { + NewEntry(logger).Error(args...) +} + +func (logger *Logger) Fatal(args ...interface{}) { + NewEntry(logger).Fatal(args...) +} + +func (logger *Logger) Panic(args ...interface{}) { + NewEntry(logger).Panic(args...) +} + +func (logger *Logger) Debugln(args ...interface{}) { + NewEntry(logger).Debugln(args...) +} + +func (logger *Logger) Infoln(args ...interface{}) { + NewEntry(logger).Infoln(args...) +} + +func (logger *Logger) Println(args ...interface{}) { + NewEntry(logger).Println(args...) +} + +func (logger *Logger) Warnln(args ...interface{}) { + NewEntry(logger).Warnln(args...) +} + +func (logger *Logger) Warningln(args ...interface{}) { + NewEntry(logger).Warnln(args...) +} + +func (logger *Logger) Errorln(args ...interface{}) { + NewEntry(logger).Errorln(args...) +} + +func (logger *Logger) Fatalln(args ...interface{}) { + NewEntry(logger).Fatalln(args...) +} + +func (logger *Logger) Panicln(args ...interface{}) { + NewEntry(logger).Panicln(args...) +} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus.go new file mode 100644 index 00000000000..43ee12e90e7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus.go @@ -0,0 +1,94 @@ +package logrus + +import ( + "fmt" + "log" +) + +// Fields type, used to pass to `WithFields`. +type Fields map[string]interface{} + +// Level type +type Level uint8 + +// Convert the Level to a string. E.g. PanicLevel becomes "panic". +func (level Level) String() string { + switch level { + case DebugLevel: + return "debug" + case InfoLevel: + return "info" + case WarnLevel: + return "warning" + case ErrorLevel: + return "error" + case FatalLevel: + return "fatal" + case PanicLevel: + return "panic" + } + + return "unknown" +} + +// ParseLevel takes a string level and returns the Logrus log level constant. +func ParseLevel(lvl string) (Level, error) { + switch lvl { + case "panic": + return PanicLevel, nil + case "fatal": + return FatalLevel, nil + case "error": + return ErrorLevel, nil + case "warn", "warning": + return WarnLevel, nil + case "info": + return InfoLevel, nil + case "debug": + return DebugLevel, nil + } + + var l Level + return l, fmt.Errorf("not a valid logrus Level: %q", lvl) +} + +// These are the different logging levels. You can set the logging level to log +// on your instance of logger, obtained with `logrus.New()`. +const ( + // PanicLevel level, highest level of severity. Logs and then calls panic with the + // message passed to Debug, Info, ... + PanicLevel Level = iota + // FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the + // logging level is set to Panic. + FatalLevel + // ErrorLevel level. Logs. Used for errors that should definitely be noted. + // Commonly used for hooks to send errors to an error tracking service. + ErrorLevel + // WarnLevel level. Non-critical entries that deserve eyes. + WarnLevel + // InfoLevel level. General operational entries about what's going on inside the + // application. + InfoLevel + // DebugLevel level. Usually only enabled when debugging. Very verbose logging. + DebugLevel +) + +// Won't compile if StdLogger can't be realized by a log.Logger +var _ StdLogger = &log.Logger{} + +// StdLogger is what your logrus-enabled library should take, that way +// it'll accept a stdlib logger and a logrus logger. There's no standard +// interface, this is the closest we get, unfortunately. +type StdLogger interface { + Print(...interface{}) + Printf(string, ...interface{}) + Println(...interface{}) + + Fatal(...interface{}) + Fatalf(string, ...interface{}) + Fatalln(...interface{}) + + Panic(...interface{}) + Panicf(string, ...interface{}) + Panicln(...interface{}) +} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus_test.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus_test.go new file mode 100644 index 00000000000..15157d172da --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus_test.go @@ -0,0 +1,247 @@ +package logrus + +import ( + "bytes" + "encoding/json" + "strconv" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) { + var buffer bytes.Buffer + var fields Fields + + logger := New() + logger.Out = &buffer + logger.Formatter = new(JSONFormatter) + + log(logger) + + err := json.Unmarshal(buffer.Bytes(), &fields) + assert.Nil(t, err) + + assertions(fields) +} + +func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) { + var buffer bytes.Buffer + + logger := New() + logger.Out = &buffer + logger.Formatter = &TextFormatter{ + DisableColors: true, + } + + log(logger) + + fields := make(map[string]string) + for _, kv := range strings.Split(buffer.String(), " ") { + if !strings.Contains(kv, "=") { + continue + } + kvArr := strings.Split(kv, "=") + key := strings.TrimSpace(kvArr[0]) + val, err := strconv.Unquote(kvArr[1]) + assert.NoError(t, err) + fields[key] = val + } + assertions(fields) +} + +func TestPrint(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Print("test") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test") + assert.Equal(t, fields["level"], "info") + }) +} + +func TestInfo(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Info("test") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test") + assert.Equal(t, fields["level"], "info") + }) +} + +func TestWarn(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Warn("test") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test") + assert.Equal(t, fields["level"], "warning") + }) +} + +func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Infoln("test", "test") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test test") + }) +} + +func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Infoln("test", 10) + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test 10") + }) +} + +func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Infoln(10, 10) + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "10 10") + }) +} + +func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Infoln(10, 10) + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "10 10") + }) +} + +func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Info("test", 10) + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test10") + }) +} + +func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Info("test", "test") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "testtest") + }) +} + +func TestWithFieldsShouldAllowAssignments(t *testing.T) { + var buffer bytes.Buffer + var fields Fields + + logger := New() + logger.Out = &buffer + logger.Formatter = new(JSONFormatter) + + localLog := logger.WithFields(Fields{ + "key1": "value1", + }) + + localLog.WithField("key2", "value2").Info("test") + err := json.Unmarshal(buffer.Bytes(), &fields) + assert.Nil(t, err) + + assert.Equal(t, "value2", fields["key2"]) + assert.Equal(t, "value1", fields["key1"]) + + buffer = bytes.Buffer{} + fields = Fields{} + localLog.Info("test") + err = json.Unmarshal(buffer.Bytes(), &fields) + assert.Nil(t, err) + + _, ok := fields["key2"] + assert.Equal(t, false, ok) + assert.Equal(t, "value1", fields["key1"]) +} + +func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.WithField("msg", "hello").Info("test") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test") + }) +} + +func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.WithField("msg", "hello").Info("test") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test") + assert.Equal(t, fields["fields.msg"], "hello") + }) +} + +func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.WithField("time", "hello").Info("test") + }, func(fields Fields) { + assert.Equal(t, fields["fields.time"], "hello") + }) +} + +func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.WithField("level", 1).Info("test") + }, func(fields Fields) { + assert.Equal(t, fields["level"], "info") + assert.Equal(t, fields["fields.level"], 1) + }) +} + +func TestDefaultFieldsAreNotPrefixed(t *testing.T) { + LogAndAssertText(t, func(log *Logger) { + ll := log.WithField("herp", "derp") + ll.Info("hello") + ll.Info("bye") + }, func(fields map[string]string) { + for _, fieldName := range []string{"fields.level", "fields.time", "fields.msg"} { + if _, ok := fields[fieldName]; ok { + t.Fatalf("should not have prefixed %q: %v", fieldName, fields) + } + } + }) +} + +func TestConvertLevelToString(t *testing.T) { + assert.Equal(t, "debug", DebugLevel.String()) + assert.Equal(t, "info", InfoLevel.String()) + assert.Equal(t, "warning", WarnLevel.String()) + assert.Equal(t, "error", ErrorLevel.String()) + assert.Equal(t, "fatal", FatalLevel.String()) + assert.Equal(t, "panic", PanicLevel.String()) +} + +func TestParseLevel(t *testing.T) { + l, err := ParseLevel("panic") + assert.Nil(t, err) + assert.Equal(t, PanicLevel, l) + + l, err = ParseLevel("fatal") + assert.Nil(t, err) + assert.Equal(t, FatalLevel, l) + + l, err = ParseLevel("error") + assert.Nil(t, err) + assert.Equal(t, ErrorLevel, l) + + l, err = ParseLevel("warn") + assert.Nil(t, err) + assert.Equal(t, WarnLevel, l) + + l, err = ParseLevel("warning") + assert.Nil(t, err) + assert.Equal(t, WarnLevel, l) + + l, err = ParseLevel("info") + assert.Nil(t, err) + assert.Equal(t, InfoLevel, l) + + l, err = ParseLevel("debug") + assert.Nil(t, err) + assert.Equal(t, DebugLevel, l) + + l, err = ParseLevel("invalid") + assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error()) +} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_darwin.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_darwin.go new file mode 100644 index 00000000000..8fe02a4aec1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_darwin.go @@ -0,0 +1,12 @@ +// Based on ssh/terminal: +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package logrus + +import "syscall" + +const ioctlReadTermios = syscall.TIOCGETA + +type Termios syscall.Termios diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_freebsd.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_freebsd.go new file mode 100644 index 00000000000..0428ee5d52a --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_freebsd.go @@ -0,0 +1,20 @@ +/* + Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin. +*/ +package logrus + +import ( + "syscall" +) + +const ioctlReadTermios = syscall.TIOCGETA + +type Termios struct { + Iflag uint32 + Oflag uint32 + Cflag uint32 + Lflag uint32 + Cc [20]uint8 + Ispeed uint32 + Ospeed uint32 +} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_linux.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_linux.go new file mode 100644 index 00000000000..a2c0b40db61 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_linux.go @@ -0,0 +1,12 @@ +// Based on ssh/terminal: +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package logrus + +import "syscall" + +const ioctlReadTermios = syscall.TCGETS + +type Termios syscall.Termios diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_notwindows.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_notwindows.go new file mode 100644 index 00000000000..276447bd5c0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_notwindows.go @@ -0,0 +1,21 @@ +// Based on ssh/terminal: +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux,!appengine darwin freebsd + +package logrus + +import ( + "syscall" + "unsafe" +) + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal() bool { + fd := syscall.Stdout + var termios Termios + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) + return err == 0 +} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_windows.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_windows.go new file mode 100644 index 00000000000..2e09f6f7e31 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_windows.go @@ -0,0 +1,27 @@ +// Based on ssh/terminal: +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package logrus + +import ( + "syscall" + "unsafe" +) + +var kernel32 = syscall.NewLazyDLL("kernel32.dll") + +var ( + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") +) + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal() bool { + fd := syscall.Stdout + var st uint32 + r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) + return r != 0 && e == 0 +} diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter.go b/Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter.go new file mode 100644 index 00000000000..fc0a4082a75 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter.go @@ -0,0 +1,95 @@ +package logrus + +import ( + "bytes" + "fmt" + "sort" + "strings" + "time" +) + +const ( + nocolor = 0 + red = 31 + green = 32 + yellow = 33 + blue = 34 +) + +var ( + baseTimestamp time.Time + isTerminal bool +) + +func init() { + baseTimestamp = time.Now() + isTerminal = IsTerminal() +} + +func miniTS() int { + return int(time.Since(baseTimestamp) / time.Second) +} + +type TextFormatter struct { + // Set to true to bypass checking for a TTY before outputting colors. + ForceColors bool + DisableColors bool +} + +func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { + + var keys []string + for k := range entry.Data { + keys = append(keys, k) + } + sort.Strings(keys) + + b := &bytes.Buffer{} + + prefixFieldClashes(entry) + + isColored := (f.ForceColors || isTerminal) && !f.DisableColors + + if isColored { + printColored(b, entry, keys) + } else { + f.appendKeyValue(b, "time", entry.Time.Format(time.RFC3339)) + f.appendKeyValue(b, "level", entry.Level.String()) + f.appendKeyValue(b, "msg", entry.Message) + for _, key := range keys { + f.appendKeyValue(b, key, entry.Data[key]) + } + } + + b.WriteByte('\n') + return b.Bytes(), nil +} + +func printColored(b *bytes.Buffer, entry *Entry, keys []string) { + var levelColor int + switch entry.Level { + case WarnLevel: + levelColor = yellow + case ErrorLevel, FatalLevel, PanicLevel: + levelColor = red + default: + levelColor = blue + } + + levelText := strings.ToUpper(entry.Level.String())[0:4] + + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message) + for _, k := range keys { + v := entry.Data[k] + fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%v", levelColor, k, v) + } +} + +func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key, value interface{}) { + switch value.(type) { + case string, error: + fmt.Fprintf(b, "%v=%q ", key, value) + default: + fmt.Fprintf(b, "%v=%v ", key, value) + } +} diff --git a/util/elog/log.go b/util/elog/log.go index 5ff3e0dfd0f..84a4a7dd58a 100644 --- a/util/elog/log.go +++ b/util/elog/log.go @@ -1,7 +1,10 @@ package elog import ( + "os" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus" logging "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-logging" "github.com/jbenet/go-ipfs/util" ) @@ -27,6 +30,8 @@ type EventLogger interface { Event(ctx context.Context, event string, m ...Loggable) } +// StandardLogger provides API compatibility with standard printf loggers +// eg. go-logging type StandardLogger interface { Critical(args ...interface{}) Criticalf(format string, args ...interface{}) @@ -46,14 +51,15 @@ type StandardLogger interface { Warningf(format string, args ...interface{}) } -// Logger retrieves a particular event logger +// Logger retrieves an event logger by name func Logger(system string) EventLogger { - return &eventLogger{util.Logger(system)} + return &eventLogger{system: system, Logger: util.Logger(system)} } // eventLogger implements the EventLogger and wraps a go-logging Logger type eventLogger struct { *logging.Logger + system string } func (el *eventLogger) Event(ctx context.Context, event string, metadata ...Loggable) { @@ -66,10 +72,15 @@ func (el *eventLogger) Event(ctx context.Context, event string, metadata ...Logg accum = DeepMerge(accum, datum.Loggable()) } accum["event"] = event + accum["system"] = el.system - str, err := accum.JsonString() - if err != nil { - return - } - el.Logger.Info(str) + logrus.WithFields(map[string]interface{}(accum)).Info(event) +} + +func init() { + // TODO use both text formatter and json formatter + // logrus.SetFormatter(new(logrus.TextFormatter)) + logrus.SetFormatter(new(logrus.JSONFormatter)) + logrus.SetOutput(os.Stderr) + logrus.SetLevel(logrus.DebugLevel) } From 395f8a6e506a6abb263559d1a13b3008114cee76 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 01:40:31 -0800 Subject: [PATCH 18/42] style(errors) import as errors License: MIT Signed-off-by: Brian Tiger Chow --- config/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/config.go b/config/config.go index 1416bb139c0..c409f68388a 100644 --- a/config/config.go +++ b/config/config.go @@ -9,7 +9,7 @@ import ( "path/filepath" u "github.com/jbenet/go-ipfs/util" - "github.com/jbenet/go-ipfs/util/debugerror" + errors "github.com/jbenet/go-ipfs/util/debugerror" ) var log = u.Logger("config") @@ -129,7 +129,7 @@ func (i *Identity) DecodePrivateKey(passphrase string) (crypto.PrivateKey, error func Load(filename string) (*Config, error) { // if nothing is there, fail. User must run 'ipfs init' if _, err := os.Stat(filename); os.IsNotExist(err) { - return nil, debugerror.New("ipfs not initialized, please run 'ipfs init'") + return nil, errors.New("ipfs not initialized, please run 'ipfs init'") } var cfg Config From d55dd16bf5950fb5f4e35632a687a94605d6ea78 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 02:13:45 -0800 Subject: [PATCH 19/42] feat(elog) Option License: MIT Signed-off-by: Brian Tiger Chow --- util/elog/option.go | 50 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 util/elog/option.go diff --git a/util/elog/option.go b/util/elog/option.go new file mode 100644 index 00000000000..876c400ca48 --- /dev/null +++ b/util/elog/option.go @@ -0,0 +1,50 @@ +package elog + +import ( + "io" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2" +) + +type Option func() + +func SetOption(o Option) { + o() +} + +var JSONFormatter = func() { + logrus.SetFormatter(&logrus.JSONFormatter{}) +} + +var TextFormatter = func() { + logrus.SetFormatter(&logrus.TextFormatter{}) +} + +type LogRotatorConfig struct { + File string + MaxSizeMB uint64 + MaxBackups uint64 + MaxAgeDays uint64 +} + +func Output(w io.Writer) Option { + return func() { + logrus.SetOutput(w) + // TODO return previous Output option + } +} + +func OutputRotatingLogFile(config LogRotatorConfig) Option { + return func() { + logrus.SetOutput( + &lumberjack.Logger{ + Filename: config.File, + MaxSize: int(config.MaxSizeMB), + MaxBackups: int(config.MaxBackups), + MaxAge: int(config.MaxAgeDays), + }) + } +} + +// TODO log levels? logrus.SetLevel(logrus.DebugLevel) From 2b72d9f14a8c0dc0279011ab484d1cd068999a0a Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 03:24:34 -0800 Subject: [PATCH 20/42] deps(logging) vendor lumberjack (MIT) log rotation License: MIT Signed-off-by: Brian Tiger Chow --- Godeps/Godeps.json | 5 + .../natefinch/lumberjack.v2/.gitignore | 23 + .../gopkg.in/natefinch/lumberjack.v2/LICENSE | 21 + .../natefinch/lumberjack.v2/README.md | 166 +++++ .../gopkg.in/natefinch/lumberjack.v2/chown.go | 11 + .../natefinch/lumberjack.v2/chown_linux.go | 19 + .../natefinch/lumberjack.v2/example_test.go | 18 + .../natefinch/lumberjack.v2/linux_test.go | 104 +++ .../natefinch/lumberjack.v2/lumberjack.go | 415 ++++++++++++ .../lumberjack.v2/lumberjack_test.go | 634 ++++++++++++++++++ .../natefinch/lumberjack.v2/rotate_test.go | 27 + .../natefinch/lumberjack.v2/testing_test.go | 91 +++ 12 files changed, 1534 insertions(+) create mode 100644 Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/.gitignore create mode 100644 Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/LICENSE create mode 100644 Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/README.md create mode 100644 Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/chown.go create mode 100644 Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/chown_linux.go create mode 100644 Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/example_test.go create mode 100644 Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/linux_test.go create mode 100644 Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/lumberjack.go create mode 100644 Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/lumberjack_test.go create mode 100644 Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/rotate_test.go create mode 100644 Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/testing_test.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index c586c7ac84e..31b3b125c4f 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -147,6 +147,11 @@ { "ImportPath": "github.com/tuxychandru/pubsub", "Rev": "02de8aa2db3d570c5ab1be5ba67b456fd0fb7c4e" + }, + { + "ImportPath": "gopkg.in/natefinch/lumberjack.v2", + "Comment": "v1.0-12-gd28785c", + "Rev": "d28785c2f27cd682d872df46ccd8232843629f54" } ] } diff --git a/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/.gitignore b/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/.gitignore new file mode 100644 index 00000000000..836562412fe --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/.gitignore @@ -0,0 +1,23 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test diff --git a/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/LICENSE b/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/LICENSE new file mode 100644 index 00000000000..c3d4cc307d2 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Nate Finch + +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. \ No newline at end of file diff --git a/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/README.md b/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/README.md new file mode 100644 index 00000000000..f7d8132dea2 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/README.md @@ -0,0 +1,166 @@ +# lumberjack [![GoDoc](https://godoc.org/gopkg.in/natefinch/lumberjack.v2?status.png)](https://godoc.org/gopkg.in/natefinch/lumberjack.v2) [![Build Status](https://drone.io/github.com/natefinch/lumberjack/status.png)](https://drone.io/github.com/natefinch/lumberjack/latest) [![Build status](https://ci.appveyor.com/api/projects/status/00gchpxtg4gkrt5d)](https://ci.appveyor.com/project/natefinch/lumberjack) + +### Lumberjack is a Go package for writing logs to rolling files. + +Package lumberjack provides a rolling logger. + +Note that this is v2.0 of lumberjack, and should be imported using gopkg.in +thusly: + + import "gopkg.in/natefinch/lumberjack.v2" + +The package name remains simply lumberjack, and the code resides at +https://github.com/natefinch/lumberjack under the v2.0 branch. + +Lumberjack is intended to be one part of a logging infrastructure. +It is not an all-in-one solution, but instead is a pluggable +component at the bottom of the logging stack that simply controls the files +to which logs are written. + +Lumberjack plays well with any logging package that can write to an +io.Writer, including the standard library's log package. + +Lumberjack assumes that only one process is writing to the output files. +Using the same lumberjack configuration from multiple processes on the same +machine will result in improper behavior. + + +**Example** + +To use lumberjack with the standard library's log package, just pass it into the SetOutput function when your application starts. + +Code: + +```go +log.SetOutput(&lumberjack.Logger{ + Filename: "/var/log/myapp/foo.log", + MaxSize: 500, // megabytes + MaxBackups: 3, + MaxAge: 28, //days +}) +``` + + + +## type Logger +``` go +type Logger struct { + // Filename is the file to write logs to. Backup log files will be retained + // in the same directory. It uses -lumberjack.log in + // os.TempDir() if empty. + Filename string `json:"filename" yaml:"filename"` + + // MaxSize is the maximum size in megabytes of the log file before it gets + // rotated. It defaults to 100 megabytes. + MaxSize int `json:"maxsize" yaml:"maxsize"` + + // MaxAge is the maximum number of days to retain old log files based on the + // timestamp encoded in their filename. Note that a day is defined as 24 + // hours and may not exactly correspond to calendar days due to daylight + // savings, leap seconds, etc. The default is not to remove old log files + // based on age. + MaxAge int `json:"maxage" yaml:"maxage"` + + // MaxBackups is the maximum number of old log files to retain. The default + // is to retain all old log files (though MaxAge may still cause them to get + // deleted.) + MaxBackups int `json:"maxbackups" yaml:"maxbackups"` + + // LocalTime determines if the time used for formatting the timestamps in + // backup files is the computer's local time. The default is to use UTC + // time. + LocalTime bool `json:"localtime" yaml:"localtime"` + // contains filtered or unexported fields +} +``` +Logger is an io.WriteCloser that writes to the specified filename. + +Logger opens or creates the logfile on first Write. If the file exists and +is less than MaxSize megabytes, lumberjack will open and append to that file. +If the file exists and its size is >= MaxSize megabytes, the file is renamed +by putting the current time in a timestamp in the name immediately before the +file's extension (or the end of the filename if there's no extension). A new +log file is then created using original filename. + +Whenever a write would cause the current log file exceed MaxSize megabytes, +the current file is closed, renamed, and a new log file created with the +original name. Thus, the filename you give Logger is always the "current" log +file. + +### Cleaning Up Old Log Files +Whenever a new logfile gets created, old log files may be deleted. The most +recent files according to the encoded timestamp will be retained, up to a +number equal to MaxBackups (or all of them if MaxBackups is 0). Any files +with an encoded timestamp older than MaxAge days are deleted, regardless of +MaxBackups. Note that the time encoded in the timestamp is the rotation +time, which may differ from the last time that file was written to. + +If MaxBackups and MaxAge are both 0, no old log files will be deleted. + + + + + + + + + + + +### func (\*Logger) Close +``` go +func (l *Logger) Close() error +``` +Close implements io.Closer, and closes the current logfile. + + + +### func (\*Logger) Rotate +``` go +func (l *Logger) Rotate() error +``` +Rotate causes Logger to close the existing log file and immediately create a +new one. This is a helper function for applications that want to initiate +rotations outside of the normal rotation rules, such as in response to +SIGHUP. After rotating, this initiates a cleanup of old log files according +to the normal rules. + +**Example** + +Example of how to rotate in response to SIGHUP. + +Code: + +```go +l := &lumberjack.Logger{} +log.SetOutput(l) +c := make(chan os.Signal, 1) +signal.Notify(c, syscall.SIGHUP) + +go func() { + for { + <-c + l.Rotate() + } +}() +``` + +### func (\*Logger) Write +``` go +func (l *Logger) Write(p []byte) (n int, err error) +``` +Write implements io.Writer. If a write would cause the log file to be larger +than MaxSize, the file is closed, renamed to include a timestamp of the +current time, and a new log file is created using the original log file name. +If the length of the write is greater than MaxSize, an error is returned. + + + + + + + + + +- - - +Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md) diff --git a/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/chown.go b/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/chown.go new file mode 100644 index 00000000000..11d06697232 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/chown.go @@ -0,0 +1,11 @@ +// +build !linux + +package lumberjack + +import ( + "os" +) + +func chown(_ string, _ os.FileInfo) error { + return nil +} diff --git a/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/chown_linux.go b/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/chown_linux.go new file mode 100644 index 00000000000..2758ec9cedd --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/chown_linux.go @@ -0,0 +1,19 @@ +package lumberjack + +import ( + "os" + "syscall" +) + +// os_Chown is a var so we can mock it out during tests. +var os_Chown = os.Chown + +func chown(name string, info os.FileInfo) error { + f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, info.Mode()) + if err != nil { + return err + } + f.Close() + stat := info.Sys().(*syscall.Stat_t) + return os_Chown(name, int(stat.Uid), int(stat.Gid)) +} diff --git a/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/example_test.go b/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/example_test.go new file mode 100644 index 00000000000..acc96c92e02 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/example_test.go @@ -0,0 +1,18 @@ +package lumberjack_test + +import ( + "log" + + "github.com/natefinch/lumberjack" +) + +// To use lumberjack with the standard library's log package, just pass it into +// the SetOutput function when your application starts. +func Example() { + log.SetOutput(&lumberjack.Logger{ + Filename: "/var/log/myapp/foo.log", + MaxSize: 500, // megabytes + MaxBackups: 3, + MaxAge: 28, // days + }) +} diff --git a/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/linux_test.go b/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/linux_test.go new file mode 100644 index 00000000000..40f3446685c --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/linux_test.go @@ -0,0 +1,104 @@ +// +build linux + +package lumberjack + +import ( + "os" + "syscall" + "testing" +) + +func TestMaintainMode(t *testing.T) { + currentTime = fakeTime + dir := makeTempDir("TestMaintainMode", t) + defer os.RemoveAll(dir) + + filename := logFile(dir) + + mode := os.FileMode(0770) + f, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, mode) + isNil(err, t) + f.Close() + + l := &Logger{ + Filename: filename, + MaxBackups: 1, + MaxSize: 100, // megabytes + } + defer l.Close() + b := []byte("boo!") + n, err := l.Write(b) + isNil(err, t) + equals(len(b), n, t) + + newFakeTime() + + err = l.Rotate() + isNil(err, t) + + filename2 := backupFile(dir) + info, err := os.Stat(filename) + isNil(err, t) + info2, err := os.Stat(filename2) + isNil(err, t) + equals(mode, info.Mode(), t) + equals(mode, info2.Mode(), t) +} + +func TestMaintainOwner(t *testing.T) { + fakeC := fakeChown{} + os_Chown = fakeC.Set + os_Stat = fakeStat + defer func() { + os_Chown = os.Chown + os_Stat = os.Stat + }() + currentTime = fakeTime + dir := makeTempDir("TestMaintainOwner", t) + defer os.RemoveAll(dir) + + filename := logFile(dir) + + l := &Logger{ + Filename: filename, + MaxBackups: 1, + MaxSize: 100, // megabytes + } + defer l.Close() + b := []byte("boo!") + n, err := l.Write(b) + isNil(err, t) + equals(len(b), n, t) + + newFakeTime() + + err = l.Rotate() + isNil(err, t) + + equals(555, fakeC.uid, t) + equals(666, fakeC.gid, t) +} + +type fakeChown struct { + name string + uid int + gid int +} + +func (f *fakeChown) Set(name string, uid, gid int) error { + f.name = name + f.uid = uid + f.gid = gid + return nil +} + +func fakeStat(name string) (os.FileInfo, error) { + info, err := os.Stat(name) + if err != nil { + return info, err + } + stat := info.Sys().(*syscall.Stat_t) + stat.Uid = 555 + stat.Gid = 666 + return info, nil +} diff --git a/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/lumberjack.go b/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/lumberjack.go new file mode 100644 index 00000000000..2e5d28302ca --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/lumberjack.go @@ -0,0 +1,415 @@ +// Package lumberjack provides a rolling logger. +// +// Note that this is v2.0 of lumberjack, and should be imported using gopkg.in +// thusly: +// +// import "gopkg.in/natefinch/lumberjack.v2" +// +// The package name remains simply lumberjack, and the code resides at +// https://github.com/natefinch/lumberjack under the v2.0 branch. +// +// Lumberjack is intended to be one part of a logging infrastructure. +// It is not an all-in-one solution, but instead is a pluggable +// component at the bottom of the logging stack that simply controls the files +// to which logs are written. +// +// Lumberjack plays well with any logging package that can write to an +// io.Writer, including the standard library's log package. +// +// Lumberjack assumes that only one process is writing to the output files. +// Using the same lumberjack configuration from multiple processes on the same +// machine will result in improper behavior. +package lumberjack + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + "sync" + "time" +) + +const ( + backupTimeFormat = "2006-01-02T15-04-05.000" + defaultMaxSize = 100 +) + +// ensure we always implement io.WriteCloser +var _ io.WriteCloser = (*Logger)(nil) + +// Logger is an io.WriteCloser that writes to the specified filename. +// +// Logger opens or creates the logfile on first Write. If the file exists and +// is less than MaxSize megabytes, lumberjack will open and append to that file. +// If the file exists and its size is >= MaxSize megabytes, the file is renamed +// by putting the current time in a timestamp in the name immediately before the +// file's extension (or the end of the filename if there's no extension). A new +// log file is then created using original filename. +// +// Whenever a write would cause the current log file exceed MaxSize megabytes, +// the current file is closed, renamed, and a new log file created with the +// original name. Thus, the filename you give Logger is always the "current" log +// file. +// +// Cleaning Up Old Log Files +// +// Whenever a new logfile gets created, old log files may be deleted. The most +// recent files according to the encoded timestamp will be retained, up to a +// number equal to MaxBackups (or all of them if MaxBackups is 0). Any files +// with an encoded timestamp older than MaxAge days are deleted, regardless of +// MaxBackups. Note that the time encoded in the timestamp is the rotation +// time, which may differ from the last time that file was written to. +// +// If MaxBackups and MaxAge are both 0, no old log files will be deleted. +type Logger struct { + // Filename is the file to write logs to. Backup log files will be retained + // in the same directory. It uses -lumberjack.log in + // os.TempDir() if empty. + Filename string `json:"filename" yaml:"filename"` + + // MaxSize is the maximum size in megabytes of the log file before it gets + // rotated. It defaults to 100 megabytes. + MaxSize int `json:"maxsize" yaml:"maxsize"` + + // MaxAge is the maximum number of days to retain old log files based on the + // timestamp encoded in their filename. Note that a day is defined as 24 + // hours and may not exactly correspond to calendar days due to daylight + // savings, leap seconds, etc. The default is not to remove old log files + // based on age. + MaxAge int `json:"maxage" yaml:"maxage"` + + // MaxBackups is the maximum number of old log files to retain. The default + // is to retain all old log files (though MaxAge may still cause them to get + // deleted.) + MaxBackups int `json:"maxbackups" yaml:"maxbackups"` + + // LocalTime determines if the time used for formatting the timestamps in + // backup files is the computer's local time. The default is to use UTC + // time. + LocalTime bool `json:"localtime" yaml:"localtime"` + + size int64 + file *os.File + mu sync.Mutex +} + +var ( + // currentTime exists so it can be mocked out by tests. + currentTime = time.Now + + // os_Stat exists so it can be mocked out by tests. + os_Stat = os.Stat + + // megabyte is the conversion factor between MaxSize and bytes. It is a + // variable so tests can mock it out and not need to write megabytes of data + // to disk. + megabyte = 1024 * 1024 +) + +// Write implements io.Writer. If a write would cause the log file to be larger +// than MaxSize, the file is closed, renamed to include a timestamp of the +// current time, and a new log file is created using the original log file name. +// If the length of the write is greater than MaxSize, an error is returned. +func (l *Logger) Write(p []byte) (n int, err error) { + l.mu.Lock() + defer l.mu.Unlock() + + writeLen := int64(len(p)) + if writeLen > l.max() { + return 0, fmt.Errorf( + "write length %d exceeds maximum file size %d", writeLen, l.max(), + ) + } + + if l.file == nil { + if err = l.openExistingOrNew(len(p)); err != nil { + return 0, err + } + } + + if l.size+writeLen > l.max() { + if err := l.rotate(); err != nil { + return 0, err + } + } + + n, err = l.file.Write(p) + l.size += int64(n) + + return n, err +} + +// Close implements io.Closer, and closes the current logfile. +func (l *Logger) Close() error { + l.mu.Lock() + defer l.mu.Unlock() + return l.close() +} + +// close closes the file if it is open. +func (l *Logger) close() error { + if l.file == nil { + return nil + } + err := l.file.Close() + l.file = nil + return err +} + +// Rotate causes Logger to close the existing log file and immediately create a +// new one. This is a helper function for applications that want to initiate +// rotations outside of the normal rotation rules, such as in response to +// SIGHUP. After rotating, this initiates a cleanup of old log files according +// to the normal rules. +func (l *Logger) Rotate() error { + l.mu.Lock() + defer l.mu.Unlock() + return l.rotate() +} + +// rotate closes the current file, moves it aside with a timestamp in the name, +// (if it exists), opens a new file with the original filename, and then runs +// cleanup. +func (l *Logger) rotate() error { + if err := l.close(); err != nil { + return err + } + + if err := l.openNew(); err != nil { + return err + } + return l.cleanup() +} + +// openNew opens a new log file for writing, moving any old log file out of the +// way. This methods assumes the file has already been closed. +func (l *Logger) openNew() error { + err := os.MkdirAll(l.dir(), 0744) + if err != nil { + return fmt.Errorf("can't make directories for new logfile: %s", err) + } + + name := l.filename() + mode := os.FileMode(0644) + info, err := os_Stat(name) + if err == nil { + // Copy the mode off the old logfile. + mode = info.Mode() + // move the existing file + newname := backupName(name, l.LocalTime) + if err := os.Rename(name, newname); err != nil { + return fmt.Errorf("can't rename log file: %s", err) + } + + // this is a no-op anywhere but linux + if err := chown(name, info); err != nil { + return err + } + } + + // we use truncate here because this should only get called when we've moved + // the file ourselves. if someone else creates the file in the meantime, + // just wipe out the contents. + f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode) + if err != nil { + return fmt.Errorf("can't open new logfile: %s", err) + } + l.file = f + l.size = 0 + return nil +} + +// backupName creates a new filename from the given name, inserting a timestamp +// between the filename and the extension, using the local time if requested +// (otherwise UTC). +func backupName(name string, local bool) string { + dir := filepath.Dir(name) + filename := filepath.Base(name) + ext := filepath.Ext(filename) + prefix := filename[:len(filename)-len(ext)] + t := currentTime() + if !local { + t = t.UTC() + } + + timestamp := t.Format(backupTimeFormat) + return filepath.Join(dir, fmt.Sprintf("%s-%s%s", prefix, timestamp, ext)) +} + +// openExistingOrNew opens the logfile if it exists and if the current write +// would not put it over MaxSize. If there is no such file or the write would +// put it over the MaxSize, a new file is created. +func (l *Logger) openExistingOrNew(writeLen int) error { + filename := l.filename() + info, err := os_Stat(filename) + if os.IsNotExist(err) { + return l.openNew() + } + if err != nil { + return fmt.Errorf("error getting log file info: %s", err) + } + // the first file we find that matches our pattern will be the most + // recently modified log file. + if info.Size()+int64(writeLen) < l.max() { + file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0644) + if err == nil { + l.file = file + l.size = info.Size() + return nil + } + // if we fail to open the old log file for some reason, just ignore + // it and open a new log file. + } + return l.openNew() +} + +// genFilename generates the name of the logfile from the current time. +func (l *Logger) filename() string { + if l.Filename != "" { + return l.Filename + } + name := filepath.Base(os.Args[0]) + "-lumberjack.log" + return filepath.Join(os.TempDir(), name) +} + +// cleanup deletes old log files, keeping at most l.MaxBackups files, as long as +// none of them are older than MaxAge. +func (l *Logger) cleanup() error { + if l.MaxBackups == 0 && l.MaxAge == 0 { + return nil + } + + files, err := l.oldLogFiles() + if err != nil { + return err + } + + var deletes []logInfo + + if l.MaxBackups > 0 && l.MaxBackups < len(files) { + deletes = files[l.MaxBackups:] + files = files[:l.MaxBackups] + } + if l.MaxAge > 0 { + diff := time.Duration(int64(24*time.Hour) * int64(l.MaxAge)) + + cutoff := currentTime().Add(-1 * diff) + + for _, f := range files { + if f.timestamp.Before(cutoff) { + deletes = append(deletes, f) + } + } + } + + if len(deletes) == 0 { + return nil + } + + go deleteAll(l.dir(), deletes) + + return nil +} + +func deleteAll(dir string, files []logInfo) { + // remove files on a separate goroutine + for _, f := range files { + // what am I going to do, log this? + _ = os.Remove(filepath.Join(dir, f.Name())) + } +} + +// oldLogFiles returns the list of backup log files stored in the same +// directory as the current log file, sorted by ModTime +func (l *Logger) oldLogFiles() ([]logInfo, error) { + files, err := ioutil.ReadDir(l.dir()) + if err != nil { + return nil, fmt.Errorf("can't read log file directory: %s", err) + } + logFiles := []logInfo{} + + prefix, ext := l.prefixAndExt() + + for _, f := range files { + if f.IsDir() { + continue + } + + name := l.timeFromName(f.Name(), prefix, ext) + if name == "" { + continue + } + t, err := time.Parse(backupTimeFormat, name) + if err == nil { + logFiles = append(logFiles, logInfo{t, f}) + } + } + + sort.Sort(byFormatTime(logFiles)) + + return logFiles, nil +} + +// timeFromName extracts the formatted time from the filename by stripping off +// the filename's prefix and extension. This prevents someone's filename from +// confusing time.parse. +func (l *Logger) timeFromName(filename, prefix, ext string) string { + if !strings.HasPrefix(filename, prefix) { + return "" + } + filename = filename[len(prefix):] + + if !strings.HasSuffix(filename, ext) { + return "" + } + filename = filename[:len(filename)-len(ext)] + return filename +} + +// max returns the maximum size in bytes of log files before rolling. +func (l *Logger) max() int64 { + if l.MaxSize == 0 { + return int64(defaultMaxSize * megabyte) + } + return int64(l.MaxSize) * int64(megabyte) +} + +// dir returns the directory for the current filename. +func (l *Logger) dir() string { + return filepath.Dir(l.filename()) +} + +// prefixAndExt returns the filename part and extension part from the Logger's +// filename. +func (l *Logger) prefixAndExt() (prefix, ext string) { + filename := filepath.Base(l.filename()) + ext = filepath.Ext(filename) + prefix = filename[:len(filename)-len(ext)] + "-" + return prefix, ext +} + +// logInfo is a convenience struct to return the filename and its embedded +// timestamp. +type logInfo struct { + timestamp time.Time + os.FileInfo +} + +// byFormatTime sorts by newest time formatted in the name. +type byFormatTime []logInfo + +func (b byFormatTime) Less(i, j int) bool { + return b[i].timestamp.After(b[j].timestamp) +} + +func (b byFormatTime) Swap(i, j int) { + b[i], b[j] = b[j], b[i] +} + +func (b byFormatTime) Len() int { + return len(b) +} diff --git a/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/lumberjack_test.go b/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/lumberjack_test.go new file mode 100644 index 00000000000..e9984f807ce --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/lumberjack_test.go @@ -0,0 +1,634 @@ +package lumberjack + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "testing" + "time" + + "github.com/BurntSushi/toml" + "gopkg.in/yaml.v1" +) + +// !!!NOTE!!! +// +// Running these tests in parallel will almost certainly cause sporadic (or even +// regular) failures, because they're all messing with the same global variable +// that controls the logic's mocked time.Now. So... don't do that. + +// Since all the tests uses the time to determine filenames etc, we need to +// control the wall clock as much as possible, which means having a wall clock +// that doesn't change unless we want it to. +var fakeCurrentTime = time.Now() + +func fakeTime() time.Time { + return fakeCurrentTime +} + +func TestNewFile(t *testing.T) { + currentTime = fakeTime + + dir := makeTempDir("TestNewFile", t) + defer os.RemoveAll(dir) + l := &Logger{ + Filename: logFile(dir), + } + defer l.Close() + b := []byte("boo!") + n, err := l.Write(b) + isNil(err, t) + equals(len(b), n, t) + existsWithLen(logFile(dir), n, t) + fileCount(dir, 1, t) +} + +func TestOpenExisting(t *testing.T) { + currentTime = fakeTime + dir := makeTempDir("TestOpenExisting", t) + defer os.RemoveAll(dir) + + filename := logFile(dir) + data := []byte("foo!") + err := ioutil.WriteFile(filename, data, 0644) + isNil(err, t) + existsWithLen(filename, len(data), t) + + l := &Logger{ + Filename: filename, + } + defer l.Close() + b := []byte("boo!") + n, err := l.Write(b) + isNil(err, t) + equals(len(b), n, t) + + // make sure the file got appended + existsWithLen(filename, len(data)+n, t) + + // make sure no other files were created + fileCount(dir, 1, t) +} + +func TestWriteTooLong(t *testing.T) { + currentTime = fakeTime + megabyte = 1 + dir := makeTempDir("TestWriteTooLong", t) + defer os.RemoveAll(dir) + l := &Logger{ + Filename: logFile(dir), + MaxSize: 5, + } + defer l.Close() + b := []byte("booooooooooooooo!") + n, err := l.Write(b) + notNil(err, t) + equals(0, n, t) + equals(err.Error(), + fmt.Sprintf("write length %d exceeds maximum file size %d", len(b), l.MaxSize), t) + _, err = os.Stat(logFile(dir)) + assert(os.IsNotExist(err), t, "File exists, but should not have been created") +} + +func TestMakeLogDir(t *testing.T) { + currentTime = fakeTime + dir := time.Now().Format("TestMakeLogDir" + backupTimeFormat) + dir = filepath.Join(os.TempDir(), dir) + defer os.RemoveAll(dir) + filename := logFile(dir) + l := &Logger{ + Filename: filename, + } + defer l.Close() + b := []byte("boo!") + n, err := l.Write(b) + isNil(err, t) + equals(len(b), n, t) + existsWithLen(logFile(dir), n, t) + fileCount(dir, 1, t) +} + +func TestDefaultFilename(t *testing.T) { + currentTime = fakeTime + dir := os.TempDir() + filename := filepath.Join(dir, filepath.Base(os.Args[0])+"-lumberjack.log") + defer os.Remove(filename) + l := &Logger{} + defer l.Close() + b := []byte("boo!") + n, err := l.Write(b) + + isNil(err, t) + equals(len(b), n, t) + existsWithLen(filename, n, t) +} + +func TestAutoRotate(t *testing.T) { + currentTime = fakeTime + megabyte = 1 + + dir := makeTempDir("TestAutoRotate", t) + defer os.RemoveAll(dir) + + filename := logFile(dir) + l := &Logger{ + Filename: filename, + MaxSize: 10, + } + defer l.Close() + b := []byte("boo!") + n, err := l.Write(b) + isNil(err, t) + equals(len(b), n, t) + + existsWithLen(filename, n, t) + fileCount(dir, 1, t) + + newFakeTime() + + b2 := []byte("foooooo!") + n, err = l.Write(b2) + isNil(err, t) + equals(len(b2), n, t) + + // the old logfile should be moved aside and the main logfile should have + // only the last write in it. + existsWithLen(filename, n, t) + + // the backup file will use the current fake time and have the old contents. + existsWithLen(backupFile(dir), len(b), t) + + fileCount(dir, 2, t) +} + +func TestFirstWriteRotate(t *testing.T) { + currentTime = fakeTime + megabyte = 1 + dir := makeTempDir("TestFirstWriteRotate", t) + defer os.RemoveAll(dir) + + filename := logFile(dir) + l := &Logger{ + Filename: filename, + MaxSize: 10, + } + defer l.Close() + + start := []byte("boooooo!") + err := ioutil.WriteFile(filename, start, 0600) + isNil(err, t) + + newFakeTime() + + // this would make us rotate + b := []byte("fooo!") + n, err := l.Write(b) + isNil(err, t) + equals(len(b), n, t) + + existsWithLen(filename, n, t) + existsWithLen(backupFile(dir), len(start), t) + + fileCount(dir, 2, t) +} + +func TestMaxBackups(t *testing.T) { + currentTime = fakeTime + megabyte = 1 + dir := makeTempDir("TestMaxBackups", t) + defer os.RemoveAll(dir) + + filename := logFile(dir) + l := &Logger{ + Filename: filename, + MaxSize: 10, + MaxBackups: 1, + } + defer l.Close() + b := []byte("boo!") + n, err := l.Write(b) + isNil(err, t) + equals(len(b), n, t) + + existsWithLen(filename, n, t) + fileCount(dir, 1, t) + + newFakeTime() + + // this will put us over the max + b2 := []byte("foooooo!") + n, err = l.Write(b2) + isNil(err, t) + equals(len(b2), n, t) + + // this will use the new fake time + secondFilename := backupFile(dir) + existsWithLen(secondFilename, len(b), t) + + // make sure the old file still exists with the same size. + existsWithLen(filename, n, t) + + fileCount(dir, 2, t) + + newFakeTime() + + // this will make us rotate again + n, err = l.Write(b2) + isNil(err, t) + equals(len(b2), n, t) + + // this will use the new fake time + thirdFilename := backupFile(dir) + existsWithLen(thirdFilename, len(b2), t) + + existsWithLen(filename, n, t) + + // we need to wait a little bit since the files get deleted on a different + // goroutine. + <-time.After(time.Millisecond * 10) + + // should only have two files in the dir still + fileCount(dir, 2, t) + + // second file name should still exist + existsWithLen(thirdFilename, len(b2), t) + + // should have deleted the first backup + notExist(secondFilename, t) + + // now test that we don't delete directories or non-logfile files + + newFakeTime() + + // create a file that is close to but different from the logfile name. + // It shouldn't get caught by our deletion filters. + notlogfile := logFile(dir) + ".foo" + err = ioutil.WriteFile(notlogfile, []byte("data"), 0644) + isNil(err, t) + + // Make a directory that exactly matches our log file filters... it still + // shouldn't get caught by the deletion filter since it's a directory. + notlogfiledir := backupFile(dir) + err = os.Mkdir(notlogfiledir, 0700) + isNil(err, t) + + newFakeTime() + + // this will make us rotate again + n, err = l.Write(b2) + isNil(err, t) + equals(len(b2), n, t) + + // this will use the new fake time + fourthFilename := backupFile(dir) + existsWithLen(fourthFilename, len(b2), t) + + // we need to wait a little bit since the files get deleted on a different + // goroutine. + <-time.After(time.Millisecond * 10) + + // We should have four things in the directory now - the 2 log files, the + // not log file, and the directory + fileCount(dir, 4, t) + + // third file name should still exist + existsWithLen(filename, n, t) + + existsWithLen(fourthFilename, len(b2), t) + + // should have deleted the first filename + notExist(thirdFilename, t) + + // the not-a-logfile should still exist + exists(notlogfile, t) + + // the directory + exists(notlogfiledir, t) +} + +func TestMaxAge(t *testing.T) { + currentTime = fakeTime + megabyte = 1 + + dir := makeTempDir("TestMaxAge", t) + defer os.RemoveAll(dir) + + filename := logFile(dir) + l := &Logger{ + Filename: filename, + MaxSize: 10, + MaxAge: 1, + } + defer l.Close() + b := []byte("boo!") + n, err := l.Write(b) + isNil(err, t) + equals(len(b), n, t) + + existsWithLen(filename, n, t) + fileCount(dir, 1, t) + + // two days later + newFakeTime() + + b2 := []byte("foooooo!") + n, err = l.Write(b2) + isNil(err, t) + equals(len(b2), n, t) + existsWithLen(backupFile(dir), len(b), t) + + // we need to wait a little bit since the files get deleted on a different + // goroutine. + <-time.After(10 * time.Millisecond) + + // We should still have 2 log files, since the most recent backup was just + // created. + fileCount(dir, 2, t) + + existsWithLen(filename, len(b2), t) + + // we should have deleted the old file due to being too old + existsWithLen(backupFile(dir), len(b), t) + + // two days later + newFakeTime() + + b3 := []byte("foooooo!") + n, err = l.Write(b2) + isNil(err, t) + equals(len(b3), n, t) + existsWithLen(backupFile(dir), len(b2), t) + + // we need to wait a little bit since the files get deleted on a different + // goroutine. + <-time.After(10 * time.Millisecond) + + // We should have 2 log files - the main log file, and the most recent + // backup. The earlier backup is past the cutoff and should be gone. + fileCount(dir, 2, t) + + existsWithLen(filename, len(b3), t) + + // we should have deleted the old file due to being too old + existsWithLen(backupFile(dir), len(b2), t) + +} + +func TestOldLogFiles(t *testing.T) { + currentTime = fakeTime + megabyte = 1 + + dir := makeTempDir("TestOldLogFiles", t) + defer os.RemoveAll(dir) + + filename := logFile(dir) + data := []byte("data") + err := ioutil.WriteFile(filename, data, 07) + isNil(err, t) + + // This gives us a time with the same precision as the time we get from the + // timestamp in the name. + t1, err := time.Parse(backupTimeFormat, fakeTime().UTC().Format(backupTimeFormat)) + isNil(err, t) + + backup := backupFile(dir) + err = ioutil.WriteFile(backup, data, 07) + isNil(err, t) + + newFakeTime() + + t2, err := time.Parse(backupTimeFormat, fakeTime().UTC().Format(backupTimeFormat)) + isNil(err, t) + + backup2 := backupFile(dir) + err = ioutil.WriteFile(backup2, data, 07) + isNil(err, t) + + l := &Logger{Filename: filename} + files, err := l.oldLogFiles() + isNil(err, t) + equals(2, len(files), t) + + // should be sorted by newest file first, which would be t2 + equals(t2, files[0].timestamp, t) + equals(t1, files[1].timestamp, t) +} + +func TestTimeFromName(t *testing.T) { + l := &Logger{Filename: "/var/log/myfoo/foo.log"} + prefix, ext := l.prefixAndExt() + val := l.timeFromName("foo-2014-05-04T14-44-33.555.log", prefix, ext) + equals("2014-05-04T14-44-33.555", val, t) + + val = l.timeFromName("foo-2014-05-04T14-44-33.555", prefix, ext) + equals("", val, t) + + val = l.timeFromName("2014-05-04T14-44-33.555.log", prefix, ext) + equals("", val, t) + + val = l.timeFromName("foo.log", prefix, ext) + equals("", val, t) +} + +func TestLocalTime(t *testing.T) { + currentTime = fakeTime + megabyte = 1 + + dir := makeTempDir("TestLocalTime", t) + defer os.RemoveAll(dir) + + l := &Logger{ + Filename: logFile(dir), + MaxSize: 10, + LocalTime: true, + } + defer l.Close() + b := []byte("boo!") + n, err := l.Write(b) + isNil(err, t) + equals(len(b), n, t) + + b2 := []byte("fooooooo!") + n2, err := l.Write(b2) + isNil(err, t) + equals(len(b2), n2, t) + + existsWithLen(logFile(dir), n2, t) + existsWithLen(backupFileLocal(dir), n, t) +} + +func TestRotate(t *testing.T) { + currentTime = fakeTime + dir := makeTempDir("TestRotate", t) + defer os.RemoveAll(dir) + + filename := logFile(dir) + + l := &Logger{ + Filename: filename, + MaxBackups: 1, + MaxSize: 100, // megabytes + } + defer l.Close() + b := []byte("boo!") + n, err := l.Write(b) + isNil(err, t) + equals(len(b), n, t) + + existsWithLen(filename, n, t) + fileCount(dir, 1, t) + + newFakeTime() + + err = l.Rotate() + isNil(err, t) + + // we need to wait a little bit since the files get deleted on a different + // goroutine. + <-time.After(10 * time.Millisecond) + + filename2 := backupFile(dir) + existsWithLen(filename2, n, t) + existsWithLen(filename, 0, t) + fileCount(dir, 2, t) + newFakeTime() + + err = l.Rotate() + isNil(err, t) + + // we need to wait a little bit since the files get deleted on a different + // goroutine. + <-time.After(10 * time.Millisecond) + + filename3 := backupFile(dir) + existsWithLen(filename3, 0, t) + existsWithLen(filename, 0, t) + fileCount(dir, 2, t) + + b2 := []byte("foooooo!") + n, err = l.Write(b2) + isNil(err, t) + equals(len(b2), n, t) + + // this will use the new fake time + existsWithLen(filename, n, t) +} + +func TestJson(t *testing.T) { + data := []byte(` +{ + "filename": "foo", + "maxsize": 5, + "maxage": 10, + "maxbackups": 3, + "localtime": true +}`[1:]) + + l := Logger{} + err := json.Unmarshal(data, &l) + isNil(err, t) + equals("foo", l.Filename, t) + equals(5, l.MaxSize, t) + equals(10, l.MaxAge, t) + equals(3, l.MaxBackups, t) + equals(true, l.LocalTime, t) +} + +func TestYaml(t *testing.T) { + data := []byte(` +filename: foo +maxsize: 5 +maxage: 10 +maxbackups: 3 +localtime: true`[1:]) + + l := Logger{} + err := yaml.Unmarshal(data, &l) + isNil(err, t) + equals("foo", l.Filename, t) + equals(5, l.MaxSize, t) + equals(10, l.MaxAge, t) + equals(3, l.MaxBackups, t) + equals(true, l.LocalTime, t) +} + +func TestToml(t *testing.T) { + data := ` +filename = "foo" +maxsize = 5 +maxage = 10 +maxbackups = 3 +localtime = true`[1:] + + l := Logger{} + md, err := toml.Decode(data, &l) + isNil(err, t) + equals("foo", l.Filename, t) + equals(5, l.MaxSize, t) + equals(10, l.MaxAge, t) + equals(3, l.MaxBackups, t) + equals(true, l.LocalTime, t) + equals(0, len(md.Undecoded()), t) +} + +// makeTempDir creates a file with a semi-unique name in the OS temp directory. +// It should be based on the name of the test, to keep parallel tests from +// colliding, and must be cleaned up after the test is finished. +func makeTempDir(name string, t testing.TB) string { + dir := time.Now().Format(name + backupTimeFormat) + dir = filepath.Join(os.TempDir(), dir) + isNilUp(os.Mkdir(dir, 0777), t, 1) + return dir +} + +// existsWithLen checks that the given file exists and has the correct length. +func existsWithLen(path string, length int, t testing.TB) { + info, err := os.Stat(path) + isNilUp(err, t, 1) + equalsUp(int64(length), info.Size(), t, 1) +} + +// logFile returns the log file name in the given directory for the current fake +// time. +func logFile(dir string) string { + return filepath.Join(dir, "foobar.log") +} + +func backupFile(dir string) string { + return filepath.Join(dir, "foobar-"+fakeTime().UTC().Format(backupTimeFormat)+".log") +} + +func backupFileLocal(dir string) string { + return filepath.Join(dir, "foobar-"+fakeTime().Format(backupTimeFormat)+".log") +} + +// logFileLocal returns the log file name in the given directory for the current +// fake time using the local timezone. +func logFileLocal(dir string) string { + return filepath.Join(dir, fakeTime().Format(backupTimeFormat)) +} + +// fileCount checks that the number of files in the directory is exp. +func fileCount(dir string, exp int, t testing.TB) { + files, err := ioutil.ReadDir(dir) + isNilUp(err, t, 1) + // Make sure no other files were created. + equalsUp(exp, len(files), t, 1) +} + +// newFakeTime sets the fake "current time" to two days later. +func newFakeTime() { + fakeCurrentTime = fakeCurrentTime.Add(time.Hour * 24 * 2) +} + +func notExist(path string, t testing.TB) { + _, err := os.Stat(path) + assertUp(os.IsNotExist(err), t, 1, "expected to get os.IsNotExist, but instead got %v", err) +} + +func exists(path string, t testing.TB) { + _, err := os.Stat(path) + assertUp(err == nil, t, 1, "expected file to exist, but got error from os.Stat: %v", err) +} diff --git a/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/rotate_test.go b/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/rotate_test.go new file mode 100644 index 00000000000..0561464ac0d --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/rotate_test.go @@ -0,0 +1,27 @@ +// +build linux + +package lumberjack_test + +import ( + "log" + "os" + "os/signal" + "syscall" + + "github.com/natefinch/lumberjack" +) + +// Example of how to rotate in response to SIGHUP. +func ExampleLogger_Rotate() { + l := &lumberjack.Logger{} + log.SetOutput(l) + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGHUP) + + go func() { + for { + <-c + l.Rotate() + } + }() +} diff --git a/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/testing_test.go b/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/testing_test.go new file mode 100644 index 00000000000..8e89c083198 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/testing_test.go @@ -0,0 +1,91 @@ +package lumberjack + +import ( + "fmt" + "path/filepath" + "reflect" + "runtime" + "testing" +) + +// assert will log the given message if condition is false. +func assert(condition bool, t testing.TB, msg string, v ...interface{}) { + assertUp(condition, t, 1, msg, v...) +} + +// assertUp is like assert, but used inside helper functions, to ensure that +// the file and line number reported by failures corresponds to one or more +// levels up the stack. +func assertUp(condition bool, t testing.TB, caller int, msg string, v ...interface{}) { + if !condition { + _, file, line, _ := runtime.Caller(caller + 1) + v = append([]interface{}{filepath.Base(file), line}, v...) + fmt.Printf("%s:%d: "+msg+"\n", v...) + t.FailNow() + } +} + +// equals tests that the two values are equal according to reflect.DeepEqual. +func equals(exp, act interface{}, t testing.TB) { + equalsUp(exp, act, t, 1) +} + +// equalsUp is like equals, but used inside helper functions, to ensure that the +// file and line number reported by failures corresponds to one or more levels +// up the stack. +func equalsUp(exp, act interface{}, t testing.TB, caller int) { + if !reflect.DeepEqual(exp, act) { + _, file, line, _ := runtime.Caller(caller + 1) + fmt.Printf("%s:%d: exp: %v (%T), got: %v (%T)\n", + filepath.Base(file), line, exp, exp, act, act) + t.FailNow() + } +} + +// isNil reports a failure if the given value is not nil. Note that values +// which cannot be nil will always fail this check. +func isNil(obtained interface{}, t testing.TB) { + isNilUp(obtained, t, 1) +} + +// isNilUp is like isNil, but used inside helper functions, to ensure that the +// file and line number reported by failures corresponds to one or more levels +// up the stack. +func isNilUp(obtained interface{}, t testing.TB, caller int) { + if !_isNil(obtained) { + _, file, line, _ := runtime.Caller(caller + 1) + fmt.Printf("%s:%d: expected nil, got: %v\n", filepath.Base(file), line, obtained) + t.FailNow() + } +} + +// notNil reports a failure if the given value is nil. +func notNil(obtained interface{}, t testing.TB) { + notNilUp(obtained, t, 1) +} + +// notNilUp is like notNil, but used inside helper functions, to ensure that the +// file and line number reported by failures corresponds to one or more levels +// up the stack. +func notNilUp(obtained interface{}, t testing.TB, caller int) { + if _isNil(obtained) { + _, file, line, _ := runtime.Caller(caller + 1) + fmt.Printf("%s:%d: expected non-nil, got: %v\n", filepath.Base(file), line, obtained) + t.FailNow() + } +} + +// _isNil is a helper function for isNil and notNil, and should not be used +// directly. +func _isNil(obtained interface{}) bool { + if obtained == nil { + return true + } + + switch v := reflect.ValueOf(obtained); v.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return v.IsNil() + } + + return false +} From d28c9cc7b44f457e5a490f7b698f2b8ebe5bc90e Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 03:51:36 -0800 Subject: [PATCH 21/42] refactor(init) extract initCheckDir function License: MIT Signed-off-by: Brian Tiger Chow # TYPES # feat # fix # docs # style (formatting, missing semi colons, etc; no code change): # refactor # test (adding missing tests, refactoring tests; no production code change) # chore (updating grunt tasks etc; no production code change) --- cmd/ipfs2/init.go | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index b0b945716a5..84b229da0fc 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -131,16 +131,9 @@ func datastoreConfig(dspath string) (config.Datastore, error) { ds.Path = dspath ds.Type = "leveldb" - // Construct the data store if missing - if err := os.MkdirAll(dspath, os.ModePerm); err != nil { - return ds, err - } - - // Check the directory is writeable - if f, err := os.Create(filepath.Join(dspath, "._check_writeable")); err == nil { - os.Remove(f.Name()) - } else { - return ds, errors.New("Datastore '" + dspath + "' is not writeable") + err := initCheckDir(dspath) + if err != nil { + return ds, errors.Errorf("datastore: %s", err) } return ds, nil @@ -224,3 +217,19 @@ func identityConfig(nbits int) (config.Identity, error) { return ident, nil } + +// initCheckDir ensures the directory exists and is writable +func initCheckDir(path string) error { + // Construct the path if missing + if err := os.MkdirAll(path, os.ModePerm); err != nil { + return err + } + + // Check the directory is writeable + if f, err := os.Create(filepath.Join(path, "._check_writeable")); err == nil { + os.Remove(f.Name()) + } else { + return errors.New("'" + path + "' is not writeable") + } + return nil +} From bd51b8216075fc9639d54d6a18d6248e05b4f131 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 03:52:09 -0800 Subject: [PATCH 22/42] feat(eventlog) configuration/options License: MIT Signed-off-by: Brian Tiger Chow # TYPES # feat # fix # docs # style (formatting, missing semi colons, etc; no code change): # refactor # test (adding missing tests, refactoring tests; no production code change) # chore (updating grunt tasks etc; no production code change) --- util/elog/option.go | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/util/elog/option.go b/util/elog/option.go index 876c400ca48..a25a1d06c91 100644 --- a/util/elog/option.go +++ b/util/elog/option.go @@ -9,11 +9,14 @@ import ( type Option func() -func SetOption(o Option) { - o() +func Configure(options ...Option) { + for _, f := range options { + f() + } } -var JSONFormatter = func() { +// LdJSONFormatter formats the event log as line-delimited JSON +var LdJSONFormatter = func() { logrus.SetFormatter(&logrus.JSONFormatter{}) } @@ -22,7 +25,7 @@ var TextFormatter = func() { } type LogRotatorConfig struct { - File string + Filename string MaxSizeMB uint64 MaxBackups uint64 MaxAgeDays uint64 @@ -39,7 +42,7 @@ func OutputRotatingLogFile(config LogRotatorConfig) Option { return func() { logrus.SetOutput( &lumberjack.Logger{ - Filename: config.File, + Filename: config.Filename, MaxSize: int(config.MaxSizeMB), MaxBackups: int(config.MaxBackups), MaxAge: int(config.MaxAgeDays), @@ -47,4 +50,14 @@ func OutputRotatingLogFile(config LogRotatorConfig) Option { } } -// TODO log levels? logrus.SetLevel(logrus.DebugLevel) +var LevelDebug = func() { + logrus.SetLevel(logrus.DebugLevel) +} + +var LevelError = func() { + logrus.SetLevel(logrus.ErrorLevel) +} + +var LevelInfo = func() { + logrus.SetLevel(logrus.InfoLevel) +} From 183dd990b69525d4d8a0bcbec053b0ba406e0d95 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 03:55:23 -0800 Subject: [PATCH 23/42] feat(eventlog) include timestamp in log License: MIT Signed-off-by: Brian Tiger Chow # TYPES # feat # fix # docs # style (formatting, missing semi colons, etc; no code change): # refactor # test (adding missing tests, refactoring tests; no production code change) # chore (updating grunt tasks etc; no production code change) --- util/elog/log.go | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/util/elog/log.go b/util/elog/log.go index 84a4a7dd58a..78a47d8dcac 100644 --- a/util/elog/log.go +++ b/util/elog/log.go @@ -1,7 +1,7 @@ package elog import ( - "os" + "time" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus" @@ -73,14 +73,7 @@ func (el *eventLogger) Event(ctx context.Context, event string, metadata ...Logg } accum["event"] = event accum["system"] = el.system + accum["time"] = util.FormatRFC3339(time.Now()) logrus.WithFields(map[string]interface{}(accum)).Info(event) } - -func init() { - // TODO use both text formatter and json formatter - // logrus.SetFormatter(new(logrus.TextFormatter)) - logrus.SetFormatter(new(logrus.JSONFormatter)) - logrus.SetOutput(os.Stderr) - logrus.SetLevel(logrus.DebugLevel) -} From 3a927cbcec96952145da8f7d630a91f965948ed1 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 03:56:10 -0800 Subject: [PATCH 24/42] feat(init) init logs upon config initialization TODO allow user dir override License: MIT Signed-off-by: Brian Tiger Chow # TYPES # feat # fix # docs # style (formatting, missing semi colons, etc; no code change): # refactor # test (adding missing tests, refactoring tests; no production code change) # chore (updating grunt tasks etc; no production code change) Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/init.go | 36 +++++++++++++++++++++++++++++++++++- config/config.go | 18 ++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index 84b229da0fc..cd589cc6e64 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -3,9 +3,9 @@ package main import ( "bytes" "encoding/base64" - "errors" "fmt" "os" + "path" "path/filepath" cmds "github.com/jbenet/go-ipfs/commands" @@ -16,6 +16,7 @@ import ( chunk "github.com/jbenet/go-ipfs/importer/chunk" peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" + errors "github.com/jbenet/go-ipfs/util/debugerror" ) var initCmd = &cmds.Command{ @@ -29,6 +30,11 @@ var initCmd = &cmds.Command{ cmds.StringOption("passphrase", "p", "Passphrase for encrypting the private key"), cmds.BoolOption("force", "f", "Overwrite existing config (if it exists)"), cmds.StringOption("datastore", "d", "Location for the IPFS data store"), + + // TODO need to decide whether to expose the override as a file or a + // directory. That is: should we allow the user to also specify the + // name of the file? + // TODO cmds.StringOption("event-logs", "l", "Location for machine-readable event logs"), }, Run: func(req cmds.Request) (interface{}, error) { @@ -97,6 +103,7 @@ func doInit(configRoot string, dspathOverride string, force bool, nBitsForKeypai return nil, err } + // TODO extract this file creation operation into a function nd, err := core.NewIpfsNode(conf, false) if err != nil { return nil, err @@ -150,6 +157,11 @@ func initConfig(configFilename string, dspathOverride string, nBitsForKeypair in return nil, err } + logConfig, err := initLogs("") // TODO allow user to override dir + if err != nil { + return nil, err + } + conf := &config.Config{ // setup the node addresses. @@ -168,6 +180,8 @@ func initConfig(configFilename string, dspathOverride string, nBitsForKeypair in Datastore: ds, + Logs: logConfig, + Identity: identity, // setup the node mount points. @@ -214,10 +228,30 @@ func identityConfig(nbits int) (config.Identity, error) { return ident, err } ident.PeerID = id.Pretty() + fmt.Println("generating key pair...") return ident, nil } +func initLogs(logpath string) (config.Logs, error) { + if len(logpath) == 0 { + var err error + logpath, err = config.LogsPath("") + if err != nil { + return config.Logs{}, errors.Wrap(err) + } + } + + err := initCheckDir(logpath) + if err != nil { + return config.Logs{}, errors.Errorf("logs: %s", err) + } + + return config.Logs{ + Filename: path.Join(logpath, "events.log"), + }, nil +} + // initCheckDir ensures the directory exists and is writable func initCheckDir(path string) error { // Construct the path if missing diff --git a/config/config.go b/config/config.go index c409f68388a..74a25db9b44 100644 --- a/config/config.go +++ b/config/config.go @@ -20,6 +20,14 @@ type Identity struct { PrivKey string } +// Logs tracks the configuration of the event logger +type Logs struct { + Filename string + MaxSizeMB uint64 + MaxBackups uint64 + MaxAgeDays uint64 +} + // Datastore tracks the configuration of the datastore. type Datastore struct { Type string @@ -63,6 +71,7 @@ type Config struct { Version Version // local node's version management Bootstrap []*BootstrapPeer // local nodes's bootstrap peers Tour Tour // local node's tour position + Logs Logs // local node's event log configuration } // DefaultPathRoot is the path to the default config dir location. @@ -77,6 +86,9 @@ const DefaultDataStoreDirectory = "datastore" // EnvDir is the environment variable used to change the path root. const EnvDir = "IPFS_DIR" +// LogsDefaultDirectory is the directory to store all IPFS event logs. +var LogsDefaultDirectory = "logs" + // PathRoot returns the default configuration root directory func PathRoot() (string, error) { dir := os.Getenv(EnvDir) @@ -107,6 +119,12 @@ func DataStorePath(configroot string) (string, error) { return Path(configroot, DefaultDataStoreDirectory) } +// LogsPath returns the default path for event logs given a configuration root +// (set an empty string to have the default configuration root) +func LogsPath(configroot string) (string, error) { + return Path(configroot, LogsDefaultDirectory) +} + // Filename returns the configuration file path given a configuration root // directory. If the configuration root directory is empty, use the default one func Filename(configroot string) (string, error) { From be62b765160c8a8df7d10ee1d9cf238020fa14e8 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 03:57:40 -0800 Subject: [PATCH 25/42] refactor(2/main) use debugerror License: MIT Signed-off-by: Brian Tiger Chow # TYPES # feat # fix # docs # style (formatting, missing semi colons, etc; no code change): # refactor # test (adding missing tests, refactoring tests; no production code change) # chore (updating grunt tasks etc; no production code change) Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/main.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 58133c4f05c..ce62a791f00 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -1,7 +1,6 @@ package main import ( - "errors" "fmt" "io" "os" @@ -22,7 +21,7 @@ import ( daemon "github.com/jbenet/go-ipfs/daemon2" updates "github.com/jbenet/go-ipfs/updates" u "github.com/jbenet/go-ipfs/util" - "github.com/jbenet/go-ipfs/util/debugerror" + errors "github.com/jbenet/go-ipfs/util/debugerror" elog "github.com/jbenet/go-ipfs/util/elog" ) @@ -321,7 +320,7 @@ func commandDetails(path []string, root *cmds.Command) (*cmdDetails, error) { var found bool cmd, found = cmd.Subcommands[cmp] if !found { - return nil, debugerror.Errorf("subcommand %s should be in root", cmp) + return nil, errors.Errorf("subcommand %s should be in root", cmp) } if cmdDetails, found := cmdDetailsMap[cmd]; found { From ac69aba56188280bcc09cb4c6188dcee8426d06e Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 03:58:13 -0800 Subject: [PATCH 26/42] feat(2/main) configure event logger License: MIT Signed-off-by: Brian Tiger Chow # TYPES # feat # fix # docs # style (formatting, missing semi colons, etc; no code change): # refactor # test (adding missing tests, refactoring tests; no production code change) # chore (updating grunt tasks etc; no production code change) Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/main.go | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index ce62a791f00..52873dfe109 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -232,6 +232,17 @@ func callPreCommandHooks(command cmdDetails, req cmds.Request, root *cmds.Comman } } + // When the upcoming command may use the config and repo, we know it's safe + // for the log config hook to touch the config/repo + if command.usesConfigAsInput() && command.usesRepo() { + log.Debug("Calling hook: Configure Event Logger") + cfg, err := req.Context().GetConfig() + if err != nil { + return err + } + configureEventLogger(cfg) + } + return nil } @@ -481,3 +492,24 @@ func allInterruptSignals() chan os.Signal { syscall.SIGTERM, syscall.SIGQUIT) return sigc } + +func configureEventLogger(config *config.Config) error { + + if u.Debug { + elog.Configure(elog.LevelDebug) + } else { + elog.Configure(elog.LevelInfo) + } + + elog.Configure(elog.LdJSONFormatter) + + rotateConf := elog.LogRotatorConfig{ + Filename: config.Logs.Filename, + MaxSizeMB: config.Logs.MaxSizeMB, + MaxBackups: config.Logs.MaxBackups, + MaxAgeDays: config.Logs.MaxAgeDays, + } + + elog.Configure(elog.OutputRotatingLogFile(rotateConf)) + return nil +} From 25c98e8e61ba08975d1411dc977c4a0334940ab2 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 04:01:02 -0800 Subject: [PATCH 27/42] refactor(eventlog) elog -> eventlog License: MIT Signed-off-by: Brian Tiger Chow # TYPES # feat # fix # docs # style (formatting, missing semi colons, etc; no code change): # refactor # test (adding missing tests, refactoring tests; no production code change) # chore (updating grunt tasks etc; no production code change) Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/main.go | 14 +++++++------- net/swarm/addrs.go | 4 ++-- net/swarm/swarm.go | 2 +- routing/dht/dht.go | 2 +- util/{elog => eventlog}/context.go | 2 +- util/{elog => eventlog}/context_test.go | 2 +- util/{elog => eventlog}/log.go | 2 +- util/{elog => eventlog}/metadata.go | 2 +- util/{elog => eventlog}/metadata_test.go | 2 +- util/{elog => eventlog}/option.go | 2 +- 10 files changed, 17 insertions(+), 17 deletions(-) rename util/{elog => eventlog}/context.go (97%) rename util/{elog => eventlog}/context_test.go (98%) rename util/{elog => eventlog}/log.go (99%) rename util/{elog => eventlog}/metadata.go (99%) rename util/{elog => eventlog}/metadata_test.go (97%) rename util/{elog => eventlog}/option.go (98%) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 52873dfe109..a900ac34bb9 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -22,11 +22,11 @@ import ( updates "github.com/jbenet/go-ipfs/updates" u "github.com/jbenet/go-ipfs/util" errors "github.com/jbenet/go-ipfs/util/debugerror" - elog "github.com/jbenet/go-ipfs/util/elog" + eventlog "github.com/jbenet/go-ipfs/util/elog" ) // log is the command logger -var log = elog.Logger("cmd/ipfs") +var log = eventlog.Logger("cmd/ipfs") // signal to output help var errHelpRequested = errors.New("Help Requested") @@ -496,20 +496,20 @@ func allInterruptSignals() chan os.Signal { func configureEventLogger(config *config.Config) error { if u.Debug { - elog.Configure(elog.LevelDebug) + eventlog.Configure(eventlog.LevelDebug) } else { - elog.Configure(elog.LevelInfo) + eventlog.Configure(eventlog.LevelInfo) } - elog.Configure(elog.LdJSONFormatter) + eventlog.Configure(eventlog.LdJSONFormatter) - rotateConf := elog.LogRotatorConfig{ + rotateConf := eventlog.LogRotatorConfig{ Filename: config.Logs.Filename, MaxSizeMB: config.Logs.MaxSizeMB, MaxBackups: config.Logs.MaxBackups, MaxAgeDays: config.Logs.MaxAgeDays, } - elog.Configure(elog.OutputRotatingLogFile(rotateConf)) + eventlog.Configure(eventlog.OutputRotatingLogFile(rotateConf)) return nil } diff --git a/net/swarm/addrs.go b/net/swarm/addrs.go index a7627ee925c..614e5198943 100644 --- a/net/swarm/addrs.go +++ b/net/swarm/addrs.go @@ -54,12 +54,12 @@ func resolveUnspecifiedAddresses(unspecifiedAddrs []ma.Multiaddr) ([]ma.Multiadd } } - log.Event(context.TODO(), "interfaceListenAddresses", func() elog.Loggable { + log.Event(context.TODO(), "interfaceListenAddresses", func() eventlog.Loggable { var addrs []string for _, addr := range outputAddrs { addrs = append(addrs, addr.String()) } - return elog.Metadata{"addresses": addrs} + return eventlog.Metadata{"addresses": addrs} }()) log.Info("InterfaceListenAddresses:", outputAddrs) return outputAddrs, nil diff --git a/net/swarm/swarm.go b/net/swarm/swarm.go index 2768f326b6d..dafa4b119e7 100644 --- a/net/swarm/swarm.go +++ b/net/swarm/swarm.go @@ -19,7 +19,7 @@ import ( ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" ) -var log = elog.Logger("swarm") +var log = eventlog.Logger("swarm") // ErrAlreadyOpen signals that a connection to a peer is already open. var ErrAlreadyOpen = errors.New("Error: Connection to this peer already open.") diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 1af4a29bd9e..f042bbd4bca 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -26,7 +26,7 @@ import ( "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" ) -var log = elog.Logger("dht") +var log = eventlog.Logger("dht") const doPinging = false diff --git a/util/elog/context.go b/util/eventlog/context.go similarity index 97% rename from util/elog/context.go rename to util/eventlog/context.go index 92e80ce8909..03e2e3c5f4f 100644 --- a/util/elog/context.go +++ b/util/eventlog/context.go @@ -1,4 +1,4 @@ -package elog +package eventlog import ( "errors" diff --git a/util/elog/context_test.go b/util/eventlog/context_test.go similarity index 98% rename from util/elog/context_test.go rename to util/eventlog/context_test.go index c973a104b3e..1bab6326912 100644 --- a/util/elog/context_test.go +++ b/util/eventlog/context_test.go @@ -1,4 +1,4 @@ -package elog +package eventlog import ( "testing" diff --git a/util/elog/log.go b/util/eventlog/log.go similarity index 99% rename from util/elog/log.go rename to util/eventlog/log.go index 78a47d8dcac..06b8e5444e8 100644 --- a/util/elog/log.go +++ b/util/eventlog/log.go @@ -1,4 +1,4 @@ -package elog +package eventlog import ( "time" diff --git a/util/elog/metadata.go b/util/eventlog/metadata.go similarity index 99% rename from util/elog/metadata.go rename to util/eventlog/metadata.go index a6408d98464..ea3cad6b597 100644 --- a/util/elog/metadata.go +++ b/util/eventlog/metadata.go @@ -1,4 +1,4 @@ -package elog +package eventlog import ( "encoding/json" diff --git a/util/elog/metadata_test.go b/util/eventlog/metadata_test.go similarity index 97% rename from util/elog/metadata_test.go rename to util/eventlog/metadata_test.go index 2abfc6d7ee5..bb07e700a1c 100644 --- a/util/elog/metadata_test.go +++ b/util/eventlog/metadata_test.go @@ -1,4 +1,4 @@ -package elog +package eventlog import "testing" diff --git a/util/elog/option.go b/util/eventlog/option.go similarity index 98% rename from util/elog/option.go rename to util/eventlog/option.go index a25a1d06c91..b278d3e92af 100644 --- a/util/elog/option.go +++ b/util/eventlog/option.go @@ -1,4 +1,4 @@ -package elog +package eventlog import ( "io" From 2134365c486dfd0cbefb2d242c10321c72746212 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 04:18:47 -0800 Subject: [PATCH 28/42] docs(eventlog) License: MIT Signed-off-by: Brian Tiger Chow --- util/eventlog/log.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/util/eventlog/log.go b/util/eventlog/log.go index 06b8e5444e8..46cfc2683b3 100644 --- a/util/eventlog/log.go +++ b/util/eventlog/log.go @@ -63,17 +63,24 @@ type eventLogger struct { } func (el *eventLogger) Event(ctx context.Context, event string, metadata ...Loggable) { + + // get any existing metadata from the context existing, err := MetadataFromContext(ctx) if err != nil { existing = Metadata{} } + + // accumulate metadata accum := existing for _, datum := range metadata { accum = DeepMerge(accum, datum.Loggable()) } + + // apply final attributes to reserved keys accum["event"] = event accum["system"] = el.system accum["time"] = util.FormatRFC3339(time.Now()) + // TODO roll our own event logger logrus.WithFields(map[string]interface{}(accum)).Info(event) } From c6a84884c97fbdb2fecc87bd9680e0f7f4747123 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 04:36:44 -0800 Subject: [PATCH 29/42] todo(eventlog) License: MIT Signed-off-by: Brian Tiger Chow --- util/eventlog/log.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/util/eventlog/log.go b/util/eventlog/log.go index 46cfc2683b3..40b74196ed9 100644 --- a/util/eventlog/log.go +++ b/util/eventlog/log.go @@ -53,6 +53,10 @@ type StandardLogger interface { // Logger retrieves an event logger by name func Logger(system string) EventLogger { + + // TODO if we would like to adjust log levels at run-time. Store this event + // logger in a map (just like the util.Logger impl) + return &eventLogger{system: system, Logger: util.Logger(system)} } From 6fec76d93e7d39b58a1e78e61587f4b66bc880a1 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 04:53:07 -0800 Subject: [PATCH 30/42] fix(imports) misc License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/main.go | 2 +- net/swarm/addrs.go | 2 +- net/swarm/swarm.go | 2 +- routing/dht/dht.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index a900ac34bb9..ca9f9662b5d 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -22,7 +22,7 @@ import ( updates "github.com/jbenet/go-ipfs/updates" u "github.com/jbenet/go-ipfs/util" errors "github.com/jbenet/go-ipfs/util/debugerror" - eventlog "github.com/jbenet/go-ipfs/util/elog" + eventlog "github.com/jbenet/go-ipfs/util/eventlog" ) // log is the command logger diff --git a/net/swarm/addrs.go b/net/swarm/addrs.go index 614e5198943..8d3a287ce22 100644 --- a/net/swarm/addrs.go +++ b/net/swarm/addrs.go @@ -3,7 +3,7 @@ package swarm import ( "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net" - "github.com/jbenet/go-ipfs/util/elog" + "github.com/jbenet/go-ipfs/util/eventlog" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" ) diff --git a/net/swarm/swarm.go b/net/swarm/swarm.go index dafa4b119e7..3fc65390ece 100644 --- a/net/swarm/swarm.go +++ b/net/swarm/swarm.go @@ -13,7 +13,7 @@ import ( peer "github.com/jbenet/go-ipfs/peer" u "github.com/jbenet/go-ipfs/util" ctxc "github.com/jbenet/go-ipfs/util/ctxcloser" - "github.com/jbenet/go-ipfs/util/elog" + "github.com/jbenet/go-ipfs/util/eventlog" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" diff --git a/routing/dht/dht.go b/routing/dht/dht.go index f042bbd4bca..7a61c75f05b 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -18,7 +18,7 @@ import ( kb "github.com/jbenet/go-ipfs/routing/kbucket" u "github.com/jbenet/go-ipfs/util" ctxc "github.com/jbenet/go-ipfs/util/ctxcloser" - "github.com/jbenet/go-ipfs/util/elog" + "github.com/jbenet/go-ipfs/util/eventlog" context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore" From 43f74c82ac112513234edf15860ed9a16366bd1a Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 05:04:04 -0800 Subject: [PATCH 31/42] feat(eventlog) entry License: MIT Signed-off-by: Brian Tiger Chow --- util/eventlog/log.go | 46 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/util/eventlog/log.go b/util/eventlog/log.go index 40b74196ed9..1b622f0f43a 100644 --- a/util/eventlog/log.go +++ b/util/eventlog/log.go @@ -64,27 +64,61 @@ func Logger(system string) EventLogger { type eventLogger struct { *logging.Logger system string + // TODO add log-level } func (el *eventLogger) Event(ctx context.Context, event string, metadata ...Loggable) { + // Collect loggables for later logging + var loggables []Loggable + // get any existing metadata from the context existing, err := MetadataFromContext(ctx) if err != nil { existing = Metadata{} } + loggables = append(loggables, existing) - // accumulate metadata - accum := existing for _, datum := range metadata { - accum = DeepMerge(accum, datum.Loggable()) + loggables = append(loggables, datum) + } + + e := entry{ + loggables: loggables, + system: el.system, + event: event, + } + + e.Log() // TODO replace this when leveled-logs have been implemented +} + +type entry struct { + loggables []Loggable + system string + event string +} + +// Log logs the event unconditionally (regardless of log level) +// TODO add support for leveled-logs once we decide which levels we want +// for our structured logs +func (e *entry) Log() { + e.log() +} + +// log is a private method invoked by the public Log, Info, Error methods +func (e *entry) log() { + // accumulate metadata + accum := Metadata{} + for _, loggable := range e.loggables { + accum = DeepMerge(accum, loggable.Loggable()) } // apply final attributes to reserved keys - accum["event"] = event - accum["system"] = el.system + // TODO accum["level"] = level + accum["event"] = e.event + accum["system"] = e.system accum["time"] = util.FormatRFC3339(time.Now()) // TODO roll our own event logger - logrus.WithFields(map[string]interface{}(accum)).Info(event) + logrus.WithFields(map[string]interface{}(accum)).Info(e.event) } From 7b7a6d8c577dff4e18b2f1852530aea07c2c600d Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 05:11:13 -0800 Subject: [PATCH 32/42] fix(eventlog) context License: MIT Signed-off-by: Brian Tiger Chow --- util/eventlog/context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/eventlog/context.go b/util/eventlog/context.go index 03e2e3c5f4f..caaa426ad72 100644 --- a/util/eventlog/context.go +++ b/util/eventlog/context.go @@ -14,7 +14,7 @@ func ContextWithMetadata(ctx context.Context, l Loggable) context.Context { existing, err := MetadataFromContext(ctx) if err != nil { // context does not contain meta. just set the new metadata - child := context.WithValue(ctx, metadataKey, l.Loggable()) + child := context.WithValue(ctx, metadataKey, Metadata(l.Loggable())) return child } From d5c3535ffb1c0819500b4fab7ed641fa801c0345 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 05:12:02 -0800 Subject: [PATCH 33/42] misc(eventlog) run tests in parallel License: MIT Signed-off-by: Brian Tiger Chow --- util/eventlog/metadata_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/eventlog/metadata_test.go b/util/eventlog/metadata_test.go index bb07e700a1c..5c25320e3e1 100644 --- a/util/eventlog/metadata_test.go +++ b/util/eventlog/metadata_test.go @@ -38,11 +38,13 @@ func TestOverwrite(t *testing.T) { } func TestMarshalJSON(t *testing.T) { + t.Parallel() bs, _ := Metadata{"a": "b"}.JsonString() t.Log(bs) } func TestMetadataIsLoggable(t *testing.T) { + t.Parallel() func(l Loggable) { }(Metadata{}) } From e906dcd862bd1cfb9b228ceda4fad746f34187cb Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 05:12:19 -0800 Subject: [PATCH 34/42] refactor(eventlog) extract entry License: MIT Signed-off-by: Brian Tiger Chow --- util/eventlog/entry.go | 39 +++++++++++++++++++++++++++++++++++++++ util/eventlog/log.go | 34 ---------------------------------- 2 files changed, 39 insertions(+), 34 deletions(-) create mode 100644 util/eventlog/entry.go diff --git a/util/eventlog/entry.go b/util/eventlog/entry.go new file mode 100644 index 00000000000..aaac78a2bc3 --- /dev/null +++ b/util/eventlog/entry.go @@ -0,0 +1,39 @@ +package eventlog + +import ( + "time" + + "github.com/jbenet/go-ipfs/util" + "github.com/maybebtc/logrus" +) + +type entry struct { + loggables []Loggable + system string + event string +} + +// Log logs the event unconditionally (regardless of log level) +// TODO add support for leveled-logs once we decide which levels we want +// for our structured logs +func (e *entry) Log() { + e.log() +} + +// log is a private method invoked by the public Log, Info, Error methods +func (e *entry) log() { + // accumulate metadata + accum := Metadata{} + for _, loggable := range e.loggables { + accum = DeepMerge(accum, loggable.Loggable()) + } + + // apply final attributes to reserved keys + // TODO accum["level"] = level + accum["event"] = e.event + accum["system"] = e.system + accum["time"] = util.FormatRFC3339(time.Now()) + + // TODO roll our own event logger + logrus.WithFields(map[string]interface{}(accum)).Info(e.event) +} diff --git a/util/eventlog/log.go b/util/eventlog/log.go index 1b622f0f43a..4f9757bd15a 100644 --- a/util/eventlog/log.go +++ b/util/eventlog/log.go @@ -1,10 +1,7 @@ package eventlog import ( - "time" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus" logging "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-logging" "github.com/jbenet/go-ipfs/util" ) @@ -91,34 +88,3 @@ func (el *eventLogger) Event(ctx context.Context, event string, metadata ...Logg e.Log() // TODO replace this when leveled-logs have been implemented } - -type entry struct { - loggables []Loggable - system string - event string -} - -// Log logs the event unconditionally (regardless of log level) -// TODO add support for leveled-logs once we decide which levels we want -// for our structured logs -func (e *entry) Log() { - e.log() -} - -// log is a private method invoked by the public Log, Info, Error methods -func (e *entry) log() { - // accumulate metadata - accum := Metadata{} - for _, loggable := range e.loggables { - accum = DeepMerge(accum, loggable.Loggable()) - } - - // apply final attributes to reserved keys - // TODO accum["level"] = level - accum["event"] = e.event - accum["system"] = e.system - accum["time"] = util.FormatRFC3339(time.Now()) - - // TODO roll our own event logger - logrus.WithFields(map[string]interface{}(accum)).Info(e.event) -} From 02d8da09ae0b6c596dfbc5053fb79469e085a973 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 05:26:59 -0800 Subject: [PATCH 35/42] fix(2/init) message displayed during init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TODO use a writer to get the command output resulting appearance: ``` go-ipfs (feat/machine-readable-logging) λ. make install_2; ipfs2 init -f cd cmd/ipfs2 && go install initializing ipfs node at /Users/btc/.go-ipfs generating key pair... peer identity: %s QmcPLgrqfvnneJ3vr3oGpkTiS9Psx8p3HxevjnoZYToDRm ``` License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/init.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index cd589cc6e64..08afa178650 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -209,7 +209,7 @@ func identityConfig(nbits int) (config.Identity, error) { return ident, errors.New("Bitsize less than 1024 is considered unsafe.") } - fmt.Printf("generating key pair...") + fmt.Println("generating key pair...") sk, pk, err := ci.GenerateKeyPair(ci.RSA, nbits) if err != nil { return ident, err @@ -228,7 +228,7 @@ func identityConfig(nbits int) (config.Identity, error) { return ident, err } ident.PeerID = id.Pretty() - fmt.Println("generating key pair...") + fmt.Println("peer identity: %s", id.Pretty()) return ident, nil } From b03fbf73f5cecaa676c18c872a3d417155c0a285 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 05:29:11 -0800 Subject: [PATCH 36/42] deps(eventlog) use maybebtc fork License: MIT Signed-off-by: Brian Tiger Chow --- Godeps/Godeps.json | 10 +++++----- .../{Sirupsen => maybebtc}/logrus/.gitignore | 0 .../{Sirupsen => maybebtc}/logrus/.travis.yml | 0 .../github.com/{Sirupsen => maybebtc}/logrus/LICENSE | 0 .../github.com/{Sirupsen => maybebtc}/logrus/README.md | 0 .../github.com/{Sirupsen => maybebtc}/logrus/entry.go | 0 .../{Sirupsen => maybebtc}/logrus/entry_test.go | 0 .../logrus/examples/basic/basic.go | 2 +- .../logrus/examples/hook/hook.go | 4 ++-- .../{Sirupsen => maybebtc}/logrus/exported.go | 0 .../{Sirupsen => maybebtc}/logrus/formatter.go | 0 .../logrus/formatter_bench_test.go | 0 .../{Sirupsen => maybebtc}/logrus/hook_test.go | 0 .../github.com/{Sirupsen => maybebtc}/logrus/hooks.go | 0 .../logrus/hooks/airbrake/airbrake.go | 2 +- .../logrus/hooks/papertrail/README.md | 0 .../logrus/hooks/papertrail/papertrail.go | 2 +- .../logrus/hooks/papertrail/papertrail_test.go | 2 +- .../logrus/hooks/syslog/README.md | 0 .../logrus/hooks/syslog/syslog.go | 2 +- .../logrus/hooks/syslog/syslog_test.go | 2 +- .../{Sirupsen => maybebtc}/logrus/json_formatter.go | 0 .../github.com/{Sirupsen => maybebtc}/logrus/logger.go | 0 .../github.com/{Sirupsen => maybebtc}/logrus/logrus.go | 0 .../{Sirupsen => maybebtc}/logrus/logrus_test.go | 0 .../{Sirupsen => maybebtc}/logrus/terminal_darwin.go | 0 .../{Sirupsen => maybebtc}/logrus/terminal_freebsd.go | 0 .../{Sirupsen => maybebtc}/logrus/terminal_linux.go | 0 .../logrus/terminal_notwindows.go | 0 .../{Sirupsen => maybebtc}/logrus/terminal_windows.go | 0 .../{Sirupsen => maybebtc}/logrus/text_formatter.go | 0 util/eventlog/entry.go | 2 +- util/eventlog/option.go | 2 +- 33 files changed, 15 insertions(+), 15 deletions(-) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/.gitignore (100%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/.travis.yml (100%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/LICENSE (100%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/README.md (100%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/entry.go (100%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/entry_test.go (100%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/examples/basic/basic.go (90%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/examples/hook/hook.go (80%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/exported.go (100%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/formatter.go (100%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/formatter_bench_test.go (100%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/hook_test.go (100%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/hooks.go (100%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/hooks/airbrake/airbrake.go (94%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/hooks/papertrail/README.md (100%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/hooks/papertrail/papertrail.go (94%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/hooks/papertrail/papertrail_test.go (84%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/hooks/syslog/README.md (100%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/hooks/syslog/syslog.go (94%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/hooks/syslog/syslog_test.go (86%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/json_formatter.go (100%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/logger.go (100%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/logrus.go (100%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/logrus_test.go (100%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/terminal_darwin.go (100%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/terminal_freebsd.go (100%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/terminal_linux.go (100%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/terminal_notwindows.go (100%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/terminal_windows.go (100%) rename Godeps/_workspace/src/github.com/{Sirupsen => maybebtc}/logrus/text_formatter.go (100%) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 31b3b125c4f..e9e8bebb1f0 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -48,11 +48,6 @@ "Comment": "null-15", "Rev": "12e4b4183793ac4b061921e7980845e750679fd0" }, - { - "ImportPath": "github.com/Sirupsen/logrus", - "Comment": "v0.6.0-5-gf92b795", - "Rev": "f92b7950b372b1db80bd3527e4d40e42555fe6c2" - }, { "ImportPath": "github.com/braintree/manners", "Comment": "0.3.1-2-g5280e25", @@ -136,6 +131,11 @@ "ImportPath": "github.com/kr/binarydist", "Rev": "9955b0ab8708602d411341e55fffd7e0700f86bd" }, + { + "ImportPath": "github.com/maybebtc/logrus", + "Comment": "v0.6.0-5-gf92b795", + "Rev": "f92b7950b372b1db80bd3527e4d40e42555fe6c2" + }, { "ImportPath": "github.com/mitchellh/go-homedir", "Rev": "7d2d8c8a4e078ce3c58736ab521a40b37a504c52" diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/.gitignore b/Godeps/_workspace/src/github.com/maybebtc/logrus/.gitignore similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/.gitignore rename to Godeps/_workspace/src/github.com/maybebtc/logrus/.gitignore diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/.travis.yml b/Godeps/_workspace/src/github.com/maybebtc/logrus/.travis.yml similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/.travis.yml rename to Godeps/_workspace/src/github.com/maybebtc/logrus/.travis.yml diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/LICENSE b/Godeps/_workspace/src/github.com/maybebtc/logrus/LICENSE similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/LICENSE rename to Godeps/_workspace/src/github.com/maybebtc/logrus/LICENSE diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/README.md b/Godeps/_workspace/src/github.com/maybebtc/logrus/README.md similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/README.md rename to Godeps/_workspace/src/github.com/maybebtc/logrus/README.md diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/entry.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/entry.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/entry.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/entry.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/entry_test.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/entry_test.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/entry_test.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/entry_test.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/basic/basic.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/examples/basic/basic.go similarity index 90% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/basic/basic.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/examples/basic/basic.go index 19422a212e3..a62ba45de50 100644 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/basic/basic.go +++ b/Godeps/_workspace/src/github.com/maybebtc/logrus/examples/basic/basic.go @@ -1,7 +1,7 @@ package main import ( - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus" + "github.com/Sirupsen/logrus" ) var log = logrus.New() diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/hook/hook.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/examples/hook/hook.go similarity index 80% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/hook/hook.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/examples/hook/hook.go index d6d43d4860e..42e7a4c9825 100644 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/examples/hook/hook.go +++ b/Godeps/_workspace/src/github.com/maybebtc/logrus/examples/hook/hook.go @@ -1,8 +1,8 @@ package main import ( - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake" + "github.com/Sirupsen/logrus" + "github.com/Sirupsen/logrus/hooks/airbrake" "github.com/tobi/airbrake-go" ) diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/exported.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/exported.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/exported.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/exported.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/formatter.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/formatter.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter_bench_test.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/formatter_bench_test.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/formatter_bench_test.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/formatter_bench_test.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hook_test.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/hook_test.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/hook_test.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/hook_test.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/hooks.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/hooks.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/hooks/airbrake/airbrake.go similarity index 94% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/hooks/airbrake/airbrake.go index 0162ddf73c3..880d21ecdc1 100644 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/airbrake/airbrake.go +++ b/Godeps/_workspace/src/github.com/maybebtc/logrus/hooks/airbrake/airbrake.go @@ -1,7 +1,7 @@ package logrus_airbrake import ( - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus" + "github.com/Sirupsen/logrus" "github.com/tobi/airbrake-go" ) diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/README.md b/Godeps/_workspace/src/github.com/maybebtc/logrus/hooks/papertrail/README.md similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/README.md rename to Godeps/_workspace/src/github.com/maybebtc/logrus/hooks/papertrail/README.md diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/hooks/papertrail/papertrail.go similarity index 94% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/hooks/papertrail/papertrail.go index 4d1765649ec..48e2feaeb57 100644 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail.go +++ b/Godeps/_workspace/src/github.com/maybebtc/logrus/hooks/papertrail/papertrail.go @@ -6,7 +6,7 @@ import ( "os" "time" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus" + "github.com/Sirupsen/logrus" ) const ( diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail_test.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/hooks/papertrail/papertrail_test.go similarity index 84% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail_test.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/hooks/papertrail/papertrail_test.go index f8146fda58c..96318d00304 100644 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/papertrail/papertrail_test.go +++ b/Godeps/_workspace/src/github.com/maybebtc/logrus/hooks/papertrail/papertrail_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus" + "github.com/Sirupsen/logrus" "github.com/stvp/go-udp-testing" ) diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/README.md b/Godeps/_workspace/src/github.com/maybebtc/logrus/hooks/syslog/README.md similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/README.md rename to Godeps/_workspace/src/github.com/maybebtc/logrus/hooks/syslog/README.md diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/hooks/syslog/syslog.go similarity index 94% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/hooks/syslog/syslog.go index fcc67bbf70a..2a18ce6130c 100644 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog.go +++ b/Godeps/_workspace/src/github.com/maybebtc/logrus/hooks/syslog/syslog.go @@ -2,7 +2,7 @@ package logrus_syslog import ( "fmt" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus" + "github.com/Sirupsen/logrus" "log/syslog" "os" ) diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/hooks/syslog/syslog_test.go similarity index 86% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/hooks/syslog/syslog_test.go index f26c87be82e..42762dc10d7 100644 --- a/Godeps/_workspace/src/github.com/Sirupsen/logrus/hooks/syslog/syslog_test.go +++ b/Godeps/_workspace/src/github.com/maybebtc/logrus/hooks/syslog/syslog_test.go @@ -1,7 +1,7 @@ package logrus_syslog import ( - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus" + "github.com/Sirupsen/logrus" "log/syslog" "testing" ) diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/json_formatter.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/json_formatter.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/json_formatter.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/logger.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/logger.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/logger.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/logger.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/logrus.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/logrus.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus_test.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/logrus_test.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/logrus_test.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/logrus_test.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_darwin.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/terminal_darwin.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_darwin.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/terminal_darwin.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_freebsd.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/terminal_freebsd.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_freebsd.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/terminal_freebsd.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_linux.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/terminal_linux.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_linux.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/terminal_linux.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_notwindows.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/terminal_notwindows.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_notwindows.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/terminal_notwindows.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_windows.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/terminal_windows.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/terminal_windows.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/terminal_windows.go diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/text_formatter.go similarity index 100% rename from Godeps/_workspace/src/github.com/Sirupsen/logrus/text_formatter.go rename to Godeps/_workspace/src/github.com/maybebtc/logrus/text_formatter.go diff --git a/util/eventlog/entry.go b/util/eventlog/entry.go index aaac78a2bc3..ec293f98e99 100644 --- a/util/eventlog/entry.go +++ b/util/eventlog/entry.go @@ -4,7 +4,7 @@ import ( "time" "github.com/jbenet/go-ipfs/util" - "github.com/maybebtc/logrus" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/maybebtc/logrus" ) type entry struct { diff --git a/util/eventlog/option.go b/util/eventlog/option.go index b278d3e92af..7c482ca11f9 100644 --- a/util/eventlog/option.go +++ b/util/eventlog/option.go @@ -3,8 +3,8 @@ package eventlog import ( "io" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/Sirupsen/logrus" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/maybebtc/logrus" ) type Option func() From 2d6b10f60540a73ba3f50862cc1452d8362d05d8 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 06:00:24 -0800 Subject: [PATCH 37/42] deps(eventlog) disable logrus' mutation of event keys now, logrus does not automatically inject time, msg, level metadata License: MIT Signed-off-by: Brian Tiger Chow --- Godeps/Godeps.json | 4 ++-- .../src/github.com/maybebtc/logrus/entry.go | 11 ++++++++--- .../src/github.com/maybebtc/logrus/entry_test.go | 2 ++ .../src/github.com/maybebtc/logrus/exported.go | 8 ++++++++ .../src/github.com/maybebtc/logrus/logger.go | 5 +++++ .../github.com/maybebtc/logrus/logrus_test.go | 1 + .../maybebtc/logrus/polite_json_formatter.go | 16 ++++++++++++++++ 7 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 Godeps/_workspace/src/github.com/maybebtc/logrus/polite_json_formatter.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index e9e8bebb1f0..4762bf5ff20 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -133,8 +133,8 @@ }, { "ImportPath": "github.com/maybebtc/logrus", - "Comment": "v0.6.0-5-gf92b795", - "Rev": "f92b7950b372b1db80bd3527e4d40e42555fe6c2" + "Comment": "v0.6.0-7-g51cc99e", + "Rev": "51cc99e4b07ec5516233a8370c28b5eedd096b6b" }, { "ImportPath": "github.com/mitchellh/go-homedir", diff --git a/Godeps/_workspace/src/github.com/maybebtc/logrus/entry.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/entry.go index e164eecb5f3..bb2aabdbcfc 100644 --- a/Godeps/_workspace/src/github.com/maybebtc/logrus/entry.go +++ b/Godeps/_workspace/src/github.com/maybebtc/logrus/entry.go @@ -71,9 +71,14 @@ func (entry *Entry) WithFields(fields Fields) *Entry { } func (entry *Entry) log(level Level, msg string) { - entry.Time = time.Now() - entry.Level = level - entry.Message = msg + + entry.Logger.mu.Lock() + if entry.Logger.WriteFields { + entry.Time = time.Now() + entry.Level = level + entry.Message = msg + } + entry.Logger.mu.Unlock() if err := entry.Logger.Hooks.Fire(level, entry); err != nil { entry.Logger.mu.Lock() diff --git a/Godeps/_workspace/src/github.com/maybebtc/logrus/entry_test.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/entry_test.go index 98717df4901..8359abe18a4 100644 --- a/Godeps/_workspace/src/github.com/maybebtc/logrus/entry_test.go +++ b/Godeps/_workspace/src/github.com/maybebtc/logrus/entry_test.go @@ -26,6 +26,7 @@ func TestEntryPanicln(t *testing.T) { logger := New() logger.Out = &bytes.Buffer{} + logger.WriteFields = true entry := NewEntry(logger) entry.WithField("err", errBoom).Panicln("kaboom") } @@ -48,6 +49,7 @@ func TestEntryPanicf(t *testing.T) { logger := New() logger.Out = &bytes.Buffer{} + logger.WriteFields = true entry := NewEntry(logger) entry.WithField("err", errBoom).Panicf("kaboom %v", true) } diff --git a/Godeps/_workspace/src/github.com/maybebtc/logrus/exported.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/exported.go index 0e2d59f19a7..46fb099aa14 100644 --- a/Godeps/_workspace/src/github.com/maybebtc/logrus/exported.go +++ b/Godeps/_workspace/src/github.com/maybebtc/logrus/exported.go @@ -56,6 +56,14 @@ func WithFields(fields Fields) *Entry { return std.WithFields(fields) } +// WriteFields gives the Logger permission to add time, level, msg info to +// Entries +func WriteFields(b bool) { + std.mu.Lock() + defer std.mu.Unlock() + std.WriteFields = b +} + // Debug logs a message at level Debug on the standard logger. func Debug(args ...interface{}) { std.Debug(args...) diff --git a/Godeps/_workspace/src/github.com/maybebtc/logrus/logger.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/logger.go index 7374fe365d3..ceec1a6c55f 100644 --- a/Godeps/_workspace/src/github.com/maybebtc/logrus/logger.go +++ b/Godeps/_workspace/src/github.com/maybebtc/logrus/logger.go @@ -26,6 +26,11 @@ type Logger struct { // to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be // logged. `logrus.Debug` is useful in Level Level + + // WriteFields permits the logger to add time, msg, level data to + // user-provided Fields + WriteFields bool + // Used to sync writing to the log. mu sync.Mutex } diff --git a/Godeps/_workspace/src/github.com/maybebtc/logrus/logrus_test.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/logrus_test.go index 15157d172da..3294279519b 100644 --- a/Godeps/_workspace/src/github.com/maybebtc/logrus/logrus_test.go +++ b/Godeps/_workspace/src/github.com/maybebtc/logrus/logrus_test.go @@ -17,6 +17,7 @@ func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fi logger := New() logger.Out = &buffer logger.Formatter = new(JSONFormatter) + logger.WriteFields = true log(logger) diff --git a/Godeps/_workspace/src/github.com/maybebtc/logrus/polite_json_formatter.go b/Godeps/_workspace/src/github.com/maybebtc/logrus/polite_json_formatter.go new file mode 100644 index 00000000000..0a8fc2ec04a --- /dev/null +++ b/Godeps/_workspace/src/github.com/maybebtc/logrus/polite_json_formatter.go @@ -0,0 +1,16 @@ +package logrus + +import ( + "encoding/json" + "fmt" +) + +type PoliteJSONFormatter struct{} + +func (f *PoliteJSONFormatter) Format(entry *Entry) ([]byte, error) { + serialized, err := json.Marshal(entry.Data) + if err != nil { + return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) + } + return append(serialized, '\n'), nil +} From 0f6a2e27789b49c96ab397c11b5cfa63e82bf4af Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 06:03:06 -0800 Subject: [PATCH 38/42] feat(eventlog) use the polite json formatter License: MIT Signed-off-by: Brian Tiger Chow --- util/eventlog/option.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/eventlog/option.go b/util/eventlog/option.go index 7c482ca11f9..4fe3daa592b 100644 --- a/util/eventlog/option.go +++ b/util/eventlog/option.go @@ -3,8 +3,8 @@ package eventlog import ( "io" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/maybebtc/logrus" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2" ) type Option func() @@ -17,7 +17,7 @@ func Configure(options ...Option) { // LdJSONFormatter formats the event log as line-delimited JSON var LdJSONFormatter = func() { - logrus.SetFormatter(&logrus.JSONFormatter{}) + logrus.SetFormatter(&logrus.PoliteJSONFormatter{}) } var TextFormatter = func() { From 6291c254557a3a245c7b4e972949028a927c3f94 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 06:51:21 -0800 Subject: [PATCH 39/42] fix(net/conn) Set peer to "Remote" in Dial method @jbenet we may want to consider setting the default type to Remote. I left the default as Unspecified just for safety. License: MIT Signed-off-by: Brian Tiger Chow --- net/conn/dial.go | 1 + 1 file changed, 1 insertion(+) diff --git a/net/conn/dial.go b/net/conn/dial.go index 7b9c1dc8603..c19927713e0 100644 --- a/net/conn/dial.go +++ b/net/conn/dial.go @@ -23,6 +23,7 @@ func (d *Dialer) Dial(ctx context.Context, network string, remote peer.Peer) (Co return nil, fmt.Errorf("No remote address for network %s", network) } + remote.SetType(peer.Remote) remote, err := d.Peerstore.Add(remote) if err != nil { log.Errorf("Error putting peer into peerstore: %s", remote) From 1c4c7412b44a48d9e502976558a0a36cb4d8bebf Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 07:40:05 -0800 Subject: [PATCH 40/42] fix(misc) address PR comments License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/main.go | 8 ++++---- routing/dht/dht.go | 6 ------ 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index ca9f9662b5d..a662e273aa9 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -205,12 +205,12 @@ func (i *cmdInvocation) requestedHelp() (short bool, long bool, err error) { return longHelp, shortHelp, nil } -func callPreCommandHooks(command cmdDetails, req cmds.Request, root *cmds.Command) error { +func callPreCommandHooks(details cmdDetails, req cmds.Request, root *cmds.Command) error { log.Debug("Calling pre-command hooks...") // some hooks only run when the command is executed locally - daemon, err := commandShouldRunOnDaemon(command, req, root) + daemon, err := commandShouldRunOnDaemon(details, req, root) if err != nil { return err } @@ -218,7 +218,7 @@ func callPreCommandHooks(command cmdDetails, req cmds.Request, root *cmds.Comman // check for updates when 1) commands is going to be run locally, 2) the // command does not initialize the config, and 3) the command does not // pre-empt updates - if !daemon && command.usesConfigAsInput() && command.doesNotPreemptAutoUpdate() { + if !daemon && details.usesConfigAsInput() && details.doesNotPreemptAutoUpdate() { log.Debug("Calling hook: Check for updates") @@ -234,7 +234,7 @@ func callPreCommandHooks(command cmdDetails, req cmds.Request, root *cmds.Comman // When the upcoming command may use the config and repo, we know it's safe // for the log config hook to touch the config/repo - if command.usesConfigAsInput() && command.usesRepo() { + if details.usesConfigAsInput() && details.usesRepo() { log.Debug("Calling hook: Configure Event Logger") cfg, err := req.Context().GetConfig() if err != nil { diff --git a/routing/dht/dht.go b/routing/dht/dht.go index 7a61c75f05b..f4d2948bc04 100644 --- a/routing/dht/dht.go +++ b/routing/dht/dht.go @@ -152,10 +152,7 @@ func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) msg.N // update the peer (on valid msgs only) dht.Update(mPeer) - // Print out diagnostic log.Event(ctx, "foo", dht.self, mPeer, pmes) - log.Debugf("%s got message type: '%s' from %s", - dht.self, pb.Message_MessageType_name[int32(pmes.GetType())], mPeer) // get handler for this msg type. handler := dht.handlerForMsgType(pmes.GetType()) @@ -198,10 +195,7 @@ func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.Peer, pmes *pb.Messa start := time.Now() - // Print out diagnostic log.Event(ctx, "sentMessage", dht.self, p, pmes) - log.Debugf("Sent message type: '%s' to %s", - pb.Message_MessageType_name[int32(pmes.GetType())], p) rmes, err := dht.sender.SendRequest(ctx, mes) if err != nil { From c8c062fe13066ff2d351709db66301887a69efa5 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 07:52:36 -0800 Subject: [PATCH 41/42] refactor(config/init) add hooks to identity generation. useful for displaying messages to the user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @jbenet this commit re-introduces the peer identity line. It's very useful. I understand it may have been removed because of the clashing of the IDs. To alleviate this, this commit places some negative space between the two lines. 'to test' -> 'to get started' as a stronger call to action ``` λ. ipfs2 init -f initializing ipfs node at /Users/btc/.go-ipfs generating key pair...done. peer identity: QmWzjxNEYKjDAxuHJqvtLP1dZTDjreBSUsArWoHai1v9yP to get started, enter: ipfs cat QmYpv2VEsxzTTXRYX3PjDg961cnJE3kY1YDXLycHGQ3zZB ``` License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/init.go | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index 08afa178650..9be58f6c180 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -103,10 +103,23 @@ func doInit(configRoot string, dspathOverride string, force bool, nBitsForKeypai return nil, err } + err = addTheWelcomeFile(conf, func(k u.Key) { + fmt.Printf("\nto get started, enter: ipfs cat %s\n", k) + }) + if err != nil { + return nil, err + } + + return nil, nil +} + +// addTheWelcomeFile adds a file containing the welcome message to the newly +// minted node. On success, it calls onSuccess +func addTheWelcomeFile(conf *config.Config, onSuccess func(u.Key)) error { // TODO extract this file creation operation into a function nd, err := core.NewIpfsNode(conf, false) if err != nil { - return nil, err + return err } defer nd.Close() @@ -115,15 +128,15 @@ func doInit(configRoot string, dspathOverride string, force bool, nBitsForKeypai defnd, err := imp.BuildDagFromReader(reader, nd.DAG, nd.Pinning.GetManual(), chunk.DefaultSplitter) if err != nil { - return nil, err + return err } k, err := defnd.Key() if err != nil { - return nil, fmt.Errorf("failed to write test file: %s", err) + return fmt.Errorf("failed to write test file: %s", err) } - fmt.Printf("done.\nto test, enter: ipfs cat %s\n", k) - return nil, nil + onSuccess(k) + return nil } func datastoreConfig(dspath string) (config.Datastore, error) { @@ -152,7 +165,12 @@ func initConfig(configFilename string, dspathOverride string, nBitsForKeypair in return nil, err } - identity, err := identityConfig(nBitsForKeypair) + identity, err := identityConfig(nBitsForKeypair, func() { + fmt.Printf("generating key pair...") + }, func(ident config.Identity) { + fmt.Printf("done.\n") + fmt.Printf("peer identity: %s\n", ident.PeerID) + }) if err != nil { return nil, err } @@ -202,14 +220,17 @@ func initConfig(configFilename string, dspathOverride string, nBitsForKeypair in return conf, nil } -func identityConfig(nbits int) (config.Identity, error) { +// identityConfig initializes a new identity. It calls onBegin when it begins +// to generate the identity and it calls onSuccess once the operation is +// completed successfully +func identityConfig(nbits int, onBegin func(), onSuccess func(config.Identity)) (config.Identity, error) { // TODO guard higher up ident := config.Identity{} if nbits < 1024 { return ident, errors.New("Bitsize less than 1024 is considered unsafe.") } - fmt.Println("generating key pair...") + onBegin() sk, pk, err := ci.GenerateKeyPair(ci.RSA, nbits) if err != nil { return ident, err @@ -228,8 +249,7 @@ func identityConfig(nbits int) (config.Identity, error) { return ident, err } ident.PeerID = id.Pretty() - fmt.Println("peer identity: %s", id.Pretty()) - + onSuccess(ident) return ident, nil } From c4ea0b78ba1235add334a4428ee7694129aee7f6 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 16 Nov 2014 08:28:08 -0800 Subject: [PATCH 42/42] fix(2/init) msg @jbenet i presume you prefer it without the period License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/init.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index 9be58f6c180..fbd20204703 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -168,7 +168,7 @@ func initConfig(configFilename string, dspathOverride string, nBitsForKeypair in identity, err := identityConfig(nBitsForKeypair, func() { fmt.Printf("generating key pair...") }, func(ident config.Identity) { - fmt.Printf("done.\n") + fmt.Printf("done\n") fmt.Printf("peer identity: %s\n", ident.PeerID) }) if err != nil {