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  }