github.com/newrelic/go-agent@v3.26.0+incompatible/internal_span_events_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  	"net/http"
     8  	"testing"
     9  
    10  	"github.com/newrelic/go-agent/internal"
    11  )
    12  
    13  func TestSpanEventSuccess(t *testing.T) {
    14  	// Test that a basic segment creates a span event, and that a
    15  	// transaction has a root span event.
    16  	replyfn := func(reply *internal.ConnectReply) {
    17  		reply.AdaptiveSampler = internal.SampleEverything{}
    18  		reply.TraceIDGenerator = internal.NewTraceIDGenerator(12345)
    19  	}
    20  	cfgfn := func(cfg *Config) {
    21  		cfg.DistributedTracer.Enabled = true
    22  	}
    23  	app := testApp(replyfn, cfgfn, t)
    24  	txn := app.StartTransaction("hello", nil, nil)
    25  	segment := StartSegment(txn, "mySegment")
    26  	segment.End()
    27  	txn.End()
    28  	app.ExpectSpanEvents(t, []internal.WantEvent{
    29  		{
    30  			Intrinsics: map[string]interface{}{
    31  				"name":          "OtherTransaction/Go/hello",
    32  				"sampled":       true,
    33  				"category":      "generic",
    34  				"priority":      internal.MatchAnything,
    35  				"guid":          "0e97aeb2f79d5d27",
    36  				"transactionId": "d9466896a525ccbf",
    37  				"nr.entryPoint": true,
    38  				"traceId":       "d9466896a525ccbf",
    39  			},
    40  			UserAttributes:  map[string]interface{}{},
    41  			AgentAttributes: map[string]interface{}{},
    42  		},
    43  		{
    44  			Intrinsics: map[string]interface{}{
    45  				"name":          "Custom/mySegment",
    46  				"sampled":       true,
    47  				"category":      "generic",
    48  				"priority":      internal.MatchAnything,
    49  				"guid":          "bcfb32e050b264b8",
    50  				"transactionId": "d9466896a525ccbf",
    51  				"traceId":       "d9466896a525ccbf",
    52  				"parentId":      "0e97aeb2f79d5d27",
    53  			},
    54  			UserAttributes:  map[string]interface{}{},
    55  			AgentAttributes: map[string]interface{}{},
    56  		},
    57  	})
    58  }
    59  
    60  func TestSpanEventsLocallyDisabled(t *testing.T) {
    61  	// Test that span events do not get created if Config.SpanEvents.Enabled
    62  	// is false.
    63  	replyfn := func(reply *internal.ConnectReply) {
    64  		reply.AdaptiveSampler = internal.SampleEverything{}
    65  	}
    66  	cfgfn := func(cfg *Config) {
    67  		cfg.DistributedTracer.Enabled = true
    68  		cfg.SpanEvents.Enabled = false
    69  	}
    70  	app := testApp(replyfn, cfgfn, t)
    71  	txn := app.StartTransaction("hello", nil, nil)
    72  	segment := StartSegment(txn, "mySegment")
    73  	segment.End()
    74  	txn.End()
    75  	app.ExpectSpanEvents(t, []internal.WantEvent{})
    76  }
    77  
    78  func TestSpanEventsRemotelyDisabled(t *testing.T) {
    79  	// Test that span events do not get created if the connect reply
    80  	// disables span events.
    81  	replyfn := func(reply *internal.ConnectReply) {
    82  		reply.AdaptiveSampler = internal.SampleEverything{}
    83  		reply.CollectSpanEvents = false
    84  	}
    85  	cfgfn := func(cfg *Config) {
    86  		cfg.DistributedTracer.Enabled = true
    87  	}
    88  	app := testApp(replyfn, cfgfn, t)
    89  	txn := app.StartTransaction("hello", nil, nil)
    90  	segment := StartSegment(txn, "mySegment")
    91  	segment.End()
    92  	txn.End()
    93  	app.ExpectSpanEvents(t, []internal.WantEvent{})
    94  }
    95  
    96  func TestSpanEventsDisabledWithoutDistributedTracing(t *testing.T) {
    97  	// Test that span events do not get created distributed tracing is not
    98  	// enabled.
    99  	replyfn := func(reply *internal.ConnectReply) {
   100  		reply.AdaptiveSampler = internal.SampleEverything{}
   101  	}
   102  	cfgfn := func(cfg *Config) {
   103  		cfg.DistributedTracer.Enabled = false
   104  	}
   105  	app := testApp(replyfn, cfgfn, t)
   106  	txn := app.StartTransaction("hello", nil, nil)
   107  	segment := StartSegment(txn, "mySegment")
   108  	segment.End()
   109  	txn.End()
   110  	app.ExpectSpanEvents(t, []internal.WantEvent{})
   111  }
   112  
   113  func TestSpanEventDatastoreExternal(t *testing.T) {
   114  	// Test that a datastore and external segments creates the correct span
   115  	// events.
   116  	replyfn := func(reply *internal.ConnectReply) {
   117  		reply.AdaptiveSampler = internal.SampleEverything{}
   118  	}
   119  	cfgfn := func(cfg *Config) {
   120  		cfg.DistributedTracer.Enabled = true
   121  	}
   122  	app := testApp(replyfn, cfgfn, t)
   123  	txn := app.StartTransaction("hello", nil, nil)
   124  	segment := DatastoreSegment{
   125  		StartTime:          StartSegmentNow(txn),
   126  		Product:            DatastoreMySQL,
   127  		Collection:         "mycollection",
   128  		Operation:          "myoperation",
   129  		ParameterizedQuery: "myquery",
   130  		Host:               "myhost",
   131  		PortPathOrID:       "myport",
   132  		DatabaseName:       "dbname",
   133  	}
   134  	segment.End()
   135  	req, _ := http.NewRequest("GET", "http://example.com?ignore=me", nil)
   136  	s := StartExternalSegment(txn, req)
   137  	s.End()
   138  	txn.End()
   139  	app.ExpectSpanEvents(t, []internal.WantEvent{
   140  		{
   141  			Intrinsics: map[string]interface{}{
   142  				"name":          "OtherTransaction/Go/hello",
   143  				"sampled":       true,
   144  				"category":      "generic",
   145  				"nr.entryPoint": true,
   146  			},
   147  			UserAttributes:  map[string]interface{}{},
   148  			AgentAttributes: map[string]interface{}{},
   149  		},
   150  		{
   151  			Intrinsics: map[string]interface{}{
   152  				"parentId":  internal.MatchAnything,
   153  				"sampled":   true,
   154  				"name":      "Datastore/statement/MySQL/mycollection/myoperation",
   155  				"category":  "datastore",
   156  				"component": "MySQL",
   157  				"span.kind": "client",
   158  			},
   159  			UserAttributes: map[string]interface{}{},
   160  			AgentAttributes: map[string]interface{}{
   161  				"db.statement":  "myquery",
   162  				"db.instance":   "dbname",
   163  				"db.collection": "mycollection",
   164  				"peer.address":  "myhost:myport",
   165  				"peer.hostname": "myhost",
   166  			},
   167  		},
   168  		{
   169  			Intrinsics: map[string]interface{}{
   170  				"parentId":  internal.MatchAnything,
   171  				"name":      "External/example.com/http/GET",
   172  				"category":  "http",
   173  				"component": "http",
   174  				"span.kind": "client",
   175  			},
   176  			UserAttributes: map[string]interface{}{},
   177  			AgentAttributes: map[string]interface{}{
   178  				"http.url":    "http://example.com",
   179  				"http.method": "GET",
   180  			},
   181  		},
   182  	})
   183  }
   184  
   185  func TestSpanEventAttributesDisabled(t *testing.T) {
   186  	// Test that SpanEvents.Attributes.Enabled correctly disables span
   187  	// attributes.
   188  	replyfn := func(reply *internal.ConnectReply) {
   189  		reply.AdaptiveSampler = internal.SampleEverything{}
   190  	}
   191  	cfgfn := func(cfg *Config) {
   192  		cfg.DistributedTracer.Enabled = true
   193  		cfg.SpanEvents.Attributes.Enabled = false
   194  	}
   195  	app := testApp(replyfn, cfgfn, t)
   196  	txn := app.StartTransaction("hello", nil, nil)
   197  	segment := DatastoreSegment{
   198  		StartTime:          StartSegmentNow(txn),
   199  		Product:            DatastoreMySQL,
   200  		Collection:         "mycollection",
   201  		Operation:          "myoperation",
   202  		ParameterizedQuery: "myquery",
   203  		Host:               "myhost",
   204  		PortPathOrID:       "myport",
   205  		DatabaseName:       "dbname",
   206  	}
   207  	segment.End()
   208  	req, _ := http.NewRequest("GET", "http://example.com?ignore=me", nil)
   209  	s := StartExternalSegment(txn, req)
   210  	s.End()
   211  	txn.End()
   212  	app.ExpectSpanEvents(t, []internal.WantEvent{
   213  		{
   214  			Intrinsics: map[string]interface{}{
   215  				"name":          "OtherTransaction/Go/hello",
   216  				"sampled":       true,
   217  				"category":      "generic",
   218  				"nr.entryPoint": true,
   219  			},
   220  			UserAttributes:  map[string]interface{}{},
   221  			AgentAttributes: map[string]interface{}{},
   222  		},
   223  		{
   224  			Intrinsics: map[string]interface{}{
   225  				"parentId":  internal.MatchAnything,
   226  				"sampled":   true,
   227  				"name":      "Datastore/statement/MySQL/mycollection/myoperation",
   228  				"category":  "datastore",
   229  				"component": "MySQL",
   230  				"span.kind": "client",
   231  			},
   232  			UserAttributes:  map[string]interface{}{},
   233  			AgentAttributes: map[string]interface{}{},
   234  		},
   235  		{
   236  			Intrinsics: map[string]interface{}{
   237  				"parentId":  internal.MatchAnything,
   238  				"name":      "External/example.com/http/GET",
   239  				"category":  "http",
   240  				"component": "http",
   241  				"span.kind": "client",
   242  			},
   243  			UserAttributes:  map[string]interface{}{},
   244  			AgentAttributes: map[string]interface{}{},
   245  		},
   246  	})
   247  }
   248  
   249  func TestSpanEventAttributesSpecificallyExcluded(t *testing.T) {
   250  	// Test that SpanEvents.Attributes.Exclude excludes span attributes.
   251  	replyfn := func(reply *internal.ConnectReply) {
   252  		reply.AdaptiveSampler = internal.SampleEverything{}
   253  	}
   254  	cfgfn := func(cfg *Config) {
   255  		cfg.DistributedTracer.Enabled = true
   256  		cfg.SpanEvents.Attributes.Exclude = []string{
   257  			SpanAttributeDBStatement,
   258  			SpanAttributeDBInstance,
   259  			SpanAttributeDBCollection,
   260  			SpanAttributePeerAddress,
   261  			SpanAttributePeerHostname,
   262  			SpanAttributeHTTPURL,
   263  			SpanAttributeHTTPMethod,
   264  		}
   265  	}
   266  	app := testApp(replyfn, cfgfn, t)
   267  	txn := app.StartTransaction("hello", nil, nil)
   268  	segment := DatastoreSegment{
   269  		StartTime:          StartSegmentNow(txn),
   270  		Product:            DatastoreMySQL,
   271  		Collection:         "mycollection",
   272  		Operation:          "myoperation",
   273  		ParameterizedQuery: "myquery",
   274  		Host:               "myhost",
   275  		PortPathOrID:       "myport",
   276  		DatabaseName:       "dbname",
   277  	}
   278  	segment.End()
   279  	req, _ := http.NewRequest("GET", "http://example.com?ignore=me", nil)
   280  	s := StartExternalSegment(txn, req)
   281  	s.End()
   282  	txn.End()
   283  	app.ExpectSpanEvents(t, []internal.WantEvent{
   284  		{
   285  			Intrinsics: map[string]interface{}{
   286  				"name":          "OtherTransaction/Go/hello",
   287  				"sampled":       true,
   288  				"category":      "generic",
   289  				"nr.entryPoint": true,
   290  			},
   291  			UserAttributes:  map[string]interface{}{},
   292  			AgentAttributes: map[string]interface{}{},
   293  		},
   294  		{
   295  			Intrinsics: map[string]interface{}{
   296  				"parentId":  internal.MatchAnything,
   297  				"sampled":   true,
   298  				"name":      "Datastore/statement/MySQL/mycollection/myoperation",
   299  				"category":  "datastore",
   300  				"component": "MySQL",
   301  				"span.kind": "client",
   302  			},
   303  			UserAttributes:  map[string]interface{}{},
   304  			AgentAttributes: map[string]interface{}{},
   305  		},
   306  		{
   307  			Intrinsics: map[string]interface{}{
   308  				"parentId":  internal.MatchAnything,
   309  				"name":      "External/example.com/http/GET",
   310  				"category":  "http",
   311  				"component": "http",
   312  				"span.kind": "client",
   313  			},
   314  			UserAttributes:  map[string]interface{}{},
   315  			AgentAttributes: map[string]interface{}{},
   316  		},
   317  	})
   318  }
   319  
   320  func TestSpanEventAttributesExcluded(t *testing.T) {
   321  	// Test that Attributes.Exclude excludes span attributes.
   322  	replyfn := func(reply *internal.ConnectReply) {
   323  		reply.AdaptiveSampler = internal.SampleEverything{}
   324  	}
   325  	cfgfn := func(cfg *Config) {
   326  		cfg.DistributedTracer.Enabled = true
   327  		cfg.Attributes.Exclude = []string{
   328  			SpanAttributeDBStatement,
   329  			SpanAttributeDBInstance,
   330  			SpanAttributeDBCollection,
   331  			SpanAttributePeerAddress,
   332  			SpanAttributePeerHostname,
   333  			SpanAttributeHTTPURL,
   334  			SpanAttributeHTTPMethod,
   335  		}
   336  	}
   337  	app := testApp(replyfn, cfgfn, t)
   338  	txn := app.StartTransaction("hello", nil, nil)
   339  	segment := DatastoreSegment{
   340  		StartTime:          StartSegmentNow(txn),
   341  		Product:            DatastoreMySQL,
   342  		Collection:         "mycollection",
   343  		Operation:          "myoperation",
   344  		ParameterizedQuery: "myquery",
   345  		Host:               "myhost",
   346  		PortPathOrID:       "myport",
   347  		DatabaseName:       "dbname",
   348  	}
   349  	segment.End()
   350  	req, _ := http.NewRequest("GET", "http://example.com?ignore=me", nil)
   351  	s := StartExternalSegment(txn, req)
   352  	s.End()
   353  	txn.End()
   354  	app.ExpectSpanEvents(t, []internal.WantEvent{
   355  		{
   356  			Intrinsics: map[string]interface{}{
   357  				"name":          "OtherTransaction/Go/hello",
   358  				"sampled":       true,
   359  				"category":      "generic",
   360  				"nr.entryPoint": true,
   361  			},
   362  			UserAttributes:  map[string]interface{}{},
   363  			AgentAttributes: map[string]interface{}{},
   364  		},
   365  		{
   366  			Intrinsics: map[string]interface{}{
   367  				"parentId":  internal.MatchAnything,
   368  				"sampled":   true,
   369  				"name":      "Datastore/statement/MySQL/mycollection/myoperation",
   370  				"category":  "datastore",
   371  				"component": "MySQL",
   372  				"span.kind": "client",
   373  			},
   374  			UserAttributes:  map[string]interface{}{},
   375  			AgentAttributes: map[string]interface{}{},
   376  		},
   377  		{
   378  			Intrinsics: map[string]interface{}{
   379  				"parentId":  internal.MatchAnything,
   380  				"name":      "External/example.com/http/GET",
   381  				"category":  "http",
   382  				"component": "http",
   383  				"span.kind": "client",
   384  			},
   385  			UserAttributes:  map[string]interface{}{},
   386  			AgentAttributes: map[string]interface{}{},
   387  		},
   388  	})
   389  }
   390  
   391  func TestSpanEventAttributesLASP(t *testing.T) {
   392  	// Test that security policies prevent the capture of the input query
   393  	// statement.
   394  	replyfn := func(reply *internal.ConnectReply) {
   395  		reply.AdaptiveSampler = internal.SampleEverything{}
   396  		reply.SecurityPolicies.RecordSQL.SetEnabled(false)
   397  	}
   398  	cfgfn := func(cfg *Config) {
   399  		cfg.DistributedTracer.Enabled = true
   400  	}
   401  	app := testApp(replyfn, cfgfn, t)
   402  	txn := app.StartTransaction("hello", nil, nil)
   403  	segment := DatastoreSegment{
   404  		StartTime:          StartSegmentNow(txn),
   405  		Product:            DatastoreMySQL,
   406  		Collection:         "mycollection",
   407  		Operation:          "myoperation",
   408  		ParameterizedQuery: "myquery",
   409  		Host:               "myhost",
   410  		PortPathOrID:       "myport",
   411  		DatabaseName:       "dbname",
   412  	}
   413  	segment.End()
   414  	req, _ := http.NewRequest("GET", "http://example.com?ignore=me", nil)
   415  	s := StartExternalSegment(txn, req)
   416  	s.End()
   417  	txn.End()
   418  	app.ExpectSpanEvents(t, []internal.WantEvent{
   419  		{
   420  			Intrinsics: map[string]interface{}{
   421  				"name":          "OtherTransaction/Go/hello",
   422  				"sampled":       true,
   423  				"category":      "generic",
   424  				"nr.entryPoint": true,
   425  			},
   426  			UserAttributes:  map[string]interface{}{},
   427  			AgentAttributes: map[string]interface{}{},
   428  		},
   429  		{
   430  			Intrinsics: map[string]interface{}{
   431  				"parentId":  internal.MatchAnything,
   432  				"sampled":   true,
   433  				"name":      "Datastore/statement/MySQL/mycollection/myoperation",
   434  				"category":  "datastore",
   435  				"component": "MySQL",
   436  				"span.kind": "client",
   437  			},
   438  			UserAttributes: map[string]interface{}{},
   439  			AgentAttributes: map[string]interface{}{
   440  				"db.instance":   "dbname",
   441  				"db.collection": "mycollection",
   442  				"peer.address":  "myhost:myport",
   443  				"peer.hostname": "myhost",
   444  				"db.statement":  "'myoperation' on 'mycollection' using 'MySQL'",
   445  			},
   446  		},
   447  		{
   448  			Intrinsics: map[string]interface{}{
   449  				"parentId":  internal.MatchAnything,
   450  				"name":      "External/example.com/http/GET",
   451  				"category":  "http",
   452  				"component": "http",
   453  				"span.kind": "client",
   454  			},
   455  			UserAttributes: map[string]interface{}{},
   456  			AgentAttributes: map[string]interface{}{
   457  				"http.url":    "http://example.com",
   458  				"http.method": "GET",
   459  			},
   460  		},
   461  	})
   462  }
   463  
   464  func TestAddAgentSpanAttribute(t *testing.T) {
   465  	// Test that AddAgentSpanAttribute successfully adds attributes to
   466  	// spans.
   467  	replyfn := func(reply *internal.ConnectReply) {
   468  		reply.AdaptiveSampler = internal.SampleEverything{}
   469  	}
   470  	cfgfn := func(cfg *Config) {
   471  		cfg.DistributedTracer.Enabled = true
   472  	}
   473  	app := testApp(replyfn, cfgfn, t)
   474  	txn := app.StartTransaction("hello", nil, nil)
   475  	s := StartSegment(txn, "hi")
   476  	internal.AddAgentSpanAttribute(txn, internal.SpanAttributeAWSRegion, "west")
   477  	internal.AddAgentSpanAttribute(txn, internal.SpanAttributeAWSRequestID, "123")
   478  	internal.AddAgentSpanAttribute(txn, internal.SpanAttributeAWSOperation, "secret")
   479  	s.End()
   480  	txn.End()
   481  	app.ExpectSpanEvents(t, []internal.WantEvent{
   482  		{
   483  			Intrinsics: map[string]interface{}{
   484  				"name":          "OtherTransaction/Go/hello",
   485  				"sampled":       true,
   486  				"category":      "generic",
   487  				"nr.entryPoint": true,
   488  			},
   489  			UserAttributes:  map[string]interface{}{},
   490  			AgentAttributes: map[string]interface{}{},
   491  		},
   492  		{
   493  			Intrinsics: map[string]interface{}{
   494  				"name":     "Custom/hi",
   495  				"sampled":  true,
   496  				"category": "generic",
   497  				"parentId": internal.MatchAnything,
   498  			},
   499  			UserAttributes: map[string]interface{}{},
   500  			AgentAttributes: map[string]interface{}{
   501  				"aws.operation": "secret",
   502  				"aws.requestId": "123",
   503  				"aws.region":    "west",
   504  			},
   505  		},
   506  	})
   507  }
   508  
   509  func TestAddAgentSpanAttributeExcluded(t *testing.T) {
   510  	// Test that span attributes added by AddAgentSpanAttribute are subject
   511  	// to span attribute configuration.
   512  	replyfn := func(reply *internal.ConnectReply) {
   513  		reply.AdaptiveSampler = internal.SampleEverything{}
   514  	}
   515  	cfgfn := func(cfg *Config) {
   516  		cfg.DistributedTracer.Enabled = true
   517  		cfg.SpanEvents.Attributes.Exclude = []string{
   518  			SpanAttributeAWSOperation,
   519  			SpanAttributeAWSRequestID,
   520  			SpanAttributeAWSRegion,
   521  		}
   522  	}
   523  	app := testApp(replyfn, cfgfn, t)
   524  	txn := app.StartTransaction("hello", nil, nil)
   525  	s := StartSegment(txn, "hi")
   526  	internal.AddAgentSpanAttribute(txn, internal.SpanAttributeAWSRegion, "west")
   527  	internal.AddAgentSpanAttribute(txn, internal.SpanAttributeAWSRequestID, "123")
   528  	internal.AddAgentSpanAttribute(txn, internal.SpanAttributeAWSOperation, "secret")
   529  	s.End()
   530  	txn.End()
   531  	app.ExpectSpanEvents(t, []internal.WantEvent{
   532  		{
   533  			Intrinsics: map[string]interface{}{
   534  				"name":          "OtherTransaction/Go/hello",
   535  				"sampled":       true,
   536  				"category":      "generic",
   537  				"nr.entryPoint": true,
   538  			},
   539  			UserAttributes:  map[string]interface{}{},
   540  			AgentAttributes: map[string]interface{}{},
   541  		},
   542  		{
   543  			Intrinsics: map[string]interface{}{
   544  				"name":     "Custom/hi",
   545  				"sampled":  true,
   546  				"category": "generic",
   547  				"parentId": internal.MatchAnything,
   548  			},
   549  			UserAttributes:  map[string]interface{}{},
   550  			AgentAttributes: map[string]interface{}{},
   551  		},
   552  	})
   553  }
   554  
   555  func TestAddSpanAttributeNoActiveSpan(t *testing.T) {
   556  	// Test that AddAgentSpanAttribute does not have problems if called when
   557  	// there is no active span.
   558  	replyfn := func(reply *internal.ConnectReply) {
   559  		reply.AdaptiveSampler = internal.SampleEverything{}
   560  	}
   561  	cfgfn := func(cfg *Config) {
   562  		cfg.DistributedTracer.Enabled = true
   563  	}
   564  	app := testApp(replyfn, cfgfn, t)
   565  	txn := app.StartTransaction("hello", nil, nil)
   566  	// Do not panic if there are no active spans!
   567  	internal.AddAgentSpanAttribute(txn, internal.SpanAttributeAWSRegion, "west")
   568  	internal.AddAgentSpanAttribute(txn, internal.SpanAttributeAWSRequestID, "123")
   569  	internal.AddAgentSpanAttribute(txn, internal.SpanAttributeAWSOperation, "secret")
   570  	txn.End()
   571  	app.ExpectSpanEvents(t, []internal.WantEvent{
   572  		{
   573  			Intrinsics: map[string]interface{}{
   574  				"name":          "OtherTransaction/Go/hello",
   575  				"sampled":       true,
   576  				"category":      "generic",
   577  				"nr.entryPoint": true,
   578  			},
   579  			UserAttributes:  map[string]interface{}{},
   580  			AgentAttributes: map[string]interface{}{},
   581  		},
   582  	})
   583  }
   584  
   585  func TestAddSpanAttributeNilTransaction(t *testing.T) {
   586  	// Test that AddAgentSpanAttribute does not panic if the transaction is
   587  	// nil.
   588  	internal.AddAgentSpanAttribute(nil, internal.SpanAttributeAWSRegion, "west")
   589  	internal.AddAgentSpanAttribute(nil, internal.SpanAttributeAWSRequestID, "123")
   590  	internal.AddAgentSpanAttribute(nil, internal.SpanAttributeAWSOperation, "secret")
   591  }