package log import ( "fmt" "github.com/SAP/jenkins-library/pkg/ans" "github.com/pkg/errors" "github.com/sirupsen/logrus" "os" "strings" ) // ANSHook is used to set the hook features for the logrus hook type ANSHook struct { client ans.Client eventTemplate ans.Event firing bool } // Levels returns the supported log level of the hook. func (ansHook *ANSHook) Levels() []logrus.Level { return []logrus.Level{logrus.WarnLevel, logrus.ErrorLevel, logrus.PanicLevel, logrus.FatalLevel} } // Fire creates a new event from the logrus and sends an event to the ANS backend func (ansHook *ANSHook) Fire(entry *logrus.Entry) (err error) { if ansHook.firing { return fmt.Errorf("ANS hook has already been fired") } ansHook.firing = true defer func() { ansHook.firing = false }() if len(strings.TrimSpace(entry.Message)) == 0 { return } var event ans.Event if event, err = ansHook.eventTemplate.Copy(); err != nil { return } logLevel := entry.Level event.SetSeverityAndCategory(logLevel) var stepName string if entry.Data["stepName"] != nil { stepName = fmt.Sprint(entry.Data["stepName"]) } else { stepName = "n/a" } event.Tags["pipeline:stepName"] = stepName if errorCategory := GetErrorCategory().String(); errorCategory != "undefined" { event.Tags["pipeline:errorCategory"] = errorCategory } event.EventTimestamp = entry.Time.Unix() if event.Subject == "" { event.Subject = fmt.Sprintf("Pipeline step '%s' sends '%s'", stepName, event.Severity) } event.Body = entry.Message event.Tags["pipeline:logLevel"] = logLevel.String() return ansHook.client.Send(event) } type registrationUtil interface { ans.Client registerHook(hook *ANSHook) } type registrationUtilImpl struct { ans.Client } func (u *registrationUtilImpl) registerHook(hook *ANSHook) { RegisterHook(hook) } func (u *registrationUtilImpl) registerSecret(secret string) { RegisterSecret(secret) } // RegisterANSHookIfConfigured creates a new ANS hook for logrus if it is configured and registers it func RegisterANSHookIfConfigured(correlationID string) error { return registerANSHookIfConfigured(correlationID, ®istrationUtilImpl{Client: &ans.ANS{}}) } func registerANSHookIfConfigured(correlationID string, util registrationUtil) error { ansServiceKeyJSON := os.Getenv("PIPER_ansHookServiceKey") if len(ansServiceKeyJSON) == 0 { return nil } ansServiceKey, err := ans.UnmarshallServiceKeyJSON(ansServiceKeyJSON) if err != nil { return errors.Wrap(err, "cannot initialize SAP Alert Notification Service due to faulty serviceKey json") } RegisterSecret(ansServiceKey.ClientSecret) util.SetServiceKey(ansServiceKey) if err = util.CheckCorrectSetup(); err != nil { return errors.Wrap(err, "check http request to SAP Alert Notification Service failed; not setting up the ANS hook") } eventTemplate, err := setupEventTemplate(os.Getenv("PIPER_ansEventTemplate"), correlationID) if err != nil { return err } util.registerHook(&ANSHook{ client: util, eventTemplate: eventTemplate, }) return nil } func setupEventTemplate(customerEventTemplate, correlationID string) (ans.Event, error) { event := ans.Event{ EventType: "Piper", Tags: map[string]interface{}{"ans:correlationId": correlationID, "ans:sourceEventId": correlationID}, Resource: &ans.Resource{ ResourceType: "Pipeline", ResourceName: "Pipeline", }, } if len(customerEventTemplate) > 0 { if err := event.MergeWithJSON([]byte(customerEventTemplate)); err != nil { Entry().WithField("stepName", "ANS").Warnf("provided SAP Alert Notification Service event template '%s' could not be unmarshalled: %v", customerEventTemplate, err) return ans.Event{}, errors.Wrapf(err, "provided SAP Alert Notification Service event template '%s' could not be unmarshalled", customerEventTemplate) } } if len(event.Severity) > 0 { Entry().WithField("stepName", "ANS").Warnf("event severity set to '%s' will be overwritten according to the log level", event.Severity) event.Severity = "" } if len(event.Category) > 0 { Entry().WithField("stepName", "ANS").Warnf("event category set to '%s' will be overwritten according to the log level", event.Category) event.Category = "" } if err := event.Validate(); err != nil { return ans.Event{}, errors.Wrap(err, "did not initialize SAP Alert Notification Service due to faulty event template json") } return event, nil }