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  }