package service import ( "bufio" "cybertron/configs" "cybertron/constants" "cybertron/models/db" "cybertron/models/instrumentation" inPodCache "cybertron/pkg/cache" "cybertron/pkg/encoder" "cybertron/pkg/kafka/producer" "cybertron/pkg/log" "cybertron/pkg/metrics" "cybertron/pkg/utils" "encoding/json" "github.com/gin-gonic/gin" "gorm.io/gorm" "net/http" ) type ExceptionService struct { logger *log.Logger dbClient *gorm.DB kafkaProducer producer.KProducer inPodCacheClient *inPodCache.Cache kafkaConfig configs.KafkaConfig } type Frame struct { Filename string `json:"filename"` Function string `json:"function"` InApp bool `json:"in_app"` Lineno int `json:"lineno,omitempty"` Colno int `json:"colno,omitempty"` } type Stacktrace struct { Frames []Frame `json:"frames"` } type ExceptionValue struct { Type string `json:"type"` Value string `json:"value"` Stacktrace Stacktrace `json:"stacktrace"` ProjectId string `json:"project_id,omitempty"` ReleaseId string `json:"release_id,omitempty"` Breadcrumbs interface{} `json:"breadcrumbs,omitempty"` Extra interface{} `json:"extra,omitempty"` Request interface{} `json:"request,omitempty"` Contexts interface{} `json:"contexts,omitempty"` AwsAccount string `json:"aws_account,omitempty"` } type Exception struct { Values []ExceptionValue `json:"values"` } type Payload struct { Exception Exception `json:"exception"` Breadcrumbs interface{} `json:"breadcrumbs"` Request interface{} `json:"request"` Extra interface{} `json:"extra"` Contexts interface{} `json:"contexts"` } func isIgnored(value string) bool { for _, pattern := range constants.IgnorePatterns { if pattern.MatchString(value) { return true } } return false } func NewExceptionService(logger *log.Logger, dbClient *gorm.DB, kafkaProducer producer.KProducer, cache *inPodCache.Cache, kafkaConfig configs.KafkaConfig) *ExceptionService { return &ExceptionService{ logger: logger, dbClient: dbClient, kafkaProducer: kafkaProducer, inPodCacheClient: cache, kafkaConfig: kafkaConfig, } } func (exceptionService *ExceptionService) CatchErrors(c *gin.Context) { projectID := c.Param("projectId") secret := c.Query("sentry_key") var secretFromDb, found = exceptionService.inPodCacheClient.Get(projectID) var awsAccountFromDb, awsAccountFound = exceptionService.inPodCacheClient.Get(projectID + "_account") //validate project id and secret is valid if !found || !awsAccountFound { var projectData db.Project exceptionService.dbClient.First(&projectData, "project_reference_id = ?", projectID) secretFromDb = projectData.Secret awsAccountFromDb = projectData.Account exceptionService.inPodCacheClient.Set(projectID, secretFromDb) exceptionService.inPodCacheClient.Set(projectID+"_account", awsAccountFromDb) } if secret != secretFromDb { utils.ErrorResponse(c, "Unable to validate") return } scanner := bufio.NewScanner(c.Request.Body) var lines []string //error metric firing for scanner.Scan() { lines = append(lines, scanner.Text()) } var jsonData Payload //ensure we are processing a valid payload if len(lines) <= 2 { //exceptionService.logger.Error("payload is not valid", zap.Strings("payload", lines)) c.JSON(http.StatusOK, gin.H{"status": "payload is not valid"}) return } err := json.Unmarshal([]byte(lines[2]), &jsonData) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Second line is not valid JSON"}) return } metrics.PublishErrorConsumptionMetric(instrumentation.ErrorConsumptionMetric{ProjectId: projectID}) for _, errorItem := range jsonData.Exception.Values { if isIgnored(errorItem.Value) { continue } errorItem.ProjectId = projectID errorItem.Breadcrumbs = jsonData.Breadcrumbs errorItem.Extra = jsonData.Extra errorItem.Request = jsonData.Request errorItem.Contexts = jsonData.Contexts errorItem.AwsAccount = awsAccountFromDb.(string) err := exceptionService.kafkaProducer.PublishEvent(errorItem, exceptionService.kafkaConfig.Topic, "", nil, encoder.JsonEncoderInstance) if err != nil { exceptionService.logger.Error("Error publishing exception to Kafka: " + err.Error()) } } c.JSON(http.StatusOK, gin.H{"status": "success"}) }