func RunWithServerLogging(action string, params InputParams, projectPointer *string,
	function func() (Loggable, error)) error {
	l := NewLoggingServiceLogger(action, params)
	_, err := l.runWithServerLogging(function, projectPointer)
	return err
}
RunWithServerLogging runs the function with server logging
package service
import (
	"bytes"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"runtime/debug"
	"strings"
	"sync"
	"time"
	"github.com/google/uuid"
	"github.com/minio/highwayhash"
	"github.com/GoogleCloudPlatform/compute-image-tools/cli_tools/common/utils/daisyutils"
	"github.com/GoogleCloudPlatform/compute-image-tools/daisy"
	"github.com/GoogleCloudPlatform/compute-image-tools/proto/go/pb"
)
var (
	httpClient            httpClientInterface = &http.Client{Timeout: 5 * time.Second}
	serverURL                                 = deinterleave(serverURLProdP1, serverURLProdP2)
	key                                       = deinterleave(keyP1, keyP2)
	serverLogEnabled                          = true
	logMutex                                  = sync.Mutex{}
	nextRequestWaitMillis int64
)
const (
	ImageImportAction        = "ImageImport"
	ImageExportAction        = "ImageExport"
	InstanceImportAction     = "InstanceImport"
	MachineImageImportAction = "MachineImageImport"
	OneStepImageImportAction = "OneStepImageImport"
	WindowsUpgrade           = "WindowsUpgrade"
	InstanceExportAction     = "InstanceExport"
	MachineImageExportAction = "MachineImageExport"
	serverURLProdP1 = "hts/frbslgigp.ogepscmv/ieo/eaylg"
	serverURLProdP2 = "tp:/ieaeogn-agolai.o/1frlglgc/o"
	keyP1           = "AzSCO1066k_gFH2sJg3I"
	keyP2           = "IaymztUIWu9U8THBeTx"
	targetSizeGb          = "target-size-gb"
	sourceSizeGb          = "source-size-gb"
	importFileFormat      = "import-file-format"
	inflationType         = "inflation-type"
	inflationTime         = "inflation-time"
	shadowInflationTime   = "shadow-inflation-time"
	shadowDiskMatchResult = "shadow-disk-match-result"
	isUEFICompatibleImage = "is-uefi-compatible-image"
	isUEFIDetected        = "is-uefi-detected"
	uefiBootable          = "uefi-bootable"
	biosBootable          = "bios-bootable"
	rootFS                = "root-fs"
	statusStart   = "Start"
	statusSuccess = "Success"
	statusFailure = "Failure"
)
type logResult string
const (
	failedOnCreateRequest          logResult = "FailedOnCreateRequest"
	failedOnCreateRequestJSON      logResult = "FailedOnCreateRequestJSON"
	failedToParseResponse          logResult = "FailedToParseResponse"
	failedAfterRetry               logResult = "FailedAfterRetry"
	failedOnUndefinedResponse      logResult = "FailedOnUndefinedResponse"
	failedOnMissingResponseDetails logResult = "FailedOnMissingResponseDetails"
	serverLogDisabled              logResult = "ServerLogDisabled"
)
type httpClientInterface interface {
	Do(req *http.Request) (*http.Response, error)
}
func deinterleave(p1, p2 string) string {
	l1 := len(p1)
	l2 := len(p2)
	if l1 != l2 && l1 != l2+1 {
		panic("Failed to prepare required data for the tool.")
	}
	strBytes := make([]byte, len(p1)+len(p2))
	for i := range p1 {
		strBytes[i*2] = p1[i]
	}
	for i := range p2 {
		strBytes[i*2+1] = p2[i]
	}
	return string(strBytes)
}
type Logger struct {
	ServerURL string
	ID        string
	Action    string
	TimeStart time.Time
	Params    InputParams
	mutex     sync.Mutex
}
func NewLoggingServiceLogger(action string, params InputParams) *Logger {
	return &Logger{
		ServerURL: serverURL,
		ID:        uuid.New().String(),
		Action:    action,
		TimeStart: time.Now(),
		Params:    params,
		mutex:     sync.Mutex{},
	}
}
func (l *Logger) logStart() (*ComputeImageToolsLogExtension, logResult) {
	logExtension := l.createComputeImageToolsLogExtension(statusStart, nil)
	return logExtension, l.sendLogToServer(logExtension)
}
func (l *Logger) logSuccess(loggable Loggable) (*ComputeImageToolsLogExtension, logResult) {
	logExtension := l.createComputeImageToolsLogExtension(statusSuccess, l.getOutputInfo(loggable, nil))
	return logExtension, l.sendLogToServer(logExtension)
}
func (l *Logger) logFailure(err error, loggable Loggable) (*ComputeImageToolsLogExtension, logResult) {
	logExtension := l.createComputeImageToolsLogExtension(statusFailure, l.getOutputInfo(loggable, err))
	return logExtension, l.sendLogToServer(logExtension)
}
func (l *Logger) createComputeImageToolsLogExtension(status string, outputInfo *OutputInfo) *ComputeImageToolsLogExtension {
	l.mutex.Lock()
	defer l.mutex.Unlock()
	return &ComputeImageToolsLogExtension{
		ID:            l.ID,
		CloudBuildID:  os.Getenv(daisyutils.BuildIDOSEnvVarName),
		ToolAction:    l.Action,
		Status:        status,
		ElapsedTimeMs: time.Since(l.TimeStart).Nanoseconds() / 1e6,
		EventTimeMs:   time.Now().UnixNano() / 1e6,
		InputParams:   &l.Params,
		OutputInfo:    outputInfo,
	}
}
func getFailureReason(err error) string {
	return daisyutils.RemovePrivacyLogTag(err.Error())
}
func getAnonymizedFailureReason(err error) string {
	derr := daisy.ToDError(err)
	if derr == nil {
		return ""
	}
	anonymizedErrs := []string{}
	for _, m := range derr.AnonymizedErrs() {
		anonymizedErrs = append(anonymizedErrs, daisyutils.RemovePrivacyLogInfo(m))
	}
	return strings.Join(anonymizedErrs, "\n")
}
func (l *Logger) getOutputInfo(loggable Loggable, err error) *OutputInfo {
	o := OutputInfo{}
	if loggable != nil {
		o.TargetsSizeGb = loggable.GetValueAsInt64Slice(targetSizeGb)
		o.SourcesSizeGb = loggable.GetValueAsInt64Slice(sourceSizeGb)
		o.ImportFileFormat = loggable.GetValue(importFileFormat)
		o.InflationType = loggable.GetValue(inflationType)
		o.InflationTime = loggable.GetValueAsInt64Slice(inflationTime)
		o.ShadowInflationTime = loggable.GetValueAsInt64Slice(shadowInflationTime)
		o.ShadowDiskMatchResult = loggable.GetValue(shadowDiskMatchResult)
		o.IsUEFICompatibleImage = loggable.GetValueAsBool(isUEFICompatibleImage)
		o.IsUEFIDetected = loggable.GetValueAsBool(isUEFIDetected)
		if l.Params.ImageImportParams != nil {
			l.Params.ImageImportParams.InspectionResults = loggable.GetInspectionResults()
		}
	}
	if err != nil {
		o.FailureMessage = getFailureReason(err)
		o.FailureMessageWithoutPrivacyInfo = getAnonymizedFailureReason(err)
		if loggable != nil {
			o.SerialOutputs = loggable.ReadSerialPortLogs()
		}
	}
	return &o
}
func (l *Logger) runWithServerLogging(function func() (Loggable, error),
	projectPointer *string) (*ComputeImageToolsLogExtension, error) {
	var logExtension *ComputeImageToolsLogExtension
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		l.logStart()
	}()
	loggable, err := runWithRecovery(function)
	l.updateParams(projectPointer)
	if err != nil {
		wg.Add(1)
		go func() {
			defer wg.Done()
			logExtension, _ = l.logFailure(err, loggable)
			log.Println(removeNewLinesFromMultilineError(logExtension.OutputInfo.FailureMessage))
		}()
	} else {
		wg.Add(1)
		go func() {
			defer wg.Done()
			logExtension, _ = l.logSuccess(loggable)
		}()
	}
	wg.Wait()
	return logExtension, err
}
func removeNewLinesFromMultilineError(s string) string {
	firstNewLineRemoved := strings.Replace(s, "\n", " ", 1)
	return strings.ReplaceAll(firstNewLineRemoved, "\n", "; ")
}