github.com/mundipagg/boleto-api@v0.0.0-20230620145841-3f9ec742599f/log/log.go (about) 1 package log 2 3 import ( 4 "fmt" 5 "strings" 6 "time" 7 8 "github.com/mralves/tracer" 9 10 "github.com/mundipagg/boleto-api/config" 11 ) 12 13 const ( 14 FailGetBoletoMessage = "Falha ao recuperar Boleto" 15 SuccessGetBoletoMessage = "Boleto recuperado com sucesso" 16 ) 17 18 type LogEntry = map[string]interface{} 19 20 var logger tracer.Logger 21 22 //Operation a operacao usada na API 23 var Operation string 24 25 //Recipient o nome do banco 26 var Recipient string 27 28 //Log struct com os elementos do log 29 type Log struct { 30 Operation string 31 Recipient string 32 PayeeGuarantor string 33 RequestKey string 34 BankName string 35 IPAddress string 36 ServiceUser string 37 NossoNumero string 38 logger tracer.Logger 39 } 40 41 //Install instala o "servico" de log do SEQ 42 func Install() { 43 configureTracer() 44 logger = tracer.GetLogger("boleto") 45 } 46 47 func formatter(message string) string { 48 return "[{Application}: {Operation}] - {MessageType} " + message 49 } 50 51 //CreateLog cria uma nova instancia do Log 52 func CreateLog() *Log { 53 return &Log{ 54 logger: logger, 55 } 56 } 57 58 //Request loga o request para algum banco 59 func (l *Log) Request(content interface{}, url string, headers map[string]string) { 60 if config.Get().DisableLog { 61 return 62 } 63 64 go (func() { 65 props := l.defaultProperties("Request", content) 66 props["URL"] = url 67 68 if config.Get().MockMode { 69 props["Headers"] = headers 70 } else { 71 props["Headers"] = l.maskSecretHeaders(headers) 72 } 73 74 action := strings.Split(url, "/") 75 msg := formatter(fmt.Sprintf("to {BankName} (%s) | {Recipient}", action[len(action)-1])) 76 77 l.logger.Info(msg, props) 78 })() 79 } 80 81 //Response loga o response para algum banco 82 func (l *Log) Response(content interface{}, url string, headers map[string]string) { 83 if config.Get().DisableLog { 84 return 85 } 86 go (func() { 87 88 action := strings.Split(url, "/") 89 msg := formatter(fmt.Sprintf("from {BankName} (%s) | {Recipient}", action[len(action)-1])) 90 91 props := l.defaultProperties("Response", content) 92 props["URL"] = url 93 94 if config.Get().MockMode { 95 props["Headers"] = headers 96 } else { 97 props["Headers"] = l.maskSecretHeaders(headers) 98 } 99 100 l.logger.Info(msg, props) 101 })() 102 } 103 104 //RequestApplication loga o request que chega na boleto api 105 func (l *Log) RequestApplication(content interface{}, url string, headers map[string]string) { 106 if config.Get().DisableLog { 107 return 108 } 109 go (func() { 110 111 props := l.defaultProperties("Request", content) 112 props["Headers"] = headers 113 props["URL"] = url 114 115 msg := formatter("from {IPAddress} | {Recipient}") 116 117 l.logger.Info(msg, props) 118 })() 119 } 120 121 //ResponseApplication loga o response que sai da boleto api 122 func (l *Log) ResponseApplication(content interface{}, url string, errorCode string) { 123 if config.Get().DisableLog { 124 return 125 } 126 go (func() { 127 props := l.defaultProperties("Response", content) 128 props["URL"] = url 129 130 if errorCode != "" { 131 props["ErrorCode"] = errorCode 132 } 133 134 msg := formatter("{Operation} | {Recipient}") 135 136 l.logger.Info(msg, props) 137 })() 138 } 139 140 //ResponseApplicationFatal loga o response que sai do panic recovery 141 func (l *Log) ResponseApplicationFatal(content interface{}, url string, errorCode string) { 142 if config.Get().DisableLog { 143 return 144 } 145 go (func() { 146 props := l.defaultProperties("Response", content) 147 props["URL"] = url 148 149 if errorCode != "" { 150 props["ErrorCode"] = errorCode 151 } 152 153 msg := formatter("{Operation} | {Recipient}") 154 155 l.logger.Fatal(msg, props) 156 })() 157 } 158 159 //Info loga mensagem do level INFO 160 func (l *Log) Info(msg string) { 161 if config.Get().DisableLog { 162 return 163 } 164 go l.logger.Info(msg, nil) 165 } 166 167 // InfoWithParams cria log generico para um map 168 func (l *Log) InfoWithParams(msg, msgType string, params map[string]interface{}) { 169 if config.Get().DisableLog { 170 return 171 } 172 go (func() { 173 props := l.defaultProperties(msgType, "") 174 for k, v := range params { 175 props[k] = v 176 } 177 l.logger.Info(formatter(msg), props) 178 })() 179 } 180 181 // InfoWithBasic Cria um log de information com as informações básicas do log 182 func (l *Log) InfoWithBasic(msg, msgType string, params map[string]interface{}) { 183 if config.Get().DisableLog { 184 return 185 } 186 go (func() { 187 props := l.basicProperties(msgType) 188 for k, v := range params { 189 props[k] = v 190 } 191 l.logger.Info(formatter(msg), props) 192 })() 193 } 194 195 //Warn loga mensagem do leve Warning 196 func (l *Log) Warn(content interface{}, msg string) { 197 if config.Get().DisableLog { 198 return 199 } 200 go (func() { 201 props := l.defaultProperties("Warning", content) 202 m := formatter(msg) 203 204 l.logger.Warn(m, props) 205 })() 206 } 207 208 func (l *Log) Error(content interface{}, msg string) { 209 if config.Get().DisableLog { 210 return 211 } 212 go (func() { 213 props := l.defaultProperties("Error", content) 214 m := formatter(msg) 215 216 l.logger.Error(m, props) 217 })() 218 } 219 220 // ErrorWithBasic Cria um log de erro com as informações básicas do log 221 func (l *Log) ErrorWithBasic(msg, msgType string, err error) { 222 if config.Get().DisableLog { 223 return 224 } 225 go (func() { 226 props := l.basicProperties(msgType) 227 props["Error"] = fmt.Sprintf("%v", err) 228 l.logger.Error(formatter(msg), props) 229 })() 230 } 231 232 // FallbackErrorWithBasic Cria um log de erro com as informações básicas do log de fallback 233 func (l *Log) ErrorWithContent(msg, msgType string, err error, content interface{}) { 234 if config.Get().DisableLog { 235 return 236 } 237 go (func() { 238 props := l.defaultProperties(msgType, content) 239 props["Error"] = fmt.Sprintf("%v", err) 240 l.logger.Error(formatter(msg), props) 241 })() 242 } 243 244 // Fatal loga erros da aplicação 245 func (l *Log) Fatal(content interface{}, msg string) { 246 if config.Get().DisableLog { 247 return 248 } 249 go (func() { 250 props := l.defaultProperties("Fatal", content) 251 m := formatter(msg) 252 253 l.logger.Fatal(m, props) 254 })() 255 } 256 257 // ErrorBasicWithContent Cria um log de erro com as informações básicas e o conteúdo 258 func (l *Log) ErrorBasicWithContent(msg, msgType string, content interface{}) { 259 if config.Get().DisableLog { 260 return 261 } 262 go (func() { 263 props := l.basicProperties(msgType) 264 props["Content"] = content 265 l.logger.Error(formatter(msg), props) 266 })() 267 } 268 269 //InitRobot loga o inicio da execução do robô de recovery 270 func (l *Log) InitRobot(totalRecords int) { 271 msg := formatter("- Starting execution") 272 go func() { 273 props := defaultRobotProperties("Execute", l.Operation, "") 274 props["TotalRecords"] = totalRecords 275 logger.Info(msg, props) 276 }() 277 } 278 279 //ResumeRobot loga um resumo de Recovery do robô de recovery 280 func (l *Log) ResumeRobot(key string) { 281 msg := formatter(key) 282 go func() { 283 props := defaultRobotProperties("RecoveryBoleto", l.Operation, key) 284 props["RequestKey"] = l.RequestKey 285 logger.Info(msg, props) 286 }() 287 } 288 289 //EndRobot loga o fim da execução do robô de recovery 290 func (l *Log) EndRobot() { 291 msg := formatter("- Finishing execution") 292 go logger.Info(msg, defaultRobotProperties("Finish", l.Operation, "")) 293 } 294 295 func (l *Log) defaultProperties(messageType string, content interface{}) LogEntry { 296 props := LogEntry{ 297 "Content": content, 298 "Recipient": l.Recipient, 299 "NossoNumero": l.NossoNumero, 300 "RequestKey": l.RequestKey, 301 "BankName": l.BankName, 302 "ServiceUser": l.ServiceUser, 303 "IPAddress": l.IPAddress, 304 } 305 306 if len(l.PayeeGuarantor) > 0 { 307 props["PayeeGuarantor"] = l.PayeeGuarantor 308 } 309 310 for k, v := range l.basicProperties(messageType) { 311 props[k] = v 312 } 313 314 return props 315 } 316 317 //GetBoleto Loga mensagem de recuperação de boleto 318 func (l *Log) GetBoleto(content interface{}, msgType string) { 319 if config.Get().DisableLog { 320 return 321 } 322 go (func() { 323 props := l.getBoletoProperties(msgType, content) 324 325 switch msgType { 326 case "Warning": 327 l.logger.Warn(formatter(FailGetBoletoMessage), props) 328 case "Error": 329 l.logger.Error(formatter(FailGetBoletoMessage), props) 330 default: 331 l.logger.Info(formatter(SuccessGetBoletoMessage), props) 332 } 333 })() 334 } 335 336 //handleMaskErros Handle de erro para funções internas para garantir que, caso ocorra algum problema interno 337 //o conteúdo continuará sendo enviado, porém sem a devida máscara. 338 339 func (l *Log) HandleMaskErrors(method string, messageType string, methodContent interface{}) { 340 if r := recover(); r != nil { 341 errorDescription := fmt.Sprintf("Recovering from panic during %s operation", method) 342 logMessage := fmt.Sprintf("to {BankName} (%s) | {Recipient} - %s", messageType, errorDescription) 343 recoverError := fmt.Errorf("%v", r) 344 345 l.ErrorWithContent(logMessage, messageType, recoverError, methodContent) 346 } 347 } 348 349 func (l *Log) basicProperties(messageType string) LogEntry { 350 loc, _ := time.LoadLocation("UTC") 351 now := time.Now().In(loc) 352 353 props := LogEntry{ 354 "MessageType": messageType, 355 "Operation": l.Operation, 356 "ExecutionDate": now, 357 } 358 return props 359 } 360 361 func (l *Log) getBoletoProperties(messageType string, content interface{}) LogEntry { 362 props := LogEntry{ 363 "RequestKey": l.RequestKey, 364 "IPAddress": l.IPAddress, 365 "Content": content, 366 } 367 368 for k, v := range l.basicProperties(messageType) { 369 props[k] = v 370 } 371 372 return props 373 } 374 375 func defaultRobotProperties(msgType, op, key string) LogEntry { 376 props := LogEntry{ 377 "MessageType": msgType, 378 "Operation": op, 379 } 380 381 if key != "" { 382 props["BoletoKey"] = key 383 } 384 return props 385 } 386 387 func (l *Log) maskSecretHeaders(headers map[string]string) map[string]string { 388 secretHeaders := map[string]bool{ 389 "Authorization": true, 390 "access_token": true, 391 "itau-chave": true, 392 } 393 394 maskedHeaders := make(map[string]string) 395 396 for key, value := range headers { 397 if secretHeaders[key] { 398 maskedHeaders[key] = "[REDACTED]" 399 } else { 400 maskedHeaders[key] = value 401 } 402 } 403 404 return maskedHeaders 405 }