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 }