Skip to content

Commit

Permalink
Merge pull request #425 from ishahid91/ms_sql_server_intergation
Browse files Browse the repository at this point in the history
Added integration for ms sql server with newrelic
  • Loading branch information
nr-swilloughby committed Jun 29, 2022
2 parents e6316bc + 7eea244 commit 74b16d1
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/ci.yaml
Expand Up @@ -89,6 +89,8 @@ jobs:
dirs: _integrations/nrb3
- go-version: 1.13.x
dirs: _integrations/nrmongo
- go-version: 1.13.x
dirs: _integrations/nrmssql

# v3 agent
- go-version: 1.7.x
Expand Down Expand Up @@ -212,6 +214,9 @@ jobs:
- go-version: 1.15.x
dirs: v3/integrations/nrgraphqlgo,v3/integrations/nrgraphqlgo/example
extratesting: go get -u github.com/graphql-go/graphql@master
- go-version: 1.15.x
dirs: v3/integrations/nrmssql
extratesting: go get -u github.com/denisenkom/go-mssqldb@master

steps:
- name: Install Go
Expand Down
8 changes: 8 additions & 0 deletions v3/integrations/nrmssql/README.md
@@ -0,0 +1,8 @@
# v3/integrations/nrmssql [![GoDoc](https://godoc.org/github.com/newrelic/go-agent/v3/integrations/nrmysql?status.svg)](https://godoc.org/github.com/newrelic/go-agent/v3/integrations/nrmysql)

Package `nrmssql` instruments github.com/denisenkom/go-mssqldb.

```go
import "github.com/newrelic/go-agent/v3/integrations/nrmssql"
```

42 changes: 42 additions & 0 deletions v3/integrations/nrmssql/example/main.go
@@ -0,0 +1,42 @@
package main

import (
"context"
"database/sql"
"fmt"
"os"
"time"

_ "github.com/newrelic/go-agent/v3/integrations/nrmssql"
"github.com/newrelic/go-agent/v3/newrelic"
)

func main() {
// Set up a local ms sql docker

db, err := sql.Open("nrmssql", "server=localhost;user id=sa;database=master;app name=MyAppName")
if nil != err {
panic(err)
}

app, err := newrelic.NewApplication(
newrelic.ConfigAppName("MSSQL App"),
newrelic.ConfigLicense(os.Getenv("NEW_RELIC_LICENSE_KEY")),
newrelic.ConfigDebugLogger(os.Stdout),
)
if nil != err {
panic(err)
}
app.WaitForConnection(5 * time.Second)
txn := app.StartTransaction("mssqlQuery")

ctx := newrelic.NewContext(context.Background(), txn)
row := db.QueryRowContext(ctx, "SELECT count(*) from tables")
var count int
row.Scan(&count)

txn.End()
app.Shutdown(5 * time.Second)

fmt.Println("number of tables in information_schema", count)
}
21 changes: 21 additions & 0 deletions v3/integrations/nrmssql/go.mod
@@ -0,0 +1,21 @@
module github.com/newrelic/go-agent/v3/integrations/nrmssql

go 1.17

require (
github.com/denisenkom/go-mssqldb v0.12.2
github.com/newrelic/go-agent/v3 v3.16.1
)

require (
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/protobuf v1.4.3 // indirect
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 // indirect
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b // indirect
golang.org/x/sys v0.0.0-20210423082822-04245dca01da // indirect
golang.org/x/text v0.3.6 // indirect
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
google.golang.org/grpc v1.39.0 // indirect
google.golang.org/protobuf v1.25.0 // indirect
)
82 changes: 82 additions & 0 deletions v3/integrations/nrmssql/nrmssql.go
@@ -0,0 +1,82 @@
// Copyright 2020 New Relic Corporation. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

// +build go1.10

