1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-12 10:55:20 +02:00
sap-jenkins-library/pkg/telemetry/telemetry.go
ffeldmann 5f4cd838cf
Updates telemetry logging information for internal reporting (#3585)
* Add StepStartTime, Renames StepDuration, adds PiperCommithash, removes Branch, GitOwner, GitRepository from logged telemetry information

* Fixes test case for telemetry logging

* Activates step monitoring data in debug mode

* Pretty debug json printing

* Reduces log noise, setting warning to debug
2022-02-28 09:45:57 +01:00

176 lines
5.0 KiB
Go

package telemetry
import (
"crypto/sha1"
"encoding/json"
"fmt"
"github.com/SAP/jenkins-library/pkg/orchestrator"
"strconv"
"time"
"net/http"
"net/url"
piperhttp "github.com/SAP/jenkins-library/pkg/http"
"github.com/SAP/jenkins-library/pkg/log"
)
// eventType
const eventType = "library-os-ng"
// actionName
const actionName = "Piper Library OS"
// LibraryRepository that is passed into with -ldflags
var LibraryRepository string
// Telemetry struct which holds necessary infos about telemetry
type Telemetry struct {
baseData BaseData
baseMetaData BaseMetaData
data Data
provider orchestrator.OrchestratorSpecificConfigProviding
disabled bool
client *piperhttp.Client
CustomReportingDsn string
CustomReportingToken string
customClient *piperhttp.Client
BaseURL string
Endpoint string
SiteID string
}
// Initialize sets up the base telemetry data and is called in generated part of the steps
func (t *Telemetry) Initialize(telemetryDisabled bool, stepName string) {
t.disabled = telemetryDisabled
provider, err := orchestrator.NewOrchestratorSpecificConfigProvider()
if err != nil || provider == nil {
log.Entry().Warningf("could not get orchestrator config provider, leads to insufficient data")
provider = &orchestrator.UnknownOrchestratorConfigProvider{}
}
t.provider = provider
if t.client == nil {
t.client = &piperhttp.Client{}
}
t.client.SetOptions(piperhttp.ClientOptions{MaxRequestDuration: 5 * time.Second, MaxRetries: -1})
if t.BaseURL == "" {
//SWA baseURL
t.BaseURL = "https://webanalytics.cfapps.eu10.hana.ondemand.com"
}
if t.Endpoint == "" {
// SWA endpoint
t.Endpoint = "/tracker/log"
}
if len(LibraryRepository) == 0 {
LibraryRepository = "https://github.com/n/a"
}
if t.SiteID == "" {
t.SiteID = "827e8025-1e21-ae84-c3a3-3f62b70b0130"
}
t.baseData = BaseData{
Orchestrator: provider.OrchestratorType(),
StageName: provider.GetStageName(),
URL: LibraryRepository,
ActionName: actionName,
EventType: eventType,
StepName: stepName,
SiteID: t.SiteID,
PipelineURLHash: t.getPipelineURLHash(), // http://server:port/jenkins/job/foo/
BuildURLHash: t.getBuildURLHash(), // http://server:port/jenkins/job/foo/15/
}
t.baseMetaData = baseMetaData
}
func (t *Telemetry) getPipelineURLHash() string {
jobUrl := t.provider.GetJobUrl()
return t.toSha1OrNA(jobUrl)
}
func (t *Telemetry) getBuildURLHash() string {
buildUrl := t.provider.GetBuildUrl()
return t.toSha1OrNA(buildUrl)
}
func (t *Telemetry) toSha1OrNA(input string) string {
if len(input) == 0 {
return "n/a"
}
return fmt.Sprintf("%x", sha1.Sum([]byte(input)))
}
// SetData sets the custom telemetry data and base data into the Data object
func (t *Telemetry) SetData(customData *CustomData) {
t.data = Data{
BaseData: t.baseData,
BaseMetaData: t.baseMetaData,
CustomData: *customData,
}
}
// GetData returns telemetryData
func (t *Telemetry) GetData() Data {
return t.data
}
// Send telemetry information to SWA
func (t *Telemetry) Send() {
// always log step telemetry data to logfile used for internal use-case
t.logStepTelemetryData()
// skip if telemetry is disabled
if t.disabled {
return
}
request, _ := url.Parse(t.BaseURL)
request.Path = t.Endpoint
request.RawQuery = t.data.toPayloadString()
log.Entry().WithField("request", request.String()).Debug("Sending telemetry data")
t.client.SendRequest(http.MethodGet, request.String(), nil, nil, nil)
}
func (t *Telemetry) logStepTelemetryData() {
var fatalError map[string]interface{}
if t.data.CustomData.ErrorCode != "0" && log.GetFatalErrorDetail() != nil {
// retrieve the error information from the logCollector
err := json.Unmarshal(log.GetFatalErrorDetail(), &fatalError)
if err != nil {
log.Entry().WithError(err).Warn("could not unmarshal fatal error struct")
}
}
// Subtracts the duration from now to estimate the step start time
i, err := strconv.ParseInt(t.data.CustomData.Duration, 10, 64)
duration := time.Millisecond * time.Duration(i)
starTime := time.Now().UTC().Add(-duration)
stepTelemetryData := StepTelemetryData{
StepStartTime: starTime.String(),
PipelineURLHash: t.data.PipelineURLHash,
BuildURLHash: t.data.BuildURLHash,
StageName: t.data.StageName,
StepName: t.data.BaseData.StepName,
ErrorCode: t.data.CustomData.ErrorCode,
StepDuration: t.data.CustomData.Duration,
ErrorCategory: t.data.CustomData.ErrorCategory,
ErrorDetail: fatalError,
CorrelationID: t.provider.GetBuildUrl(),
PiperCommitHash: t.data.CustomData.PiperCommitHash,
}
stepTelemetryJSON, err := json.Marshal(stepTelemetryData)
if err != nil {
log.Entry().Error("could not marshal step telemetry data")
log.Entry().Infof("Step telemetry data: {n/a}")
} else {
// log step telemetry data, changes here need to change the regex in the internal piper lib
log.Entry().Infof("Step telemetry data:%v", string(stepTelemetryJSON))
}
}