Skip to content

Commit

Permalink
support telemetry (#122)
Browse files Browse the repository at this point in the history
  • Loading branch information
ms-henglu authored Dec 17, 2024
1 parent 00ca186 commit a9dede1
Show file tree
Hide file tree
Showing 21 changed files with 401 additions and 18 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## v2.2.0 (unreleased)

FEATURES:
- Support collecting telemetry data.

## v2.1.0

FEATURES:
Expand Down
15 changes: 15 additions & 0 deletions internal/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"github.com/Azure/azapi-lsp/internal/filesystem"
"github.com/Azure/azapi-lsp/internal/langserver/diagnostics"
"github.com/Azure/azapi-lsp/internal/langserver/session"
"github.com/Azure/azapi-lsp/internal/telemetry"
)

type contextKey struct {
Expand All @@ -20,6 +21,7 @@ var (
ctxDiagsNotifier = &contextKey{"diagnostics notifier"}
ctxLsVersion = &contextKey{"language server version"}
ctxClientCaller = &contextKey{Name: "client caller"}
ctxTelemetry = &contextKey{"telemetry"}
)

func missingContextErr(ctxKey *contextKey) *MissingContextErr {
Expand Down Expand Up @@ -89,3 +91,16 @@ func ClientNotifier(ctx context.Context) (session.ClientNotifier, error) {

return clientNotifier, nil
}

func WithTelemetry(ctx context.Context, telemetry telemetry.Sender) context.Context {
return context.WithValue(ctx, ctxTelemetry, telemetry)
}

func Telemetry(ctx context.Context) (telemetry.Sender, error) {
tel, ok := ctx.Value(ctxTelemetry).(telemetry.Sender)
if !ok {
return nil, missingContextErr(ctxTelemetry)
}

return tel, nil
}
24 changes: 23 additions & 1 deletion internal/langserver/handlers/command/arm_template_converter.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package command

import (
"context"
"encoding/json"
"fmt"
"strings"

"github.com/Azure/azapi-lsp/internal/telemetry"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclwrite"
Expand Down Expand Up @@ -69,7 +71,7 @@ func (c *Context) String() string {
return result
}

func convertARMTemplate(input string) (string, error) {
func convertARMTemplate(ctx context.Context, input string, telemetrySender telemetry.Sender) (string, error) {
var model ARMTemplateModel
err := json.Unmarshal([]byte(input), &model)
if err != nil {
Expand All @@ -94,7 +96,17 @@ func convertARMTemplate(input string) (string, error) {
c.AppendBlock(varBlock)
}

typeSet := make(map[string]bool)
for _, resource := range model.Resources {
typeValue := ""
if resource["type"] != nil {
typeValue = resource["type"].(string)
}
if resource["apiVersion"] != nil {
typeValue = fmt.Sprintf("%s@%s", typeValue, resource["apiVersion"].(string))
}
typeSet[typeValue] = true

res := flattenARMExpression(resource)
data, err := json.MarshalIndent(res, "", " ")
if err != nil {
Expand All @@ -111,6 +123,16 @@ func convertARMTemplate(input string) (string, error) {
c.AppendBlock(resourceBlock)
}

types := make([]string, 0)
for t := range typeSet {
types = append(types, t)
}
telemetrySender.SendEvent(ctx, "ConvertJsonToAzapi", map[string]interface{}{
"status": "completed",
"kind": "arm-template",
"type": strings.Join(types, ","),
})

return c.String(), nil
}

Expand Down
31 changes: 31 additions & 0 deletions internal/langserver/handlers/command/aztfmigrate_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
lsctx "github.com/Azure/azapi-lsp/internal/context"
ilsp "github.com/Azure/azapi-lsp/internal/lsp"
lsp "github.com/Azure/azapi-lsp/internal/protocol"
"github.com/Azure/azapi-lsp/internal/utils"
"github.com/Azure/aztfmigrate/azurerm"
"github.com/Azure/aztfmigrate/tf"
"github.com/Azure/aztfmigrate/types"
Expand Down Expand Up @@ -43,6 +44,11 @@ func (c AztfMigrateCommand) Handle(ctx context.Context, arguments []json.RawMess
}
}

telemetrySender, err := context2.Telemetry(ctx)
if err != nil {
return nil, err
}

clientCaller, err := context2.ClientCaller(ctx)
if err != nil {
return nil, err
Expand All @@ -53,6 +59,9 @@ func (c AztfMigrateCommand) Handle(ctx context.Context, arguments []json.RawMess
return nil, err
}

telemetrySender.SendEvent(ctx, "aztfmigrate", map[string]interface{}{
"status": "started",
})
reportProgress(ctx, "Parsing Terraform configurations...", 0)
defer reportProgress(ctx, "Migration completed.", 100)

Expand Down Expand Up @@ -173,6 +182,9 @@ func (c AztfMigrateCommand) Handle(ctx context.Context, arguments []json.RawMess

allResources := types.ListResourcesFromPlan(plan)
resources := make([]types.AzureResource, 0)

srcAzapiTypes := make(map[string]bool)
srcAzurermTypes := make(map[string]bool)
for _, r := range allResources {
if syntaxBlockMap[r.OldAddress(nil)] == nil {
continue
Expand All @@ -199,11 +211,15 @@ func (c AztfMigrateCommand) Handle(ctx context.Context, arguments []json.RawMess
resource.ResourceType = resourceTypes[0]
resources = append(resources, resource)

azureResourceType := utils.GetResourceType(resourceId)
srcAzapiTypes[azureResourceType] = true

case *types.AzurermResource:
if len(resource.Instances) == 0 {
continue
}
resources = append(resources, resource)
srcAzurermTypes[resource.OldResourceType] = true
}
}

Expand Down Expand Up @@ -311,6 +327,21 @@ func (c AztfMigrateCommand) Handle(ctx context.Context, arguments []json.RawMess
},
})

azapiTypes := make([]string, 0)
for t := range srcAzapiTypes {
azapiTypes = append(azapiTypes, t)
}
azurermTypes := make([]string, 0)
for t := range srcAzurermTypes {
azurermTypes = append(azurermTypes, t)
}
telemetrySender.SendEvent(ctx, "aztfmigrate", map[string]interface{}{
"status": "completed",
"count": fmt.Sprintf("%d", len(resources)),
"azapi": strings.Join(azapiTypes, ","),
"azurerm": strings.Join(azurermTypes, ","),
})

return nil, nil
}

Expand Down
31 changes: 28 additions & 3 deletions internal/langserver/handlers/command/convert_json_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"encoding/json"
"fmt"

lsctx "github.com/Azure/azapi-lsp/internal/context"
pluralize "github.com/gertd/go-pluralize"
)

Expand All @@ -25,21 +27,44 @@ func (c ConvertJsonCommand) Handle(ctx context.Context, arguments []json.RawMess
return nil, nil
}

telemetrySender, err := lsctx.Telemetry(ctx)
if err != nil {
return nil, err
}

var model map[string]interface{}
err := json.Unmarshal([]byte(content), &model)
err = json.Unmarshal([]byte(content), &model)
if err != nil {
return nil, fmt.Errorf("unable to unmarshal JSON content: %w", err)
}

result := ""
if model["$schema"] != nil {
result, err = convertARMTemplate(content)
telemetrySender.SendEvent(ctx, "ConvertJsonToAzapi", map[string]interface{}{
"status": "started",
"kind": "arm-template",
})
result, err = convertARMTemplate(ctx, content, telemetrySender)
if err != nil {
telemetrySender.SendEvent(ctx, "ConvertJsonToAzapi", map[string]interface{}{
"status": "failed",
"kind": "arm-template",
"error": err.Error(),
})
return nil, err
}
} else {
result, err = convertResourceJson(content)
telemetrySender.SendEvent(ctx, "ConvertJsonToAzapi", map[string]interface{}{
"status": "started",
"kind": "resource-json",
})
result, err = convertResourceJson(ctx, content, telemetrySender)
if err != nil {
telemetrySender.SendEvent(ctx, "ConvertJsonToAzapi", map[string]interface{}{
"status": "failed",
"kind": "resource-json",
"error": err.Error(),
})
return nil, err
}
}
Expand Down
10 changes: 9 additions & 1 deletion internal/langserver/handlers/command/resource_json_converter.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package command

import (
"context"
"encoding/json"
"fmt"
"log"
"strings"

"github.com/Azure/azapi-lsp/internal/azure"
"github.com/Azure/azapi-lsp/internal/azure/types"
"github.com/Azure/azapi-lsp/internal/telemetry"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclwrite"
Expand All @@ -29,7 +31,7 @@ type identityModel struct {
UserAssignedIdentities map[string]interface{} `json:"userAssignedIdentities"`
}

func convertResourceJson(input string) (string, error) {
func convertResourceJson(ctx context.Context, input string, telemetrySender telemetry.Sender) (string, error) {
var model resourceModel
err := json.Unmarshal([]byte(input), &model)
if err != nil {
Expand All @@ -52,6 +54,12 @@ func convertResourceJson(input string) (string, error) {
apiVersion = parts[1]
}

telemetrySender.SendEvent(ctx, "ConvertJsonToAzapi", map[string]interface{}{
"status": "completed",
"kind": "resource-json",
"type": typeValue,
})

importBlock := hclwrite.NewBlock("import", nil)
importBlock.Body().SetAttributeValue("id", cty.StringVal(fmt.Sprintf("%s?api-version=%s", model.ID, apiVersion)))
importBlock.Body().SetAttributeTraversal("to", hcl.Traversal{hcl.TraverseRoot{Name: "azapi_resource"}, hcl.TraverseAttr{Name: label}})
Expand Down
30 changes: 30 additions & 0 deletions internal/langserver/handlers/command/telemetry_command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package command

import (
"context"
"encoding/json"
"fmt"
lsctx "github.com/Azure/azapi-lsp/internal/context"
lsp "github.com/Azure/azapi-lsp/internal/protocol"
)

type TelemetryCommand struct {
}

func (t TelemetryCommand) Handle(ctx context.Context, arguments []json.RawMessage) (interface{}, error) {
telemetrySender, err := lsctx.Telemetry(ctx)
if err != nil {
return nil, err
}

var params lsp.TelemetryEvent
if len(arguments) != 0 {
err := json.Unmarshal(arguments[0], &params)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal arguments: %w", err)
}
}

telemetrySender.SendEvent(ctx, params.Name, params.Properties)
return nil, nil
}
9 changes: 8 additions & 1 deletion internal/langserver/handlers/complete.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ func (svc *service) TextDocumentComplete(ctx context.Context, params lsp.Complet
IsIncomplete: false,
Items: complete.CandidatesAtPos(data, doc.Filename(), fPos.Position(), svc.logger),
}
svc.logger.Printf("received candidates: %#v", candidates)

if len(candidates.Items) > 0 {
telemetrySender, err := lsctx.Telemetry(ctx)
if err != nil {
return candidates, err
}
telemetrySender.SendEvent(ctx, "textDocument/completion", nil)
}
return candidates, err
}
4 changes: 3 additions & 1 deletion internal/langserver/handlers/execute_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@ var handlerMap = map[string]command.CommandHandler{}

const CommandConvertJsonToAzapi = "azapi.convertJsonToAzapi"
const CommandAztfMigrate = "azapi.aztfmigrate"
const CommandTelemetry = "azapi.telemetry"

func availableCommands() []string {
return []string{CommandConvertJsonToAzapi, CommandAztfMigrate}
return []string{CommandConvertJsonToAzapi, CommandAztfMigrate, CommandTelemetry}
}

func init() {
handlerMap = make(map[string]command.CommandHandler)
handlerMap[CommandConvertJsonToAzapi] = command.ConvertJsonCommand{}
handlerMap[CommandAztfMigrate] = command.AztfMigrateCommand{}
handlerMap[CommandTelemetry] = command.TelemetryCommand{}
}

func (svc *service) WorkspaceExecuteCommand(ctx context.Context, params lsp.ExecuteCommandParams) (interface{}, error) {
Expand Down
3 changes: 2 additions & 1 deletion internal/langserver/handlers/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ func initializeResponse(t *testing.T, commandPrefix string) string {
"executeCommandProvider": {
"commands": [
"azapi.convertJsonToAzapi",
"azapi.aztfmigrate"
"azapi.aztfmigrate",
"azapi.telemetry"
],
"workDoneProgress": true
}
Expand Down
14 changes: 11 additions & 3 deletions internal/langserver/handlers/hover.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ package handlers
import (
"context"

"github.com/Azure/azapi-lsp/internal/langserver/handlers/hover"

lsctx "github.com/Azure/azapi-lsp/internal/context"
"github.com/Azure/azapi-lsp/internal/langserver/handlers/hover"
ilsp "github.com/Azure/azapi-lsp/internal/lsp"
lsp "github.com/Azure/azapi-lsp/internal/protocol"
)
Expand Down Expand Up @@ -36,9 +35,18 @@ func (svc *service) TextDocumentHover(ctx context.Context, params lsp.TextDocume
return nil, err
}

telemetrySender, err := lsctx.Telemetry(ctx)
if err != nil {
return nil, err
}

svc.logger.Printf("Looking for hover data at %q -> %#v", doc.Filename(), fPos.Position())
hoverData := hover.HoverAtPos(data, doc.Filename(), fPos.Position(), svc.logger)
hoverData := hover.HoverAtPos(ctx, data, doc.Filename(), fPos.Position(), svc.logger, telemetrySender)
svc.logger.Printf("received hover data: %#v", hoverData)

if hoverData != nil {
telemetrySender.SendEvent(ctx, "textDocument/hover", nil)
}

return hoverData, nil
}
Loading

0 comments on commit a9dede1

Please sign in to comment.