internal/platform/statistics.go (130 lines of code) (raw):

/* * Copyright 2021-2024 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package platform import ( "encoding/json" "fmt" "os" "path/filepath" "runtime" "sync" "time" "github.com/JetBrains/qodana-cli/internal/cloud" "github.com/JetBrains/qodana-cli/internal/platform/strutil" "github.com/JetBrains/qodana-cli/internal/platform/thirdpartyscan" "github.com/JetBrains/qodana-cli/internal/platform/utils" "github.com/JetBrains/qodana-cli/internal/tooling" "github.com/google/uuid" log "github.com/sirupsen/logrus" ) var wg sync.WaitGroup const qodanaProjectId = "system_qdcld_project_id" func createFuserEventChannel(events *[]tooling.FuserEvent) chan tooling.FuserEvent { ch := make(chan tooling.FuserEvent) guid := uuid.New().String() go func() { for event := range ch { event.SessionId = guid *events = append(*events, event) wg.Done() } }() return ch } func sendFuserEvents( ch chan tooling.FuserEvent, events *[]tooling.FuserEvent, c thirdpartyscan.Context, deviceId string, ) { linterInfo := c.LinterInfo() mountInfo := c.MountInfo() wg.Wait() close(ch) if c.NoStatistics() { println("Statistics disabled, skipping FUS") return } if !cloud.Token.IsAllowedToSendFUS() { println("You are not allowed to send FUS") return } fatBytes, err := json.Marshal(*events) if err != nil { log.Error(fmt.Errorf("failed to marshal events to json: %w", err)) return } // create a file in temp dir fileName := filepath.Join(GetTmpResultsDir(c.ResultsDir()), "fuser.json") f, err := os.Create(fileName) if err != nil { log.Error(fmt.Errorf("failed to create file %s: %w", fileName, err)) return } defer func(f *os.File) { err := f.Close() if err != nil { log.Error(fmt.Errorf("error closing resulting FUS file: %w", err)) } }(f) _, err = f.Write(fatBytes) if err != nil { log.Error(fmt.Errorf("failed to write events to file %s: %w", fileName, err)) return } args := []string{ strutil.QuoteForWindows(mountInfo.JavaPath), "-jar", strutil.QuoteForWindows(mountInfo.Fuser), deviceId, linterInfo.ProductCode, linterInfo.LinterVersion, strutil.QuoteForWindows(fileName), } if os.Getenv("GO_TESTING") == "true" { args = append(args, "true") } _, _, _, _ = utils.LaunchAndLog(c.LogDir(), "fuser", args...) } func currentTimestamp() int64 { return time.Now().UnixNano() / int64(time.Millisecond) } func commonEventData(linterInfo thirdpartyscan.LinterInfo, projectIdHash string) map[string]string { eventData := map[string]string{"version": linterInfo.GetMajorVersion()} if projectIdHash != "" { eventData[qodanaProjectId] = projectIdHash } return eventData } func logProjectOpen(ch chan tooling.FuserEvent, linterInfo thirdpartyscan.LinterInfo, projectIdHash string) { wg.Add(1) eventData := commonEventData(linterInfo, projectIdHash) ch <- tooling.FuserEvent{ GroupId: "qd.cl.lifecycle", EventName: "project.opened", EventData: eventData, Time: currentTimestamp(), State: false, } } func logProjectClose(ch chan tooling.FuserEvent, linterInfo thirdpartyscan.LinterInfo, projectIdHash string) { wg.Add(1) eventData := commonEventData(linterInfo, projectIdHash) ch <- tooling.FuserEvent{ GroupId: "qd.cl.lifecycle", EventName: "project.closed", EventData: eventData, Time: currentTimestamp(), State: false, } } func logOs(ch chan tooling.FuserEvent, linterInfo thirdpartyscan.LinterInfo, projectIdHash string) { wg.Add(1) eventData := commonEventData(linterInfo, projectIdHash) eventData["name"] = runtime.GOOS eventData["arch"] = runtime.GOARCH ch <- tooling.FuserEvent{ GroupId: "qd.cl.system.os", EventName: "os.name", EventData: eventData, Time: currentTimestamp(), State: true, } }