github.com/newrelic/go-agent@v3.26.0+incompatible/internal_slow_queries_test.go (about)

     1  // Copyright 2020 New Relic Corporation. All rights reserved.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package newrelic
     5  
     6  import (
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/newrelic/go-agent/internal"
    12  	"github.com/newrelic/go-agent/internal/crossagent"
    13  )
    14  
    15  func TestSlowQueryBasic(t *testing.T) {
    16  	cfgfn := func(cfg *Config) {
    17  		cfg.DatastoreTracer.SlowQuery.Threshold = 0
    18  	}
    19  	app := testApp(nil, cfgfn, t)
    20  	txn := app.StartTransaction("hello", nil, helloRequest)
    21  	s1 := DatastoreSegment{
    22  		StartTime:          StartSegmentNow(txn),
    23  		Product:            DatastoreMySQL,
    24  		Collection:         "users",
    25  		Operation:          "INSERT",
    26  		ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)",
    27  	}
    28  	s1.End()
    29  	txn.End()
    30  
    31  	app.ExpectSlowQueries(t, []internal.WantSlowQuery{{
    32  		Count:        1,
    33  		MetricName:   "Datastore/statement/MySQL/users/INSERT",
    34  		Query:        "INSERT INTO users (name, age) VALUES ($1, $2)",
    35  		TxnName:      "WebTransaction/Go/hello",
    36  		TxnURL:       "/hello",
    37  		DatabaseName: "",
    38  		Host:         "",
    39  		PortPathOrID: "",
    40  	}})
    41  }
    42  
    43  func TestSlowQueryLocallyDisabled(t *testing.T) {
    44  	cfgfn := func(cfg *Config) {
    45  		cfg.DatastoreTracer.SlowQuery.Threshold = 0
    46  		cfg.DatastoreTracer.SlowQuery.Enabled = false
    47  	}
    48  	app := testApp(nil, cfgfn, t)
    49  	txn := app.StartTransaction("hello", nil, helloRequest)
    50  	s1 := DatastoreSegment{
    51  		StartTime:          StartSegmentNow(txn),
    52  		Product:            DatastoreMySQL,
    53  		Collection:         "users",
    54  		Operation:          "INSERT",
    55  		ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)",
    56  	}
    57  	s1.End()
    58  	txn.End()
    59  
    60  	app.ExpectSlowQueries(t, []internal.WantSlowQuery{})
    61  }
    62  
    63  func TestSlowQueryRemotelyDisabled(t *testing.T) {
    64  	cfgfn := func(cfg *Config) {
    65  		cfg.DatastoreTracer.SlowQuery.Threshold = 0
    66  	}
    67  	replyfn := func(reply *internal.ConnectReply) {
    68  		reply.CollectTraces = false
    69  	}
    70  	app := testApp(replyfn, cfgfn, t)
    71  	txn := app.StartTransaction("hello", nil, helloRequest)
    72  	s1 := DatastoreSegment{
    73  		StartTime:          StartSegmentNow(txn),
    74  		Product:            DatastoreMySQL,
    75  		Collection:         "users",
    76  		Operation:          "INSERT",
    77  		ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)",
    78  	}
    79  	s1.End()
    80  	txn.End()
    81  
    82  	app.ExpectSlowQueries(t, []internal.WantSlowQuery{})
    83  }
    84  
    85  func TestSlowQueryBelowThreshold(t *testing.T) {
    86  	cfgfn := func(cfg *Config) {
    87  		cfg.DatastoreTracer.SlowQuery.Threshold = 1 * time.Hour
    88  	}
    89  	app := testApp(nil, cfgfn, t)
    90  	txn := app.StartTransaction("hello", nil, helloRequest)
    91  	s1 := DatastoreSegment{
    92  		StartTime:          StartSegmentNow(txn),
    93  		Product:            DatastoreMySQL,
    94  		Collection:         "users",
    95  		Operation:          "INSERT",
    96  		ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)",
    97  	}
    98  	s1.End()
    99  	txn.End()
   100  
   101  	app.ExpectSlowQueries(t, []internal.WantSlowQuery{})
   102  }
   103  
   104  func TestSlowQueryDatabaseProvided(t *testing.T) {
   105  	cfgfn := func(cfg *Config) {
   106  		cfg.DatastoreTracer.SlowQuery.Threshold = 0
   107  	}
   108  	app := testApp(nil, cfgfn, t)
   109  	txn := app.StartTransaction("hello", nil, helloRequest)
   110  	s1 := DatastoreSegment{
   111  		StartTime:          StartSegmentNow(txn),
   112  		Product:            DatastoreMySQL,
   113  		Collection:         "users",
   114  		Operation:          "INSERT",
   115  		ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)",
   116  		DatabaseName:       "my_database",
   117  	}
   118  	s1.End()
   119  	txn.End()
   120  
   121  	app.ExpectSlowQueries(t, []internal.WantSlowQuery{{
   122  		Count:        1,
   123  		MetricName:   "Datastore/statement/MySQL/users/INSERT",
   124  		Query:        "INSERT INTO users (name, age) VALUES ($1, $2)",
   125  		TxnName:      "WebTransaction/Go/hello",
   126  		TxnURL:       "/hello",
   127  		DatabaseName: "my_database",
   128  		Host:         "",
   129  		PortPathOrID: "",
   130  	}})
   131  }
   132  
   133  func TestSlowQueryHostProvided(t *testing.T) {
   134  	cfgfn := func(cfg *Config) {
   135  		cfg.DatastoreTracer.SlowQuery.Threshold = 0
   136  	}
   137  	app := testApp(nil, cfgfn, t)
   138  	txn := app.StartTransaction("hello", nil, helloRequest)
   139  	s1 := DatastoreSegment{
   140  		StartTime:          StartSegmentNow(txn),
   141  		Product:            DatastoreMySQL,
   142  		Collection:         "users",
   143  		Operation:          "INSERT",
   144  		ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)",
   145  		Host:               "db-server-1",
   146  	}
   147  	s1.End()
   148  	txn.End()
   149  
   150  	app.ExpectSlowQueries(t, []internal.WantSlowQuery{{
   151  		Count:        1,
   152  		MetricName:   "Datastore/statement/MySQL/users/INSERT",
   153  		Query:        "INSERT INTO users (name, age) VALUES ($1, $2)",
   154  		TxnName:      "WebTransaction/Go/hello",
   155  		TxnURL:       "/hello",
   156  		DatabaseName: "",
   157  		Host:         "db-server-1",
   158  		PortPathOrID: "unknown",
   159  	}})
   160  	scope := "WebTransaction/Go/hello"
   161  	app.ExpectMetrics(t, append([]internal.WantMetric{
   162  		{Name: "Datastore/all", Scope: "", Forced: true, Data: nil},
   163  		{Name: "Datastore/allWeb", Scope: "", Forced: true, Data: nil},
   164  		{Name: "Datastore/MySQL/all", Scope: "", Forced: true, Data: nil},
   165  		{Name: "Datastore/MySQL/allWeb", Scope: "", Forced: true, Data: nil},
   166  		{Name: "Datastore/operation/MySQL/INSERT", Scope: "", Forced: false, Data: nil},
   167  		{Name: "Datastore/statement/MySQL/users/INSERT", Scope: "", Forced: false, Data: nil},
   168  		{Name: "Datastore/statement/MySQL/users/INSERT", Scope: scope, Forced: false, Data: nil},
   169  		{Name: "Datastore/instance/MySQL/db-server-1/unknown", Scope: "", Forced: false, Data: nil},
   170  	}, webMetrics...))
   171  }
   172  
   173  func TestSlowQueryPortProvided(t *testing.T) {
   174  	cfgfn := func(cfg *Config) {
   175  		cfg.DatastoreTracer.SlowQuery.Threshold = 0
   176  	}
   177  	app := testApp(nil, cfgfn, t)
   178  	txn := app.StartTransaction("hello", nil, helloRequest)
   179  	s1 := DatastoreSegment{
   180  		StartTime:          StartSegmentNow(txn),
   181  		Product:            DatastoreMySQL,
   182  		Collection:         "users",
   183  		Operation:          "INSERT",
   184  		ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)",
   185  		PortPathOrID:       "98021",
   186  	}
   187  	s1.End()
   188  	txn.End()
   189  
   190  	app.ExpectSlowQueries(t, []internal.WantSlowQuery{{
   191  		Count:        1,
   192  		MetricName:   "Datastore/statement/MySQL/users/INSERT",
   193  		Query:        "INSERT INTO users (name, age) VALUES ($1, $2)",
   194  		TxnName:      "WebTransaction/Go/hello",
   195  		TxnURL:       "/hello",
   196  		DatabaseName: "",
   197  		Host:         "unknown",
   198  		PortPathOrID: "98021",
   199  	}})
   200  	scope := "WebTransaction/Go/hello"
   201  	app.ExpectMetrics(t, append([]internal.WantMetric{
   202  		{Name: "Datastore/all", Scope: "", Forced: true, Data: nil},
   203  		{Name: "Datastore/allWeb", Scope: "", Forced: true, Data: nil},
   204  		{Name: "Datastore/MySQL/all", Scope: "", Forced: true, Data: nil},
   205  		{Name: "Datastore/MySQL/allWeb", Scope: "", Forced: true, Data: nil},
   206  		{Name: "Datastore/operation/MySQL/INSERT", Scope: "", Forced: false, Data: nil},
   207  		{Name: "Datastore/statement/MySQL/users/INSERT", Scope: "", Forced: false, Data: nil},
   208  		{Name: "Datastore/statement/MySQL/users/INSERT", Scope: scope, Forced: false, Data: nil},
   209  		{Name: "Datastore/instance/MySQL/unknown/98021", Scope: "", Forced: false, Data: nil},
   210  	}, webMetrics...))
   211  }
   212  
   213  func TestSlowQueryHostPortProvided(t *testing.T) {
   214  	cfgfn := func(cfg *Config) {
   215  		cfg.DatastoreTracer.SlowQuery.Threshold = 0
   216  	}
   217  	app := testApp(nil, cfgfn, t)
   218  	txn := app.StartTransaction("hello", nil, helloRequest)
   219  	s1 := DatastoreSegment{
   220  		StartTime:          StartSegmentNow(txn),
   221  		Product:            DatastoreMySQL,
   222  		Collection:         "users",
   223  		Operation:          "INSERT",
   224  		ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)",
   225  		Host:               "db-server-1",
   226  		PortPathOrID:       "98021",
   227  	}
   228  	s1.End()
   229  	txn.End()
   230  
   231  	app.ExpectSlowQueries(t, []internal.WantSlowQuery{{
   232  		Count:        1,
   233  		MetricName:   "Datastore/statement/MySQL/users/INSERT",
   234  		Query:        "INSERT INTO users (name, age) VALUES ($1, $2)",
   235  		TxnName:      "WebTransaction/Go/hello",
   236  		TxnURL:       "/hello",
   237  		DatabaseName: "",
   238  		Host:         "db-server-1",
   239  		PortPathOrID: "98021",
   240  	}})
   241  	scope := "WebTransaction/Go/hello"
   242  	app.ExpectMetrics(t, append([]internal.WantMetric{
   243  		{Name: "Datastore/all", Scope: "", Forced: true, Data: nil},
   244  		{Name: "Datastore/allWeb", Scope: "", Forced: true, Data: nil},
   245  		{Name: "Datastore/MySQL/all", Scope: "", Forced: true, Data: nil},
   246  		{Name: "Datastore/MySQL/allWeb", Scope: "", Forced: true, Data: nil},
   247  		{Name: "Datastore/operation/MySQL/INSERT", Scope: "", Forced: false, Data: nil},
   248  		{Name: "Datastore/statement/MySQL/users/INSERT", Scope: "", Forced: false, Data: nil},
   249  		{Name: "Datastore/statement/MySQL/users/INSERT", Scope: scope, Forced: false, Data: nil},
   250  		{Name: "Datastore/instance/MySQL/db-server-1/98021", Scope: "", Forced: false, Data: nil},
   251  	}, webMetrics...))
   252  }
   253  
   254  func TestSlowQueryAggregation(t *testing.T) {
   255  	cfgfn := func(cfg *Config) {
   256  		cfg.DatastoreTracer.SlowQuery.Threshold = 0
   257  	}
   258  	app := testApp(nil, cfgfn, t)
   259  	txn := app.StartTransaction("hello", nil, helloRequest)
   260  	ds := DatastoreSegment{
   261  		StartTime:          StartSegmentNow(txn),
   262  		Product:            DatastoreMySQL,
   263  		Collection:         "users",
   264  		Operation:          "INSERT",
   265  		ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)",
   266  	}
   267  	ds.End()
   268  	ds = DatastoreSegment{
   269  		StartTime:          StartSegmentNow(txn),
   270  		Product:            DatastoreMySQL,
   271  		Collection:         "users",
   272  		Operation:          "INSERT",
   273  		ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)",
   274  	}
   275  	ds.End()
   276  	ds = DatastoreSegment{
   277  		StartTime:          StartSegmentNow(txn),
   278  		Product:            DatastorePostgres,
   279  		Collection:         "products",
   280  		Operation:          "INSERT",
   281  		ParameterizedQuery: "INSERT INTO products (name, price) VALUES ($1, $2)",
   282  	}
   283  	ds.End()
   284  	txn.End()
   285  
   286  	app.ExpectSlowQueries(t, []internal.WantSlowQuery{{
   287  		Count:        2,
   288  		MetricName:   "Datastore/statement/MySQL/users/INSERT",
   289  		Query:        "INSERT INTO users (name, age) VALUES ($1, $2)",
   290  		TxnName:      "WebTransaction/Go/hello",
   291  		TxnURL:       "/hello",
   292  		DatabaseName: "",
   293  		Host:         "",
   294  		PortPathOrID: "",
   295  	}, {
   296  		Count:        1,
   297  		MetricName:   "Datastore/statement/Postgres/products/INSERT",
   298  		Query:        "INSERT INTO products (name, price) VALUES ($1, $2)",
   299  		TxnName:      "WebTransaction/Go/hello",
   300  		TxnURL:       "/hello",
   301  		DatabaseName: "",
   302  		Host:         "",
   303  		PortPathOrID: "",
   304  	},
   305  	})
   306  }
   307  
   308  func TestSlowQueryMissingQuery(t *testing.T) {
   309  	cfgfn := func(cfg *Config) {
   310  		cfg.DatastoreTracer.SlowQuery.Threshold = 0
   311  	}
   312  	app := testApp(nil, cfgfn, t)
   313  	txn := app.StartTransaction("hello", nil, helloRequest)
   314  	s1 := DatastoreSegment{
   315  		StartTime:  StartSegmentNow(txn),
   316  		Product:    DatastoreMySQL,
   317  		Collection: "users",
   318  		Operation:  "INSERT",
   319  	}
   320  	s1.End()
   321  	txn.End()
   322  
   323  	app.ExpectSlowQueries(t, []internal.WantSlowQuery{{
   324  		Count:        1,
   325  		MetricName:   "Datastore/statement/MySQL/users/INSERT",
   326  		Query:        "'INSERT' on 'users' using 'MySQL'",
   327  		TxnName:      "WebTransaction/Go/hello",
   328  		TxnURL:       "/hello",
   329  		DatabaseName: "",
   330  		Host:         "",
   331  		PortPathOrID: "",
   332  	}})
   333  }
   334  
   335  func TestSlowQueryMissingEverything(t *testing.T) {
   336  	cfgfn := func(cfg *Config) {
   337  		cfg.DatastoreTracer.SlowQuery.Threshold = 0
   338  	}
   339  	app := testApp(nil, cfgfn, t)
   340  	txn := app.StartTransaction("hello", nil, helloRequest)
   341  	s1 := DatastoreSegment{
   342  		StartTime: StartSegmentNow(txn),
   343  	}
   344  	s1.End()
   345  	txn.End()
   346  
   347  	app.ExpectSlowQueries(t, []internal.WantSlowQuery{{
   348  		Count:        1,
   349  		MetricName:   "Datastore/operation/Unknown/other",
   350  		Query:        "'other' on 'unknown' using 'Unknown'",
   351  		TxnName:      "WebTransaction/Go/hello",
   352  		TxnURL:       "/hello",
   353  		DatabaseName: "",
   354  		Host:         "",
   355  		PortPathOrID: "",
   356  	}})
   357  	scope := "WebTransaction/Go/hello"
   358  	app.ExpectMetrics(t, append([]internal.WantMetric{
   359  		{Name: "Datastore/all", Scope: "", Forced: true, Data: nil},
   360  		{Name: "Datastore/allWeb", Scope: "", Forced: true, Data: nil},
   361  		{Name: "Datastore/Unknown/all", Scope: "", Forced: true, Data: nil},
   362  		{Name: "Datastore/Unknown/allWeb", Scope: "", Forced: true, Data: nil},
   363  		{Name: "Datastore/operation/Unknown/other", Scope: "", Forced: false, Data: nil},
   364  		{Name: "Datastore/operation/Unknown/other", Scope: scope, Forced: false, Data: nil},
   365  	}, webMetrics...))
   366  }
   367  
   368  func TestSlowQueryWithQueryParameters(t *testing.T) {
   369  	cfgfn := func(cfg *Config) {
   370  		cfg.DatastoreTracer.SlowQuery.Threshold = 0
   371  	}
   372  	app := testApp(nil, cfgfn, t)
   373  	txn := app.StartTransaction("hello", nil, helloRequest)
   374  	params := map[string]interface{}{
   375  		"str": "zap",
   376  		"int": 123,
   377  	}
   378  	s1 := DatastoreSegment{
   379  		StartTime:          StartSegmentNow(txn),
   380  		Product:            DatastoreMySQL,
   381  		Collection:         "users",
   382  		Operation:          "INSERT",
   383  		ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)",
   384  		QueryParameters:    params,
   385  	}
   386  	s1.End()
   387  	txn.End()
   388  
   389  	app.ExpectSlowQueries(t, []internal.WantSlowQuery{{
   390  		Count:        1,
   391  		MetricName:   "Datastore/statement/MySQL/users/INSERT",
   392  		Query:        "INSERT INTO users (name, age) VALUES ($1, $2)",
   393  		TxnName:      "WebTransaction/Go/hello",
   394  		TxnURL:       "/hello",
   395  		DatabaseName: "",
   396  		Host:         "",
   397  		PortPathOrID: "",
   398  		Params:       params,
   399  	}})
   400  }
   401  
   402  func TestSlowQueryHighSecurity(t *testing.T) {
   403  	cfgfn := func(cfg *Config) {
   404  		cfg.DatastoreTracer.SlowQuery.Threshold = 0
   405  		cfg.HighSecurity = true
   406  	}
   407  	app := testApp(nil, cfgfn, t)
   408  	txn := app.StartTransaction("hello", nil, helloRequest)
   409  	params := map[string]interface{}{
   410  		"str": "zap",
   411  		"int": 123,
   412  	}
   413  	s1 := DatastoreSegment{
   414  		StartTime:          StartSegmentNow(txn),
   415  		Product:            DatastoreMySQL,
   416  		Collection:         "users",
   417  		Operation:          "INSERT",
   418  		ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)",
   419  		QueryParameters:    params,
   420  	}
   421  	s1.End()
   422  	txn.End()
   423  
   424  	app.ExpectSlowQueries(t, []internal.WantSlowQuery{{
   425  		Count:        1,
   426  		MetricName:   "Datastore/statement/MySQL/users/INSERT",
   427  		Query:        "INSERT INTO users (name, age) VALUES ($1, $2)",
   428  		TxnName:      "WebTransaction/Go/hello",
   429  		TxnURL:       "/hello",
   430  		DatabaseName: "",
   431  		Host:         "",
   432  		PortPathOrID: "",
   433  		Params:       nil,
   434  	}})
   435  }
   436  
   437  func TestSlowQuerySecurityPolicyFalse(t *testing.T) {
   438  	// When the record_sql security policy is set to false, sql parameters
   439  	// and the sql format string should be replaced.
   440  	cfgfn := func(cfg *Config) {
   441  		cfg.DatastoreTracer.SlowQuery.Threshold = 0
   442  	}
   443  	replyfn := func(reply *internal.ConnectReply) {
   444  		reply.SecurityPolicies.RecordSQL.SetEnabled(false)
   445  	}
   446  	app := testApp(replyfn, cfgfn, t)
   447  	txn := app.StartTransaction("hello", nil, helloRequest)
   448  	params := map[string]interface{}{
   449  		"str": "zap",
   450  		"int": 123,
   451  	}
   452  	s1 := DatastoreSegment{
   453  		StartTime:          StartSegmentNow(txn),
   454  		Product:            DatastoreMySQL,
   455  		Collection:         "users",
   456  		Operation:          "INSERT",
   457  		ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)",
   458  		QueryParameters:    params,
   459  	}
   460  	s1.End()
   461  	txn.End()
   462  
   463  	app.ExpectSlowQueries(t, []internal.WantSlowQuery{{
   464  		Count:        1,
   465  		MetricName:   "Datastore/statement/MySQL/users/INSERT",
   466  		Query:        "'INSERT' on 'users' using 'MySQL'",
   467  		TxnName:      "WebTransaction/Go/hello",
   468  		TxnURL:       "/hello",
   469  		DatabaseName: "",
   470  		Host:         "",
   471  		PortPathOrID: "",
   472  		Params:       nil,
   473  	}})
   474  }
   475  
   476  func TestSlowQuerySecurityPolicyTrue(t *testing.T) {
   477  	// When the record_sql security policy is set to true, sql parameters
   478  	// should be omitted.
   479  	cfgfn := func(cfg *Config) {
   480  		cfg.DatastoreTracer.SlowQuery.Threshold = 0
   481  	}
   482  	replyfn := func(reply *internal.ConnectReply) {
   483  		reply.SecurityPolicies.RecordSQL.SetEnabled(true)
   484  	}
   485  	app := testApp(replyfn, cfgfn, t)
   486  	txn := app.StartTransaction("hello", nil, helloRequest)
   487  	params := map[string]interface{}{
   488  		"str": "zap",
   489  		"int": 123,
   490  	}
   491  	s1 := DatastoreSegment{
   492  		StartTime:          StartSegmentNow(txn),
   493  		Product:            DatastoreMySQL,
   494  		Collection:         "users",
   495  		Operation:          "INSERT",
   496  		ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)",
   497  		QueryParameters:    params,
   498  	}
   499  	s1.End()
   500  	txn.End()
   501  
   502  	app.ExpectSlowQueries(t, []internal.WantSlowQuery{{
   503  		Count:        1,
   504  		MetricName:   "Datastore/statement/MySQL/users/INSERT",
   505  		Query:        "INSERT INTO users (name, age) VALUES ($1, $2)",
   506  		TxnName:      "WebTransaction/Go/hello",
   507  		TxnURL:       "/hello",
   508  		DatabaseName: "",
   509  		Host:         "",
   510  		PortPathOrID: "",
   511  		Params:       nil,
   512  	}})
   513  }
   514  
   515  func TestSlowQueryInvalidParameters(t *testing.T) {
   516  	cfgfn := func(cfg *Config) {
   517  		cfg.DatastoreTracer.SlowQuery.Threshold = 0
   518  	}
   519  	app := testApp(nil, cfgfn, t)
   520  	txn := app.StartTransaction("hello", nil, helloRequest)
   521  	params := map[string]interface{}{
   522  		"str":                               "zap",
   523  		"int":                               123,
   524  		"invalid_value":                     struct{}{},
   525  		strings.Repeat("key-too-long", 100): 1,
   526  		"long-key":                          strings.Repeat("A", 300),
   527  	}
   528  	s1 := DatastoreSegment{
   529  		StartTime:          StartSegmentNow(txn),
   530  		Product:            DatastoreMySQL,
   531  		Collection:         "users",
   532  		Operation:          "INSERT",
   533  		ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)",
   534  		QueryParameters:    params,
   535  	}
   536  	s1.End()
   537  	txn.End()
   538  
   539  	app.ExpectSlowQueries(t, []internal.WantSlowQuery{{
   540  		Count:        1,
   541  		MetricName:   "Datastore/statement/MySQL/users/INSERT",
   542  		Query:        "INSERT INTO users (name, age) VALUES ($1, $2)",
   543  		TxnName:      "WebTransaction/Go/hello",
   544  		TxnURL:       "/hello",
   545  		DatabaseName: "",
   546  		Host:         "",
   547  		PortPathOrID: "",
   548  		Params: map[string]interface{}{
   549  			"str":      "zap",
   550  			"int":      123,
   551  			"long-key": strings.Repeat("A", 255),
   552  		},
   553  	}})
   554  }
   555  
   556  func TestSlowQueryParametersDisabled(t *testing.T) {
   557  	cfgfn := func(cfg *Config) {
   558  		cfg.DatastoreTracer.SlowQuery.Threshold = 0
   559  		cfg.DatastoreTracer.QueryParameters.Enabled = false
   560  	}
   561  	app := testApp(nil, cfgfn, t)
   562  	txn := app.StartTransaction("hello", nil, helloRequest)
   563  	params := map[string]interface{}{
   564  		"str": "zap",
   565  		"int": 123,
   566  	}
   567  	s1 := DatastoreSegment{
   568  		StartTime:          StartSegmentNow(txn),
   569  		Product:            DatastoreMySQL,
   570  		Collection:         "users",
   571  		Operation:          "INSERT",
   572  		ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)",
   573  		QueryParameters:    params,
   574  	}
   575  	s1.End()
   576  	txn.End()
   577  
   578  	app.ExpectSlowQueries(t, []internal.WantSlowQuery{{
   579  		Count:        1,
   580  		MetricName:   "Datastore/statement/MySQL/users/INSERT",
   581  		Query:        "INSERT INTO users (name, age) VALUES ($1, $2)",
   582  		TxnName:      "WebTransaction/Go/hello",
   583  		TxnURL:       "/hello",
   584  		DatabaseName: "",
   585  		Host:         "",
   586  		PortPathOrID: "",
   587  		Params:       nil,
   588  	}})
   589  }
   590  
   591  func TestSlowQueryInstanceDisabled(t *testing.T) {
   592  	cfgfn := func(cfg *Config) {
   593  		cfg.DatastoreTracer.SlowQuery.Threshold = 0
   594  		cfg.DatastoreTracer.InstanceReporting.Enabled = false
   595  	}
   596  	app := testApp(nil, cfgfn, t)
   597  	txn := app.StartTransaction("hello", nil, helloRequest)
   598  	s1 := DatastoreSegment{
   599  		StartTime:          StartSegmentNow(txn),
   600  		Product:            DatastoreMySQL,
   601  		Collection:         "users",
   602  		Operation:          "INSERT",
   603  		ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)",
   604  		Host:               "db-server-1",
   605  	}
   606  	s1.End()
   607  	txn.End()
   608  
   609  	app.ExpectSlowQueries(t, []internal.WantSlowQuery{{
   610  		Count:        1,
   611  		MetricName:   "Datastore/statement/MySQL/users/INSERT",
   612  		Query:        "INSERT INTO users (name, age) VALUES ($1, $2)",
   613  		TxnName:      "WebTransaction/Go/hello",
   614  		TxnURL:       "/hello",
   615  		DatabaseName: "",
   616  		Host:         "",
   617  		PortPathOrID: "",
   618  	}})
   619  	scope := "WebTransaction/Go/hello"
   620  	app.ExpectMetrics(t, append([]internal.WantMetric{
   621  		{Name: "Datastore/all", Scope: "", Forced: true, Data: nil},
   622  		{Name: "Datastore/allWeb", Scope: "", Forced: true, Data: nil},
   623  		{Name: "Datastore/MySQL/all", Scope: "", Forced: true, Data: nil},
   624  		{Name: "Datastore/MySQL/allWeb", Scope: "", Forced: true, Data: nil},
   625  		{Name: "Datastore/operation/MySQL/INSERT", Scope: "", Forced: false, Data: nil},
   626  		{Name: "Datastore/statement/MySQL/users/INSERT", Scope: "", Forced: false, Data: nil},
   627  		{Name: "Datastore/statement/MySQL/users/INSERT", Scope: scope, Forced: false, Data: nil},
   628  	}, webMetrics...))
   629  }
   630  
   631  func TestSlowQueryInstanceDisabledLocalhost(t *testing.T) {
   632  	cfgfn := func(cfg *Config) {
   633  		cfg.DatastoreTracer.SlowQuery.Threshold = 0
   634  		cfg.DatastoreTracer.InstanceReporting.Enabled = false
   635  	}
   636  	app := testApp(nil, cfgfn, t)
   637  	txn := app.StartTransaction("hello", nil, helloRequest)
   638  	s1 := DatastoreSegment{
   639  		StartTime:          StartSegmentNow(txn),
   640  		Product:            DatastoreMySQL,
   641  		Collection:         "users",
   642  		Operation:          "INSERT",
   643  		ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)",
   644  		Host:               "localhost",
   645  		PortPathOrID:       "3306",
   646  	}
   647  	s1.End()
   648  	txn.End()
   649  
   650  	app.ExpectSlowQueries(t, []internal.WantSlowQuery{{
   651  		Count:        1,
   652  		MetricName:   "Datastore/statement/MySQL/users/INSERT",
   653  		Query:        "INSERT INTO users (name, age) VALUES ($1, $2)",
   654  		TxnName:      "WebTransaction/Go/hello",
   655  		TxnURL:       "/hello",
   656  		DatabaseName: "",
   657  		Host:         "",
   658  		PortPathOrID: "",
   659  	}})
   660  	scope := "WebTransaction/Go/hello"
   661  	app.ExpectMetrics(t, append([]internal.WantMetric{
   662  		{Name: "Datastore/all", Scope: "", Forced: true, Data: nil},
   663  		{Name: "Datastore/allWeb", Scope: "", Forced: true, Data: nil},
   664  		{Name: "Datastore/MySQL/all", Scope: "", Forced: true, Data: nil},
   665  		{Name: "Datastore/MySQL/allWeb", Scope: "", Forced: true, Data: nil},
   666  		{Name: "Datastore/operation/MySQL/INSERT", Scope: "", Forced: false, Data: nil},
   667  		{Name: "Datastore/statement/MySQL/users/INSERT", Scope: "", Forced: false, Data: nil},
   668  		{Name: "Datastore/statement/MySQL/users/INSERT", Scope: scope, Forced: false, Data: nil},
   669  	}, webMetrics...))
   670  }
   671  
   672  func TestSlowQueryDatabaseNameDisabled(t *testing.T) {
   673  	cfgfn := func(cfg *Config) {
   674  		cfg.DatastoreTracer.SlowQuery.Threshold = 0
   675  		cfg.DatastoreTracer.DatabaseNameReporting.Enabled = false
   676  	}
   677  	app := testApp(nil, cfgfn, t)
   678  	txn := app.StartTransaction("hello", nil, helloRequest)
   679  	s1 := DatastoreSegment{
   680  		StartTime:          StartSegmentNow(txn),
   681  		Product:            DatastoreMySQL,
   682  		Collection:         "users",
   683  		Operation:          "INSERT",
   684  		ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)",
   685  		DatabaseName:       "db-server-1",
   686  	}
   687  	s1.End()
   688  	txn.End()
   689  
   690  	app.ExpectSlowQueries(t, []internal.WantSlowQuery{{
   691  		Count:        1,
   692  		MetricName:   "Datastore/statement/MySQL/users/INSERT",
   693  		Query:        "INSERT INTO users (name, age) VALUES ($1, $2)",
   694  		TxnName:      "WebTransaction/Go/hello",
   695  		TxnURL:       "/hello",
   696  		DatabaseName: "",
   697  		Host:         "",
   698  		PortPathOrID: "",
   699  	}})
   700  }
   701  
   702  func TestDatastoreAPICrossAgent(t *testing.T) {
   703  	var testcases []struct {
   704  		TestName string `json:"test_name"`
   705  		Input    struct {
   706  			Parameters struct {
   707  				Product      string `json:"product"`
   708  				Collection   string `json:"collection"`
   709  				Operation    string `json:"operation"`
   710  				Host         string `json:"host"`
   711  				PortPathOrID string `json:"port_path_or_id"`
   712  				DatabaseName string `json:"database_name"`
   713  			} `json:"parameters"`
   714  			IsWeb          bool   `json:"is_web"`
   715  			SystemHostname string `json:"system_hostname"`
   716  			Configuration  struct {
   717  				InstanceEnabled bool `json:"datastore_tracer.instance_reporting.enabled"`
   718  				DatabaseEnabled bool `json:"datastore_tracer.database_name_reporting.enabled"`
   719  			}
   720  		}
   721  		Expectation struct {
   722  			MetricsScoped   []string `json:"metrics_scoped"`
   723  			MetricsUnscoped []string `json:"metrics_unscoped"`
   724  			Trace           struct {
   725  				MetricName   string `json:"metric_name"`
   726  				Host         string `json:"host"`
   727  				PortPathOrID string `json:"port_path_or_id"`
   728  				DatabaseName string `json:"database_name"`
   729  			} `json:"transaction_segment_and_slow_query_trace"`
   730  		}
   731  	}
   732  
   733  	err := crossagent.ReadJSON("datastores/datastore_api.json", &testcases)
   734  	if err != nil {
   735  		t.Fatal(err)
   736  	}
   737  
   738  	for _, tc := range testcases {
   739  		query := "my query"
   740  		cfgfn := func(cfg *Config) {
   741  			cfg.DatastoreTracer.SlowQuery.Threshold = 0
   742  			cfg.DatastoreTracer.InstanceReporting.Enabled =
   743  				tc.Input.Configuration.InstanceEnabled
   744  			cfg.DatastoreTracer.DatabaseNameReporting.Enabled =
   745  				tc.Input.Configuration.DatabaseEnabled
   746  		}
   747  		app := testApp(nil, cfgfn, t)
   748  		var txn Transaction
   749  		var txnURL string
   750  		if tc.Input.IsWeb {
   751  			txnURL = helloPath
   752  			txn = app.StartTransaction("hello", nil, helloRequest)
   753  		} else {
   754  			txn = app.StartTransaction("hello", nil, nil)
   755  		}
   756  		ds := DatastoreSegment{
   757  			StartTime:          StartSegmentNow(txn),
   758  			Product:            DatastoreProduct(tc.Input.Parameters.Product),
   759  			Operation:          tc.Input.Parameters.Operation,
   760  			Collection:         tc.Input.Parameters.Collection,
   761  			PortPathOrID:       tc.Input.Parameters.PortPathOrID,
   762  			Host:               tc.Input.Parameters.Host,
   763  			DatabaseName:       tc.Input.Parameters.DatabaseName,
   764  			ParameterizedQuery: query,
   765  		}
   766  		ds.End()
   767  		txn.End()
   768  
   769  		var metrics []internal.WantMetric
   770  		var scope string
   771  		if tc.Input.IsWeb {
   772  			scope = "WebTransaction/Go/hello"
   773  			metrics = append([]internal.WantMetric{}, webMetrics...)
   774  		} else {
   775  			scope = "OtherTransaction/Go/hello"
   776  			metrics = append([]internal.WantMetric{}, backgroundMetrics...)
   777  		}
   778  
   779  		for _, m := range tc.Expectation.MetricsScoped {
   780  			metrics = append(metrics, internal.WantMetric{
   781  				Name: m, Scope: scope, Forced: nil, Data: nil,
   782  			})
   783  		}
   784  		for _, m := range tc.Expectation.MetricsUnscoped {
   785  			metrics = append(metrics, internal.WantMetric{
   786  				Name: m, Scope: "", Forced: nil, Data: nil,
   787  			})
   788  		}
   789  
   790  		expectTraceHost := tc.Expectation.Trace.Host
   791  		if tc.Input.SystemHostname != "" {
   792  			for i := range metrics {
   793  				metrics[i].Name = strings.Replace(metrics[i].Name,
   794  					tc.Input.SystemHostname,
   795  					internal.ThisHost, -1)
   796  			}
   797  			expectTraceHost = strings.Replace(expectTraceHost,
   798  				tc.Input.SystemHostname,
   799  				internal.ThisHost, -1)
   800  		}
   801  
   802  		tt := internal.ExtendValidator(t, tc.TestName)
   803  		app.ExpectMetrics(tt, metrics)
   804  		app.ExpectSlowQueries(tt, []internal.WantSlowQuery{{
   805  			Count:        1,
   806  			MetricName:   tc.Expectation.Trace.MetricName,
   807  			TxnName:      scope,
   808  			DatabaseName: tc.Expectation.Trace.DatabaseName,
   809  			Host:         expectTraceHost,
   810  			PortPathOrID: tc.Expectation.Trace.PortPathOrID,
   811  			TxnURL:       txnURL,
   812  			Query:        query,
   813  		}})
   814  	}
   815  }
   816  
   817  func TestSlowQueryParamsInvalid(t *testing.T) {
   818  	cfgfn := func(cfg *Config) {
   819  		cfg.DatastoreTracer.SlowQuery.Threshold = 0
   820  	}
   821  	app := testApp(nil, cfgfn, t)
   822  	txn := app.StartTransaction("hello", nil, helloRequest)
   823  	s1 := DatastoreSegment{
   824  		StartTime:          StartSegmentNow(txn),
   825  		Product:            DatastoreMySQL,
   826  		Collection:         "users",
   827  		Operation:          "INSERT",
   828  		ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)",
   829  		QueryParameters: map[string]interface{}{
   830  			"cookies": []string{"chocolate", "sugar", "oatmeal"},
   831  			"number":  5,
   832  		},
   833  	}
   834  	err := s1.End()
   835  	if nil == err {
   836  		t.Error("error should have been returned")
   837  	}
   838  	txn.End()
   839  
   840  	app.ExpectSlowQueries(t, []internal.WantSlowQuery{{
   841  		Count:        1,
   842  		MetricName:   "Datastore/statement/MySQL/users/INSERT",
   843  		Query:        "INSERT INTO users (name, age) VALUES ($1, $2)",
   844  		TxnName:      "WebTransaction/Go/hello",
   845  		TxnURL:       "/hello",
   846  		DatabaseName: "",
   847  		Host:         "",
   848  		PortPathOrID: "",
   849  		Params:       map[string]interface{}{"number": 5},
   850  	}})
   851  }