Skip to content

Commit

Permalink
feat: Module Integration Implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
YashishDua authored and kamilogorek committed May 13, 2019
1 parent 05f3d17 commit dcd26bf
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 4 deletions.
4 changes: 0 additions & 4 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,13 @@ import (
"context"
"crypto/x509"
"io"
"io/ioutil"
"log"
"math/rand"
"net/http"
"os"
"sort"
"time"
)

var debugger = log.New(ioutil.Discard, "[Sentry]", log.LstdFlags)

type Integration interface {
Name() string
SetupOnce()
Expand Down
1 change: 1 addition & 0 deletions examples/basic/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ func main() {
Transport: new(DevNullTransport),
Integrations: []sentry.Integration{
new(sentryintegrations.EnvironmentIntegration),
new(sentryintegrations.ModulesIntegration),
},
}); err != nil {
panic(err)
Expand Down
166 changes: 166 additions & 0 deletions integrations/modules.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package sentryintegrations

import (
"bufio"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"strings"

"github.com/getsentry/sentry-go"
)

type ModulesIntegration struct{}

var _modulesCache map[string]string

func (mi ModulesIntegration) Name() string {
return "Modules"
}

func (mi ModulesIntegration) SetupOnce() {
sentry.AddGlobalEventProcessor(mi.processor)
}

func (mi ModulesIntegration) processor(event *sentry.Event, hint *sentry.EventHint) *sentry.Event {
// Run the integration only on the Client that registered it
if sentry.CurrentHub().GetIntegration(mi.Name()) == nil {
return event
}

if event.Modules == nil {
event.Modules = extractModules()
}

return event
}

func extractModules() map[string]string {
if _modulesCache != nil {
return _modulesCache
}

extractedModules, err := getModules()
if err != nil {
debugger.Printf("ModuleIntegration wasn't able to extract modules: %v\n", err)
return nil
}

_modulesCache = extractedModules

return extractedModules
}

func getModules() (map[string]string, error) {
if fileExists("go.mod") {
return getModulesFromMod()
}

if fileExists("vendor") {
// Priority given to vendor created by modules
if fileExists("vendor/modules.txt") {
return getModulesFromVendorTxt()
}

if fileExists("vendor/vendor.json") {
return getModulesFromVendorJSON()
}
}

return nil, fmt.Errorf("module integration failed")
}

func getModulesFromMod() (map[string]string, error) {
modules := make(map[string]string)

file, err := os.Open("go.mod")
if err != nil {
return nil, fmt.Errorf("unable to open mod file")
}

defer file.Close()

areModulesPresent := false

scanner := bufio.NewScanner(file)
for scanner.Scan() {
splits := strings.Split(scanner.Text(), " ")

if splits[0] == "require" {
areModulesPresent = true

// Mod file has only 1 dependency
if len(splits) > 2 {
modules[strings.TrimSpace(splits[1])] = splits[2]
return modules, nil
}
} else if areModulesPresent && splits[0] != ")" {
modules[strings.TrimSpace(splits[0])] = splits[1]
}
}

if scannerErr := scanner.Err(); scannerErr != nil {
return nil, scannerErr
}

return modules, nil
}

func getModulesFromVendorTxt() (map[string]string, error) {
modules := make(map[string]string)

file, err := os.Open("vendor/modules.txt")
if err != nil {
return nil, fmt.Errorf("unable to open vendor/modules.txt")
}

defer file.Close()

scanner := bufio.NewScanner(file)
for scanner.Scan() {
splits := strings.Split(scanner.Text(), " ")

if splits[0] == "#" {
modules[splits[1]] = splits[2]
}
}

if scannerErr := scanner.Err(); scannerErr != nil {
return nil, scannerErr
}

return modules, nil
}

func getModulesFromVendorJSON() (map[string]string, error) {
modules := make(map[string]string)

file, err := ioutil.ReadFile("vendor/vendor.json")

if err != nil {
return nil, fmt.Errorf("unable to open vendor/vendor.json")
}

var vendor map[string]interface{}
if unmarshalErr := json.Unmarshal(file, &vendor); unmarshalErr != nil {
return nil, unmarshalErr
}

packages := vendor["package"].([]interface{})

// To avoid iterative dependencies, TODO: Change of default value
lastPath := "\n"

for _, value := range packages {
path := value.(map[string]interface{})["path"].(string)

if !strings.Contains(path, lastPath) {
// No versions are available through vendor.json
modules[path] = ""
lastPath = path
}
}

return modules, nil
}
17 changes: 17 additions & 0 deletions integrations/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package sentryintegrations

import (
"io/ioutil"
"log"
"os"
)

var debugger = log.New(ioutil.Discard, "[Sentry]", log.LstdFlags)

func fileExists(fileName string) bool {
if _, err := os.Stat(fileName); err != nil {
return false
}

return true
}
4 changes: 4 additions & 0 deletions util.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
)

var debugger = log.New(ioutil.Discard, "[Sentry]", log.LstdFlags)

func uuid() string {
id := make([]byte, 16)
_, _ = io.ReadFull(rand.Reader, id)
Expand Down

0 comments on commit dcd26bf

Please sign in to comment.