github.com/snowflakedb/gosnowflake@v1.9.0/telemetry.go (about) 1 // Copyright (c) 2021-2022 Snowflake Computing Inc. All rights reserved. 2 3 package gosnowflake 4 5 import ( 6 "context" 7 "encoding/json" 8 "fmt" 9 "net/http" 10 "sync" 11 "time" 12 ) 13 14 const ( 15 telemetryPath = "/telemetry/send" 16 defaultTelemetryTimeout = 10 * time.Second 17 defaultFlushSize = 100 18 ) 19 20 const ( 21 typeKey = "type" 22 sourceKey = "source" 23 queryIDKey = "QueryID" 24 driverTypeKey = "DriverType" 25 driverVersionKey = "DriverVersion" 26 sqlStateKey = "SQLState" 27 reasonKey = "reason" 28 errorNumberKey = "ErrorNumber" 29 stacktraceKey = "Stacktrace" 30 ) 31 32 const ( 33 telemetrySource = "golang_driver" 34 sqlException = "client_sql_exception" 35 connectionParameters = "client_connection_parameters" 36 ) 37 38 type telemetryData struct { 39 Timestamp int64 `json:"timestamp,omitempty"` 40 Message map[string]string `json:"message,omitempty"` 41 } 42 43 type snowflakeTelemetry struct { 44 logs []*telemetryData 45 flushSize int 46 sr *snowflakeRestful 47 mutex *sync.Mutex 48 enabled bool 49 } 50 51 func (st *snowflakeTelemetry) addLog(data *telemetryData) error { 52 if !st.enabled { 53 return fmt.Errorf("telemetry disabled; not adding log") 54 } 55 st.mutex.Lock() 56 st.logs = append(st.logs, data) 57 st.mutex.Unlock() 58 if len(st.logs) >= st.flushSize { 59 if err := st.sendBatch(); err != nil { 60 return err 61 } 62 } 63 return nil 64 } 65 66 func (st *snowflakeTelemetry) sendBatch() error { 67 if !st.enabled { 68 err := fmt.Errorf("telemetry disabled; not sending log") 69 logger.Debug(err) 70 return err 71 } 72 type telemetry struct { 73 Logs []*telemetryData `json:"logs"` 74 } 75 76 st.mutex.Lock() 77 logsToSend := st.logs 78 st.logs = make([]*telemetryData, 0) 79 st.mutex.Unlock() 80 81 if len(logsToSend) == 0 { 82 logger.Debug("nothing to send to telemetry") 83 return nil 84 } 85 86 s := &telemetry{logsToSend} 87 body, err := json.Marshal(s) 88 if err != nil { 89 return err 90 } 91 logger.Debugf("sending %v logs to telemetry. inband telemetry payload "+ 92 "being sent: %v", len(logsToSend), string(body)) 93 94 headers := getHeaders() 95 if token, _, _ := st.sr.TokenAccessor.GetTokens(); token != "" { 96 headers[headerAuthorizationKey] = fmt.Sprintf(headerSnowflakeToken, token) 97 } 98 resp, err := st.sr.FuncPost(context.Background(), st.sr, 99 st.sr.getFullURL(telemetryPath, nil), headers, body, 100 defaultTelemetryTimeout, defaultTimeProvider, nil) 101 if err != nil { 102 logger.Info("failed to upload metrics to telemetry. err: %v", err) 103 return err 104 } 105 defer resp.Body.Close() 106 if resp.StatusCode != http.StatusOK { 107 err = fmt.Errorf("non-successful response from telemetry server: %v. "+ 108 "disabling telemetry", resp.StatusCode) 109 logger.Info(err) 110 st.enabled = false 111 return err 112 } 113 var respd telemetryResponse 114 if err = json.NewDecoder(resp.Body).Decode(&respd); err != nil { 115 logger.Info(err) 116 st.enabled = false 117 return err 118 } 119 if !respd.Success { 120 err = fmt.Errorf("telemetry send failed with error code: %v, message: %v", 121 respd.Code, respd.Message) 122 logger.Info(err) 123 st.enabled = false 124 return err 125 } 126 logger.Debug("successfully uploaded metrics to telemetry") 127 return nil 128 }