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  }