github.com/dashpay/godash@v0.0.0-20160726055534-e038a21e0e3d/peer/log.go (about)

     1  // Copyright (c) 2015 The btcsuite developers
     2  // Copyright (c) 2016 The Dash developers
     3  // Use of this source code is governed by an ISC
     4  // license that can be found in the LICENSE file.
     5  
     6  package peer
     7  
     8  import (
     9  	"errors"
    10  	"fmt"
    11  	"io"
    12  	"strings"
    13  	"time"
    14  
    15  	"github.com/btcsuite/btclog"
    16  	"github.com/dashpay/godash/txscript"
    17  	"github.com/dashpay/godash/wire"
    18  )
    19  
    20  const (
    21  	// maxRejectReasonLen is the maximum length of a sanitized reject reason
    22  	// that will be logged.
    23  	maxRejectReasonLen = 250
    24  )
    25  
    26  // log is a logger that is initialized with no output filters.  This
    27  // means the package will not perform any logging by default until the caller
    28  // requests it.
    29  var log btclog.Logger
    30  
    31  // The default amount of logging is none.
    32  func init() {
    33  	DisableLog()
    34  }
    35  
    36  // DisableLog disables all library log output.  Logging output is disabled
    37  // by default until either UseLogger or SetLogWriter are called.
    38  func DisableLog() {
    39  	log = btclog.Disabled
    40  }
    41  
    42  // UseLogger uses a specified Logger to output package logging info.
    43  // This should be used in preference to SetLogWriter if the caller is also
    44  // using btclog.
    45  func UseLogger(logger btclog.Logger) {
    46  	log = logger
    47  }
    48  
    49  // SetLogWriter uses a specified io.Writer to output package logging info.
    50  // This allows a caller to direct package logging output without needing a
    51  // dependency on seelog.  If the caller is also using btclog, UseLogger should
    52  // be used instead.
    53  func SetLogWriter(w io.Writer, level string) error {
    54  	if w == nil {
    55  		return errors.New("nil writer")
    56  	}
    57  
    58  	lvl, ok := btclog.LogLevelFromString(level)
    59  	if !ok {
    60  		return errors.New("invalid log level")
    61  	}
    62  
    63  	l, err := btclog.NewLoggerFromWriter(w, lvl)
    64  	if err != nil {
    65  		return err
    66  	}
    67  
    68  	UseLogger(l)
    69  	return nil
    70  }
    71  
    72  // LogClosure is a closure that can be printed with %v to be used to
    73  // generate expensive-to-create data for a detailed log level and avoid doing
    74  // the work if the data isn't printed.
    75  type logClosure func() string
    76  
    77  func (c logClosure) String() string {
    78  	return c()
    79  }
    80  
    81  func newLogClosure(c func() string) logClosure {
    82  	return logClosure(c)
    83  }
    84  
    85  // directionString is a helper function that returns a string that represents
    86  // the direction of a connection (inbound or outbound).
    87  func directionString(inbound bool) string {
    88  	if inbound {
    89  		return "inbound"
    90  	}
    91  	return "outbound"
    92  }
    93  
    94  // formatLockTime returns a transaction lock time as a human-readable string.
    95  func formatLockTime(lockTime uint32) string {
    96  	// The lock time field of a transaction is either a block height at
    97  	// which the transaction is finalized or a timestamp depending on if the
    98  	// value is before the lockTimeThreshold.  When it is under the
    99  	// threshold it is a block height.
   100  	if lockTime < txscript.LockTimeThreshold {
   101  		return fmt.Sprintf("height %d", lockTime)
   102  	}
   103  
   104  	return time.Unix(int64(lockTime), 0).String()
   105  }
   106  
   107  // invSummary returns an inventory message as a human-readable string.
   108  func invSummary(invList []*wire.InvVect) string {
   109  	// No inventory.
   110  	invLen := len(invList)
   111  	if invLen == 0 {
   112  		return "empty"
   113  	}
   114  
   115  	// One inventory item.
   116  	if invLen == 1 {
   117  		iv := invList[0]
   118  		switch iv.Type {
   119  		case wire.InvTypeError:
   120  			return fmt.Sprintf("error %s", iv.Hash)
   121  		case wire.InvTypeBlock:
   122  			return fmt.Sprintf("block %s", iv.Hash)
   123  		case wire.InvTypeTx:
   124  			return fmt.Sprintf("tx %s", iv.Hash)
   125  		}
   126  
   127  		return fmt.Sprintf("unknown (%d) %s", uint32(iv.Type), iv.Hash)
   128  	}
   129  
   130  	// More than one inv item.
   131  	return fmt.Sprintf("size %d", invLen)
   132  }
   133  
   134  // locatorSummary returns a block locator as a human-readable string.
   135  func locatorSummary(locator []*wire.ShaHash, stopHash *wire.ShaHash) string {
   136  	if len(locator) > 0 {
   137  		return fmt.Sprintf("locator %s, stop %s", locator[0], stopHash)
   138  	}
   139  
   140  	return fmt.Sprintf("no locator, stop %s", stopHash)
   141  
   142  }
   143  
   144  // sanitizeString strips any characters which are even remotely dangerous, such
   145  // as html control characters, from the passed string.  It also limits it to
   146  // the passed maximum size, which can be 0 for unlimited.  When the string is
   147  // limited, it will also add "..." to the string to indicate it was truncated.
   148  func sanitizeString(str string, maxLength uint) string {
   149  	const safeChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY" +
   150  		"Z01234567890 .,;_/:?@"
   151  
   152  	// Strip any characters not in the safeChars string removed.
   153  	str = strings.Map(func(r rune) rune {
   154  		if strings.IndexRune(safeChars, r) >= 0 {
   155  			return r
   156  		}
   157  		return -1
   158  	}, str)
   159  
   160  	// Limit the string to the max allowed length.
   161  	if maxLength > 0 && uint(len(str)) > maxLength {
   162  		str = str[:maxLength]
   163  		str = str + "..."
   164  	}
   165  	return str
   166  }
   167  
   168  // messageSummary returns a human-readable string which summarizes a message.
   169  // Not all messages have or need a summary.  This is used for debug logging.
   170  func messageSummary(msg wire.Message) string {
   171  	switch msg := msg.(type) {
   172  	case *wire.MsgVersion:
   173  		return fmt.Sprintf("agent %s, pver %d, block %d",
   174  			msg.UserAgent, msg.ProtocolVersion, msg.LastBlock)
   175  
   176  	case *wire.MsgVerAck:
   177  		// No summary.
   178  
   179  	case *wire.MsgGetAddr:
   180  		// No summary.
   181  
   182  	case *wire.MsgAddr:
   183  		return fmt.Sprintf("%d addr", len(msg.AddrList))
   184  
   185  	case *wire.MsgPing:
   186  		// No summary - perhaps add nonce.
   187  
   188  	case *wire.MsgPong:
   189  		// No summary - perhaps add nonce.
   190  
   191  	case *wire.MsgAlert:
   192  		// No summary.
   193  
   194  	case *wire.MsgMemPool:
   195  		// No summary.
   196  
   197  	case *wire.MsgTx:
   198  		return fmt.Sprintf("hash %s, %d inputs, %d outputs, lock %s",
   199  			msg.TxSha(), len(msg.TxIn), len(msg.TxOut),
   200  			formatLockTime(msg.LockTime))
   201  
   202  	case *wire.MsgBlock:
   203  		header := &msg.Header
   204  		return fmt.Sprintf("hash %s, ver %d, %d tx, %s", msg.BlockSha(),
   205  			header.Version, len(msg.Transactions), header.Timestamp)
   206  
   207  	case *wire.MsgInv:
   208  		return invSummary(msg.InvList)
   209  
   210  	case *wire.MsgNotFound:
   211  		return invSummary(msg.InvList)
   212  
   213  	case *wire.MsgGetData:
   214  		return invSummary(msg.InvList)
   215  
   216  	case *wire.MsgGetBlocks:
   217  		return locatorSummary(msg.BlockLocatorHashes, &msg.HashStop)
   218  
   219  	case *wire.MsgGetHeaders:
   220  		return locatorSummary(msg.BlockLocatorHashes, &msg.HashStop)
   221  
   222  	case *wire.MsgHeaders:
   223  		return fmt.Sprintf("num %d", len(msg.Headers))
   224  
   225  	case *wire.MsgReject:
   226  		// Ensure the variable length strings don't contain any
   227  		// characters which are even remotely dangerous such as HTML
   228  		// control characters, etc.  Also limit them to sane length for
   229  		// logging.
   230  		rejCommand := sanitizeString(msg.Cmd, wire.CommandSize)
   231  		rejReason := sanitizeString(msg.Reason, maxRejectReasonLen)
   232  		summary := fmt.Sprintf("cmd %v, code %v, reason %v", rejCommand,
   233  			msg.Code, rejReason)
   234  		if rejCommand == wire.CmdBlock || rejCommand == wire.CmdTx {
   235  			summary += fmt.Sprintf(", hash %v", msg.Hash)
   236  		}
   237  		return summary
   238  	}
   239  
   240  	// No summary for other messages.
   241  	return ""
   242  }