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