Skip to content

App insights Telemetry for Commands #409

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/download-info.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Download Info

on:
push:
branches:
- main

jobs:
print-download-info:
runs-on: ubuntu-latest

steps:
- name: Check out code
uses: actions/checkout@v2

- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x' # Replace with the desired Python version

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests

- name: Get Download Info
run: |
python - <<EOF
import requests

url = "https://github.com/api/repos/microsoft/go-sqlcmd/releases"
response = requests.get(url)
data = response.json()

for release in data:
for asset in release['assets']:
browser_download_url = asset['browser_download_url']
download_count = asset['download_count']
print("Download URL:", browser_download_url)
print("Download Count:", download_count)
print()
EOF
25 changes: 21 additions & 4 deletions cmd/modern/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
package main

import (
"os"
"path"

"github.com/microsoft/go-sqlcmd/internal"
"github.com/microsoft/go-sqlcmd/internal/cmdparser"
"github.com/microsoft/go-sqlcmd/internal/cmdparser/dependency"
Expand All @@ -20,11 +23,9 @@ import (
"github.com/microsoft/go-sqlcmd/internal/output"
"github.com/microsoft/go-sqlcmd/internal/output/verbosity"
"github.com/microsoft/go-sqlcmd/internal/pal"
"github.com/microsoft/go-sqlcmd/internal/telemetry"
"github.com/microsoft/go-sqlcmd/pkg/sqlcmd"
"github.com/spf13/cobra"
"path"

"os"

legacyCmd "github.com/microsoft/go-sqlcmd/cmd/sqlcmd"
)
Expand All @@ -38,17 +39,31 @@ var version = "local-build" // overridden in pipeline builds with: -ldflags="-X
// If the first argument is a modern CLI subcommand, the modern CLI is
// executed. Otherwise, the legacy CLI is executed.
func main() {
telemetry.InitializeAppInsights()

defer telemetry.CloseTelemetry()
defer telemetry.FlushTelemetry()

dependencies := dependency.Options{
Output: output.New(output.Options{
StandardWriter: os.Stdout,
ErrorHandler: checkErr,
HintHandler: displayHints})}
HintHandler: displayHints}),
}
rootCmd = cmdparser.New[*Root](dependencies)
if isFirstArgModernCliSubCommand() {
cmdparser.Initialize(initializeCallback)
properties := map[string]string{
"sqlcmd": "modern",
}
telemetry.TrackEvent("cli", properties)
rootCmd.Execute()
} else {
initializeEnvVars()
properties := map[string]string{
"sqlcmd": "legacy",
}
telemetry.TrackEvent("cli", properties)
legacyCmd.Execute(version)
}
}
Expand Down Expand Up @@ -106,6 +121,8 @@ func isFirstArgModernCliSubCommand() (isNewCliCommand bool) {
if len(os.Args) > 1 {
if rootCmd.IsValidSubCommand(os.Args[1]) {
isNewCliCommand = true
command := os.Args[1]
telemetry.TrackCommand(command)
}
}
return
Expand Down
1 change: 0 additions & 1 deletion cmd/modern/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
// all the sub-commands, like install, query, config etc.
type Root struct {
cmdparser.Cmd

configFilename string
loggingLevel int
outputType string
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ require (
)

require (
code.cloudfoundry.org/clock v0.0.0-20180518195852-02e53af36e6c // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
Expand All @@ -38,6 +39,7 @@ require (
github.com/docker/go-units v0.5.0 // indirect
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gofrs/uuid v3.3.0+incompatible // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
Expand All @@ -49,6 +51,7 @@ require (
github.com/magiconair/properties v1.8.6 // indirect
github.com/mattn/go-runewidth v0.0.3 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/microsoft/ApplicationInsights-Go v0.4.4 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/term v0.0.0-20221120202655-abb19827d345 // indirect
github.com/morikuni/aec v1.0.0 // indirect
Expand Down
16 changes: 16 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
code.cloudfoundry.org/clock v0.0.0-20180518195852-02e53af36e6c h1:5eeuG0BHx1+DHeT3AP+ISKZ2ht1UjGhm581ljqYpVeQ=
code.cloudfoundry.org/clock v0.0.0-20180518195852-02e53af36e6c/go.mod h1:QD9Lzhd/ux6eNQVUDVRJX/RKTigpewimNYBi7ivZKY8=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 h1:8kDqDngH+DmVBiCtIjCFTGa7MBnsIOkF9IccInFEbjk=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
Expand Down Expand Up @@ -103,6 +105,7 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
Expand All @@ -115,6 +118,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofrs/uuid v3.3.0+incompatible h1:8K4tyRfvU1CYPgJsveYFQMhpFd/wXNM7iK6rR7UHz84=
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
Expand Down Expand Up @@ -198,6 +203,7 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
Expand Down Expand Up @@ -236,6 +242,8 @@ github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8Bz
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microsoft/ApplicationInsights-Go v0.4.4 h1:G4+H9WNs6ygSCe6sUyxRc2U81TI5Es90b2t/MwX5KqY=
github.com/microsoft/ApplicationInsights-Go v0.4.4/go.mod h1:fKRUseBqkw6bDiXTs3ESTiU/4YTIHsQS4W3fP2ieF4U=
github.com/microsoft/go-mssqldb v1.3.0 h1:JcPVl+acL8Z/cQcJc9zP0OkjQ+l20bco/cCDpMbmGJk=
github.com/microsoft/go-mssqldb v1.3.0/go.mod h1:lmWsjHD8XX/Txr0f8ZqgbEZSC+BZjmEQy/Ms+rLrvho=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
Expand All @@ -252,6 +260,9 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
Expand Down Expand Up @@ -328,6 +339,7 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/tedsuo/ifrit v0.0.0-20180802180643-bea94bb476cc/go.mod h1:eyZnKCc955uh98WQvzOm0dgAeLnf2O0Rz0LPoC5ze+0=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down Expand Up @@ -390,6 +402,7 @@ golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down Expand Up @@ -451,6 +464,7 @@ golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down Expand Up @@ -677,8 +691,10 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Expand Down
10 changes: 9 additions & 1 deletion internal/cmdparser/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ package cmdparser

import (
"fmt"
"strings"

"github.com/microsoft/go-sqlcmd/internal/cmdparser/dependency"
"github.com/microsoft/go-sqlcmd/internal/pal"
"strings"

"github.com/microsoft/go-sqlcmd/internal/output"
"github.com/microsoft/go-sqlcmd/internal/telemetry"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -273,6 +275,10 @@ func (c *Cmd) run(_ *cobra.Command, args []string) {
if c.options.FirstArgAlternativeForFlag.Value == nil {
panic("Must set Value")
}
// Track telemetry for the sub-command
command := c.options.Use
subCommand := args[0]
telemetry.TrackSubCommand(command, subCommand)
*c.options.FirstArgAlternativeForFlag.Value = args[0]
}
}
Expand All @@ -283,6 +289,8 @@ func (c *Cmd) run(_ *cobra.Command, args []string) {
err := c.command.Help()
c.CheckErr(err)
} else {
command := c.options.Use
telemetry.TrackSubCommand(command, "")
c.options.Run()
}
}
5 changes: 4 additions & 1 deletion internal/cmdparser/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ package cmdparser

import (
"fmt"
"testing"

"github.com/microsoft/go-sqlcmd/internal/cmdparser/dependency"
"github.com/microsoft/go-sqlcmd/internal/output"
"github.com/microsoft/go-sqlcmd/internal/telemetry"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"testing"
)

func TestCmd_run(t *testing.T) {
Expand Down Expand Up @@ -37,6 +39,7 @@ func TestCmd_run(t *testing.T) {
Usage: "name",
String: &s,
})
telemetry.SetTelemetryClientFromInstrumentationKey("")
c.DefineCommand()
c.run(nil, []string{"name-value"})
}
Expand Down
8 changes: 6 additions & 2 deletions internal/cmdparser/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@
package cmdparser

import (
"regexp"
"testing"

"github.com/microsoft/go-sqlcmd/internal"
"github.com/microsoft/go-sqlcmd/internal/buffer"
"github.com/microsoft/go-sqlcmd/internal/cmdparser/dependency"
"github.com/microsoft/go-sqlcmd/internal/config"
"github.com/microsoft/go-sqlcmd/internal/output"
"github.com/microsoft/go-sqlcmd/internal/output/verbosity"
"github.com/microsoft/go-sqlcmd/internal/telemetry"
"github.com/microsoft/go-sqlcmd/pkg/sqlcmd"
"regexp"
"testing"
)

// Test.go contains functions useful for creating compact unit tests for the
Expand Down Expand Up @@ -59,6 +61,8 @@ func TestCmd[T PtrAsReceiverWrapper[pointerType], pointerType any](args ...strin
}

func testCmd[T PtrAsReceiverWrapper[pointerType], pointerType any](args ...string) (result string, err error) {
telemetry.SetTelemetryClientFromInstrumentationKey("")

buf := buffer.NewMemoryBuffer()
defer func() { buf.Close() }()
c := New[T](dependency.Options{
Expand Down
69 changes: 69 additions & 0 deletions internal/telemetry/appinsights.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package telemetry
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ackage telemetry

it could be useful to define a method that accepts event name and properties plus a func() as input. It would run the function with a timer and add an elapsed time property to the telemetry event before posting it.

Something like

TrackTimedOperation(name string, properties map[string]string, func ())

then we can measure how long it takes to deploy things without trying to look for a "begin" event and its corresponding "end" event.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good suggestion , but i would like to add one POV here . Instead of every granular event to have start and end time , we should have these func parmater with specified events (such as deploy and create).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I don't think every operation needs an elapsed time property, just certain ones. At the lowest level, though, the function just needs to take an event name and properties. The application-level code will decide which events need to use it.


import (
"fmt"
"time"

"github.com/microsoft/ApplicationInsights-Go/appinsights"
)

var telemetryClient appinsights.TelemetryClient

func SetTelemetryClient(client appinsights.TelemetryClient) {
telemetryClient = client
}

func SetTelemetryClientFromInstrumentationKey(instrumentationKey string) {
telemetryClient = appinsights.NewTelemetryClient(instrumentationKey)
}

type Telemetry struct {
Client appinsights.TelemetryClient
}

func InitializeAppInsights() {
instrumentationKey := "3fdeec77-2951-456f-bcb2-9f003b512a3f"
config := appinsights.NewTelemetryConfiguration(instrumentationKey)
telemetryClient = appinsights.NewTelemetryClientFromConfig(config)
SetTelemetryClient(telemetryClient)
// Add a diagnostics listener for printing telemetry messages
appinsights.NewDiagnosticsMessageListener(func(msg string) error {
fmt.Printf("[%s] %s\n", time.Now().Format(time.UnixDate), msg)
return nil
})
}

// TrackCommand tracks a command execution event
func TrackCommand(command string) {
event := appinsights.NewEventTelemetry("command")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

etry("command")

Do we need a hierarchy/categorized name for events and properties so we can group things?

Eg in SSMS there are multiple events that happen to an object explorer node. The event names are / delimited like:

sql/ssms/explorerhierarchynode/setupmenuparent
                                    DataModel.Action.DurationInMilliseconds = 0
                                    SQL.SSMS.ObjectExplorer.Entity = Server/Login
                                    SQL.SSMS.ObjectExplorer.IsSqlDW = False
                                    SQL.SSMS.ObjectExplorer.ServerType = SqlAzureDatabase
sql/ssms/explorerhierarchynode/getmenuitems
                                    DataModel.Action.DurationInMilliseconds = 567.6678
                                    SQL.SSMS.ObjectExplorer.Entity = Server/Login
                                    SQL.SSMS.ObjectExplorer.IsSqlDW = False
                                    SQL.SSMS.ObjectExplorer.ServerType = SqlAzureDatabase

There are also a bunch of "ambient" properties we tried to include in most events, like the type of server OE is connected to.

In our case, I feel like command and sub-command don't need to be different events.
We can have a single command event that has a property like CommandId whose value is <commandname>/<subcommand name>
When we are querying the backend data, we can more easily filter based on the CommandId property of one event instead of searching for separate events and manually aggregating them.

Also - for "modern" commands we should log the context id (a guid) with every relevant event. That will enable us to track the lifecycle of a context. EG if the customer runs sqlcmd to create a context, there should be an event that logs the context id guid along with non-PII metadata about the context (container type, operating system of the host, version of SQL, etc). Subsequent commands using that context can then be correlated with that creation.

If the user uses an existing context for an operation, we can log a 'ConnectContext' event that has the similar metadata as the CreateContext event.

event.Properties["command"] = command
telemetryClient.Track(event)
}

// TrackSubCommand tracks a sub-command execution event
func TrackSubCommand(command, subCommand string) {
event := appinsights.NewEventTelemetry("sub-command")
event.Properties["command"] = command
event.Properties["sub-command"] = subCommand
telemetryClient.Track(event)
}

func TrackEvent(eventName string, properties map[string]string) {
event := appinsights.NewEventTelemetry(eventName)
for key, value := range properties {
event.Properties[key] = value
}
telemetryClient.Track(event)
}

func CloseTelemetry() {
telemetryClient.Channel().Close()
}
func FlushTelemetry() {
telemetryClient.Channel().Flush()
}

func LogMessage(msg string) error {
fmt.Printf("[%s] %s\n", time.Now().Format(time.UnixDate), msg)
return nil
}