// Package nrmssql instruments github.com/denisenkom/go-mssqldb.
//
// Use this package to instrument your MSSQL calls without having to manually
// create DatastoreSegments. This is done in a two step process:
//
// 1. Use this package's driver in place of the mssql driver.
//
// If your code is using sql.Open like this:
//
// import (
// _ "github.com/denisenkom/go-mssqldb"
// )
//
// func main() {
// db, err := sql.Open("mssql", "server=localhost;user id=sa;database=master;app name=MyAppName")
// }
//
// Then change the side-effect import to this package, and open "nrmssql" instead:
//
// import (
// _ "github.com/newrelic/go-agent/v3/integrations/nrmssql"
// )
//
// func main() {
// db, err := sql.Open("nrmssql", "server=localhost;user id=sa;database=master;app name=MyAppName")
// }
//
// 2. Provide a context containing a newrelic.Transaction to all exec and query
// methods on sql.DB, sql.Conn, sql.Tx, and sql.Stmt. This requires using the
// context methods ExecContext, QueryContext, and QueryRowContext in place of
// Exec, Query, and QueryRow respectively. For example, instead of the
// following:
//
// row := db.QueryRow("SELECT count(*) from tables")
//
// Do this:
//
// ctx := newrelic.NewContext(context.Background(), txn)
// row := db.QueryRowContext(ctx, "SELECT count(*) from tables")
package nrmssql

import (
"database/sql"
"fmt"
"github.com/denisenkom/go-mssqldb/msdsn"
"github.com/newrelic/go-agent/v3/internal"

"github.com/denisenkom/go-mssqldb"
"github.com/newrelic/go-agent/v3/newrelic"
"github.com/newrelic/go-agent/v3/newrelic/sqlparse"
)

var (
baseBuilder = newrelic.SQLDriverSegmentBuilder{
BaseSegment: newrelic.DatastoreSegment{
Product: newrelic.DatastoreMSSQL,
},
ParseQuery: sqlparse.ParseQuery,
ParseDSN: parseDSN,
}
)

func init() {
sql.Register("nrmssql", newrelic.InstrumentSQLDriver(&mssql.Driver{}, baseBuilder))
internal.TrackUsage("integration", "driver", "mssql")
}

func parseDSN(s *newrelic.DatastoreSegment, dsn string) {
cfg, _, err := msdsn.Parse(dsn)
if nil != err {
return
}

s.DatabaseName = cfg.Database
s.Host = cfg.Host
s.PortPathOrID = fmt.Sprint(cfg.Port)
}
67 changes: 67 additions & 0 deletions v3/integrations/nrmssql/nrmssql_test.go
@@ -0,0 +1,67 @@
package nrmssql

import (
"github.com/newrelic/go-agent/v3/newrelic"
"testing"
)

func TestParseDSN(t *testing.T) {
testcases := []struct {
dsn string
expHost string
expPortPathOrID string
expDatabaseName string
}{
// examples from https://github.com/denisenkom/go-mssqldb/blob/master/msdsn/conn_str_test.go
{
dsn: "server=server\\instance;database=testdb;user id=tester;password=pwd",
expHost: "server",
expPortPathOrID: "0",
expDatabaseName: "testdb",
},
{
dsn: "server=(local)",
expHost: "localhost",
expPortPathOrID: "0",
expDatabaseName: "",
},
{
dsn: "sqlserver://someuser@somehost?connection+timeout=30",
expHost: "somehost",
expPortPathOrID: "0",
expDatabaseName: "",
},
{
dsn: "sqlserver://someuser:foo%3A%2F%5C%21~%40;bar@somehost:1434?connection+timeout=30",
expHost: "somehost",
expPortPathOrID: "1434",
expDatabaseName: "",
},
{
dsn: "Server=mssql.test.local; Initial Catalog=test; User ID=xxxxxxx; Password=abccxxxxxx;",
expHost: "mssql.test.local",
expPortPathOrID: "0",
expDatabaseName: "test",
},
{
dsn: "sport=invalid",
expHost: "localhost",
expPortPathOrID: "0",
expDatabaseName: "",
},
}

for _, test := range testcases {
s := &newrelic.DatastoreSegment{}
parseDSN(s, test.dsn)
if test.expHost != s.Host {
t.Errorf(`incorrect host, expected="%s", actual="%s"`, test.expHost, s.Host)
}
if test.expPortPathOrID != s.PortPathOrID {
t.Errorf(`incorrect port path or id, expected="%s", actual="%s"`, test.expPortPathOrID, s.PortPathOrID)
}
if test.expDatabaseName != s.DatabaseName {
t.Errorf(`incorrect database name, expected="%s", actual="%s"`, test.expDatabaseName, s.DatabaseName)
}
}
}

0 comments on commit 74b16d1

Please sign in to comment.