Skip to content
Snippets Groups Projects
logs.go 5.23 KiB
Newer Older
Francé Wilke's avatar
Francé Wilke committed
package logs

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"runtime"
	"strings"

	"github.com/MindscapeHQ/raygun4go"

	"github.com/aws/aws-lambda-go/events"
	log "github.com/sirupsen/logrus"
)

var logger *log.Entry

var apiRequest *events.APIGatewayProxyRequest
var currentRequestID *string
var isDebug = false
var build string
var raygunClient *raygun4go.Client

// TODO
// Sensitive word filtering

func InitLogs(requestID *string, isDebugBuild bool, buildVersion string, request *events.APIGatewayProxyRequest, client *raygun4go.Client) {
	currentRequestID = requestID
	apiRequest = request
	isDebug = isDebugBuild
	build = buildVersion
	raygunClient = client

	if isDebugBuild {
		log.SetReportCaller(true)
		log.SetFormatter(&log.TextFormatter{
			ForceColors:      true,
			PadLevelText:     true,
			DisableTimestamp: true,
			CallerPrettyfier: func(f *runtime.Frame) (string, string) {
				// Exclude the caller, will rather be added as a field
				return "", ""
			},
		})
	} else {
		log.SetReportCaller(true)
		log.SetFormatter(&log.JSONFormatter{
			CallerPrettyfier: func(f *runtime.Frame) (string, string) {
				// Exclude the caller, will rather be added as a field
				return "", ""
			}})
	}
	log.SetLevel(LogLevel())

	val, exists := os.LookupEnv("DEBUGGING")
	if exists && val == "true" {
		log.SetLevel(log.TraceLevel)
		log.SetReportCaller(true)
	}

	logger = log.WithFields(log.Fields{
		"environment": getEnvironment(),
	})

	if requestID != nil {
		logger = log.WithFields(log.Fields{
			"request_id": *requestID,
		})
	}
}

func LogLevel() log.Level {
	logLevelString := os.Getenv("LOG_LEVEL")

	logLevel := log.InfoLevel

	if logLevelString != "" {
		logLevelString = strings.ToLower(logLevelString)

		switch logLevelString {
		case "error":
			logLevel = log.ErrorLevel
		case "warn":
			logLevel = log.WarnLevel
		case "info":
			logLevel = log.InfoLevel
		case "debug":
			logLevel = log.DebugLevel
		}
		log.SetLevel(logLevel)
	}
	return logLevel
}

func getEnvironment() string {
	environment := os.Getenv("ENVIRONMENT")
	if environment == "" {
		environment = "dev"
		os.Setenv("ENVIRONMENT", "dev")
	}
	return environment
}

func getLogger() *log.Entry {
	if logger == nil {
		logger = log.WithFields(log.Fields{
			"environment": getEnvironment(),
		})
	}

	return logger
}

func InfoWithFields(fields map[string]interface{}, message interface{}) {
	getLogger().WithFields(fields).Info(message)
}

func Info(format string, a ...interface{}) {
	getLogger().Info(fmt.Sprintf(format, a...))
}

func ErrorWithFields(fields map[string]interface{}, err error) {
	sendRaygunError(fields, err)
	getLogger().WithFields(fields).Error(err)
}

func ErrorWithMsg(message string, err error) {
	if err == nil {
		err = errors.New(message)
	}
	ErrorWithFields(map[string]interface{}{
		"message": message,
	}, err)
}

func ErrorMsg(message string) {
	ErrorWithMsg(message, nil)
}

func Warn(format string, a ...interface{}) {
	getLogger().Warn(fmt.Sprintf(format, a...))
}

func WarnWithFields(fields map[string]interface{}, err error) {
	getLogger().WithFields(fields).Warn(err)
}

func SQLDebugInfo(sql string) {
	getLogger().WithFields(map[string]interface{}{
		"sql": sql,
	}).Debug("SQL query")
}

func LogShipmentID(id int64) {
	InfoWithFields(map[string]interface{}{
		"shipment_id": id,
	}, "Current-shipment-ID")
}

func LogRequestInfo(req events.APIGatewayProxyRequest) {
	InfoWithFields(map[string]interface{}{
		"http_method":                req.HTTPMethod,
		"path":                       req.Path,
		"api_gateway_request_id":     req.RequestContext.RequestID,
		"user_cognito_auth_provider": req.RequestContext.Identity.CognitoAuthenticationProvider,
		"user_arn":                   req.RequestContext.Identity.UserArn,
	}, "Request Info start")
}

func LogApiAudit(fields log.Fields) {
	getLogger().WithFields(fields).Info("api-audit-log")
}

func LogSQSEvent(event events.SQSEvent) {
	InfoWithFields(map[string]interface{}{
		"records": event.Records,
	}, "SQS event start")
}

func SetOutputToFile(file *os.File) {
	log.SetOutput(file)
}

func ClearInfo() {
	logger = nil
}

func sendRaygunError(fields map[string]interface{}, errToSend error) {
	if isDebug || raygunClient == nil {
		// Don't log raygun errors on debug
		return
	}

	env := getEnvironment()

	tags := []string{env}
	raygunClient.Version(build)
	tags = append(tags, build)

	if apiRequest != nil {
		methodAndPath := apiRequest.HTTPMethod + ": " + apiRequest.Path
		tags = append(tags, methodAndPath)
		fields["body"] = apiRequest.Body
		fields["query"] = apiRequest.QueryStringParameters
		fields["identity"] = apiRequest.RequestContext.Identity
	}

	raygunClient.Tags(tags)
	if currentRequestID != nil {
		fields["request_id"] = currentRequestID
	}

	fields["env"] = env
	raygunClient.CustomData(fields)
	raygunClient.Request(fakeHttpRequest())

	if errToSend == nil {
		errToSend = errors.New("")
	}
	err := raygunClient.SendError(errToSend)
	if err != nil {
		log.Println("Failed to send raygun error:", err.Error())
	}
}

func fakeHttpRequest() *http.Request {
	if apiRequest == nil {
		return nil
	}

	requestURL := url.URL{
		Path: apiRequest.Path,
		Host: apiRequest.Headers["Host"],
	}
	request := http.Request{
		Method: apiRequest.HTTPMethod,
		URL:    &requestURL,
		Header: apiRequest.MultiValueHeaders,
	}
	return &request
}