github.com/uber/kraken@v0.1.4/lib/torrent/scheduler/torrentlog/logger.go (about) 1 // Copyright (c) 2016-2019 Uber Technologies, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package torrentlog 15 16 import ( 17 "errors" 18 "fmt" 19 "os" 20 "time" 21 22 "github.com/uber/kraken/core" 23 "github.com/uber/kraken/utils/log" 24 25 "go.uber.org/zap" 26 "go.uber.org/zap/zapcore" 27 ) 28 29 var ( 30 errEmptyReceivedPieces = errors.New("empty received piece counts") 31 errNegativeReceivedPieces = errors.New("negative value in received piece counts") 32 ) 33 34 // Logger wraps structured log entries for important torrent events. These events 35 // are intended to be consumed at the cluster level via ELK, and are distinct from 36 // the verbose stdout logs of the agent. In particular, Logger bridges host-agnostic 37 // metrics to individual hostnames. 38 // 39 // For example, if there is a spike in download times, an engineer can cross-reference 40 // the spike with the torrent logs in ELK and zero-in on a single host. From there, 41 // the engineer can inspect the stdout logs of the host for more detailed information 42 // as to why the download took so long. 43 type Logger struct { 44 zap *zap.Logger 45 } 46 47 // New creates a new Logger. 48 func New(config log.Config, pctx core.PeerContext) (*Logger, error) { 49 hostname, err := os.Hostname() 50 if err != nil { 51 return nil, fmt.Errorf("hostname: %s", err) 52 } 53 54 logger, err := log.New(config, map[string]interface{}{ 55 "hostname": hostname, 56 "zone": pctx.Zone, 57 "cluster": pctx.Cluster, 58 "peer_id": pctx.PeerID.String(), 59 }) 60 if err != nil { 61 return nil, fmt.Errorf("config: %s", err) 62 } 63 return &Logger{logger}, nil 64 } 65 66 // NewNopLogger returns a Logger containing a no-op zap logger for testing purposes. 67 func NewNopLogger() *Logger { 68 return &Logger{zap.NewNop()} 69 } 70 71 // OutgoingConnectionAccept logs an accepted outgoing connection. 72 func (l *Logger) OutgoingConnectionAccept( 73 d core.Digest, 74 infoHash core.InfoHash, 75 remotePeerID core.PeerID) { 76 77 l.zap.Debug( 78 "Outgoing connection accept", 79 zap.String("name", d.Hex()), 80 zap.String("info_hash", infoHash.String()), 81 zap.String("remote_peer_id", remotePeerID.String())) 82 } 83 84 // OutgoingConnectionReject logs a rejected outgoing connection. 85 func (l *Logger) OutgoingConnectionReject(d core.Digest, 86 infoHash core.InfoHash, 87 remotePeerID core.PeerID, 88 err error) { 89 90 l.zap.Debug( 91 "Outgoing connection reject", 92 zap.String("name", d.Hex()), 93 zap.String("info_hash", infoHash.String()), 94 zap.String("remote_peer_id", remotePeerID.String()), 95 zap.Error(err)) 96 } 97 98 // IncomingConnectionAccept logs an accepted incoming connection. 99 func (l *Logger) IncomingConnectionAccept( 100 d core.Digest, 101 infoHash core.InfoHash, 102 remotePeerID core.PeerID) { 103 104 l.zap.Debug( 105 "Incoming connection accept", 106 zap.String("name", d.Hex()), 107 zap.String("info_hash", infoHash.String()), 108 zap.String("remote_peer_id", remotePeerID.String())) 109 } 110 111 // IncomingConnectionReject logs a rejected incoming connection. 112 func (l *Logger) IncomingConnectionReject( 113 d core.Digest, 114 infoHash core.InfoHash, 115 remotePeerID core.PeerID, 116 err error) { 117 118 l.zap.Debug( 119 "Incoming connection reject", 120 zap.String("name", d.Hex()), 121 zap.String("info_hash", infoHash.String()), 122 zap.String("remote_peer_id", remotePeerID.String()), 123 zap.Error(err)) 124 } 125 126 // SeedTimeout logs a seeding torrent being torn down due to timeout. 127 func (l *Logger) SeedTimeout(d core.Digest, infoHash core.InfoHash) { 128 l.zap.Debug( 129 "Seed timeout", 130 zap.String("name", d.Hex()), 131 zap.String("info_hash", infoHash.String())) 132 } 133 134 // LeechTimeout logs a leeching torrent being torn down due to timeout. 135 func (l *Logger) LeechTimeout(d core.Digest, infoHash core.InfoHash) { 136 l.zap.Debug( 137 "Leech timeout", 138 zap.String("name", d.Hex()), 139 zap.String("info_hash", infoHash.String())) 140 } 141 142 // DownloadSuccess logs a successful download. 143 func (l *Logger) DownloadSuccess(namespace string, d core.Digest, size int64, downloadTime time.Duration) { 144 l.zap.Info( 145 "Download success", 146 zap.String("namespace", namespace), 147 zap.String("name", d.Hex()), 148 zap.Int64("size", size), 149 zap.Duration("download_time", downloadTime)) 150 } 151 152 // DownloadFailure logs a failed download. 153 func (l *Logger) DownloadFailure(namespace string, d core.Digest, size int64, err error) { 154 l.zap.Error( 155 "Download failure", 156 zap.String("namespace", namespace), 157 zap.String("name", d.Hex()), 158 zap.Int64("size", size), 159 zap.Error(err)) 160 } 161 162 // SeederSummaries logs a summary of the pieces requested and received from peers for a torrent. 163 func (l *Logger) SeederSummaries( 164 d core.Digest, 165 infoHash core.InfoHash, 166 summaries SeederSummaries) error { 167 168 l.zap.Debug( 169 "Seeder summaries", 170 zap.String("name", d.Hex()), 171 zap.String("info_hash", infoHash.String()), 172 zap.Array("seeder_summaries", summaries)) 173 return nil 174 } 175 176 // LeecherSummaries logs a summary of the pieces requested by and sent to peers for a torrent. 177 func (l *Logger) LeecherSummaries( 178 d core.Digest, 179 infoHash core.InfoHash, 180 summaries LeecherSummaries) error { 181 182 l.zap.Debug( 183 "Leecher summaries", 184 zap.String("name", d.Hex()), 185 zap.String("info_hash", infoHash.String()), 186 zap.Array("leecher_summaries", summaries)) 187 return nil 188 } 189 190 // Sync flushes the log. 191 func (l *Logger) Sync() { 192 l.zap.Sync() 193 } 194 195 // SeederSummary contains information about piece requests to and pieces received from a peer. 196 type SeederSummary struct { 197 PeerID core.PeerID 198 RequestsSent int 199 GoodPiecesReceived int 200 DuplicatePiecesReceived int 201 } 202 203 // MarshalLogObject marshals a SeederSummary for logging. 204 func (s SeederSummary) MarshalLogObject(enc zapcore.ObjectEncoder) error { 205 enc.AddString("peer_id", s.PeerID.String()) 206 enc.AddInt("requests_sent", s.RequestsSent) 207 enc.AddInt("good_pieces_received", s.GoodPiecesReceived) 208 enc.AddInt("duplicate_pieces_received", s.DuplicatePiecesReceived) 209 return nil 210 } 211 212 // SeederSummaries represents a slice of type SeederSummary 213 // that can be marshalled for logging. 214 type SeederSummaries []SeederSummary 215 216 // MarshalLogArray marshals a SeederSummaries slice for logging. 217 func (ss SeederSummaries) MarshalLogArray(enc zapcore.ArrayEncoder) error { 218 for _, summary := range ss { 219 enc.AppendObject(summary) 220 } 221 return nil 222 } 223 224 // LeecherSummary contains information about piece requests from and pieces sent to a peer. 225 type LeecherSummary struct { 226 PeerID core.PeerID 227 RequestsReceived int 228 PiecesSent int 229 } 230 231 // MarshalLogObject marshals a LeecherSummary for logging. 232 func (s LeecherSummary) MarshalLogObject(enc zapcore.ObjectEncoder) error { 233 enc.AddString("peer_id", s.PeerID.String()) 234 enc.AddInt("requests_received", s.RequestsReceived) 235 enc.AddInt("pieces_sent", s.PiecesSent) 236 return nil 237 } 238 239 // LeecherSummaries represents a slice of type LeecherSummary 240 // that can be marshalled for logging. 241 type LeecherSummaries []LeecherSummary 242 243 // MarshalLogArray marshals a LeecherSummaries slice for logging. 244 func (ls LeecherSummaries) MarshalLogArray(enc zapcore.ArrayEncoder) error { 245 for _, summary := range ls { 246 enc.AppendObject(summary) 247 } 248 return nil 249 }