azkustodata/client_details.go (102 lines of code) (raw):
package azkustodata
import (
"fmt"
"github.com/Azure/azure-kusto-go/azkustodata/internal/version"
"github.com/Azure/azure-kusto-go/azkustodata/utils"
"os"
"os/user"
"path/filepath"
"regexp"
"runtime"
"strings"
"github.com/samber/lo"
)
type ClientDetails struct {
// applicationForTracing is the name of the application that is using the client.
applicationForTracing string
// userNameForTracing is the name of the user that is using the client.
userNameForTracing string
// clientVersionForTracing is the version of the client.
clientVersionForTracing string
}
func NewClientDetails(applicationForTracing string, userNameForTracing string) *ClientDetails {
return &ClientDetails{applicationForTracing: applicationForTracing, userNameForTracing: userNameForTracing}
}
type StringPair struct {
Key string
Value string
}
const NONE = "[none]"
var defaultTracingValuesOnce = utils.NewOnceWithInit[ClientDetails](func() (ClientDetails, error) {
return ClientDetails{
applicationForTracing: filepath.Base(os.Args[0]),
userNameForTracing: getOsUser(),
clientVersionForTracing: buildHeaderFormat(StringPair{Key: "Kusto.Go.Client", Value: version.Kusto}, StringPair{Key: "Runtime.Go", Value: runtime.Version()}),
}, nil
})
func getOsUser() string {
var final string
current, err := user.Current()
if err == nil && current.Username != "" {
final = current.Username
} else {
// get from env and try domain too
final = os.Getenv("USERNAME")
domain := os.Getenv("USERDOMAIN")
if !isEmpty(domain) && !isEmpty(final) {
final = domain + "\\" + final
}
}
if isEmpty(final) {
final = NONE
}
return final
}
var escapeRegex = regexp.MustCompile("[\\r\\n\\s{}|]+")
func escape(s string) string {
return "{" + escapeRegex.ReplaceAllString(s, "_") + "}"
}
func defaultTracingValues() ClientDetails {
r, _ := defaultTracingValuesOnce.DoWithInit()
return r
}
func (c *ClientDetails) ApplicationForTracing() string {
if c.applicationForTracing == "" {
return defaultTracingValues().applicationForTracing
}
return c.applicationForTracing
}
func (c *ClientDetails) UserNameForTracing() string {
if c.userNameForTracing == "" {
return defaultTracingValues().userNameForTracing
}
return c.userNameForTracing
}
func (c *ClientDetails) ClientVersionForTracing() string {
return defaultTracingValues().clientVersionForTracing
}
func buildHeaderFormat(args ...StringPair) string {
return strings.Join(lo.Map(args, func(arg StringPair, _ int) string {
return fmt.Sprintf("%s:%s", arg.Key, escape(arg.Value))
}), "|")
}
func setConnectorDetails(name, version, appName, appVersion string, sendUser bool, overrideUser string, additionalFields ...StringPair) (string, string) {
var additionalFieldsList []StringPair
additionalFieldsList = append(additionalFieldsList, StringPair{Key: "Kusto." + name, Value: version})
if appName == "" {
appName = defaultTracingValues().applicationForTracing
}
if appVersion == "" {
appVersion = NONE
}
additionalFieldsList = append(additionalFieldsList, StringPair{Key: "App.{" + appName + "}", Value: appVersion})
if additionalFields != nil {
additionalFieldsList = append(additionalFieldsList, additionalFields...)
}
app := buildHeaderFormat(additionalFieldsList...)
user := NONE
if sendUser {
if overrideUser != "" {
user = overrideUser
} else {
user = defaultTracingValues().userNameForTracing
}
}
return app, user
}