attributes.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. package internal
  2. import (
  3. "bytes"
  4. "fmt"
  5. "net/http"
  6. "sort"
  7. "strconv"
  8. "strings"
  9. )
  10. // New agent attributes must be added in the following places:
  11. // * Constants here.
  12. // * Top level attributes.go file.
  13. // * agentAttributes
  14. // * agentAttributeDests
  15. // * calculateAgentAttributeDests
  16. // * writeAgentAttributes
  17. const (
  18. responseCode = "httpResponseCode"
  19. requestMethod = "request.method"
  20. requestAccept = "request.headers.accept"
  21. requestContentType = "request.headers.contentType"
  22. requestContentLength = "request.headers.contentLength"
  23. requestHost = "request.headers.host"
  24. responseContentType = "response.headers.contentType"
  25. responseContentLength = "response.headers.contentLength"
  26. hostDisplayName = "host.displayName"
  27. requestUserAgent = "request.headers.User-Agent"
  28. requestReferer = "request.headers.referer"
  29. )
  30. // https://source.datanerd.us/agents/agent-specs/blob/master/Agent-Attributes-PORTED.md
  31. // AttributeDestinationConfig matches newrelic.AttributeDestinationConfig to
  32. // avoid circular dependency issues.
  33. type AttributeDestinationConfig struct {
  34. Enabled bool
  35. Include []string
  36. Exclude []string
  37. }
  38. type destinationSet int
  39. const (
  40. destTxnEvent destinationSet = 1 << iota
  41. destError
  42. destTxnTrace
  43. destBrowser
  44. )
  45. const (
  46. destNone destinationSet = 0
  47. // DestAll contains all destinations.
  48. DestAll destinationSet = destTxnEvent | destTxnTrace | destError | destBrowser
  49. )
  50. const (
  51. attributeWildcardSuffix = '*'
  52. )
  53. type attributeModifier struct {
  54. match string // This will not contain a trailing '*'.
  55. includeExclude
  56. }
  57. type byMatch []*attributeModifier
  58. func (m byMatch) Len() int { return len(m) }
  59. func (m byMatch) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
  60. func (m byMatch) Less(i, j int) bool { return m[i].match < m[j].match }
  61. // AttributeConfig is created at connect and shared between all transactions.
  62. type AttributeConfig struct {
  63. disabledDestinations destinationSet
  64. exactMatchModifiers map[string]*attributeModifier
  65. // Once attributeConfig is constructed, wildcardModifiers is sorted in
  66. // lexicographical order. Modifiers appearing later have precedence
  67. // over modifiers appearing earlier.
  68. wildcardModifiers []*attributeModifier
  69. agentDests agentAttributeDests
  70. }
  71. type includeExclude struct {
  72. include destinationSet
  73. exclude destinationSet
  74. }
  75. func modifierApply(m *attributeModifier, d destinationSet) destinationSet {
  76. // Include before exclude, since exclude has priority.
  77. d |= m.include
  78. d &^= m.exclude
  79. return d
  80. }
  81. func applyAttributeConfig(c *AttributeConfig, key string, d destinationSet) destinationSet {
  82. // Important: The wildcard modifiers must be applied before the exact
  83. // match modifiers, and the slice must be iterated in a forward
  84. // direction.
  85. for _, m := range c.wildcardModifiers {
  86. if strings.HasPrefix(key, m.match) {
  87. d = modifierApply(m, d)
  88. }
  89. }
  90. if m, ok := c.exactMatchModifiers[key]; ok {
  91. d = modifierApply(m, d)
  92. }
  93. d &^= c.disabledDestinations
  94. return d
  95. }
  96. func addModifier(c *AttributeConfig, match string, d includeExclude) {
  97. if "" == match {
  98. return
  99. }
  100. exactMatch := true
  101. if attributeWildcardSuffix == match[len(match)-1] {
  102. exactMatch = false
  103. match = match[0 : len(match)-1]
  104. }
  105. mod := &attributeModifier{
  106. match: match,
  107. includeExclude: d,
  108. }
  109. if exactMatch {
  110. if m, ok := c.exactMatchModifiers[mod.match]; ok {
  111. m.include |= mod.include
  112. m.exclude |= mod.exclude
  113. } else {
  114. c.exactMatchModifiers[mod.match] = mod
  115. }
  116. } else {
  117. for _, m := range c.wildcardModifiers {
  118. // Important: Duplicate entries for the same match
  119. // string would not work because exclude needs
  120. // precedence over include.
  121. if m.match == mod.match {
  122. m.include |= mod.include
  123. m.exclude |= mod.exclude
  124. return
  125. }
  126. }
  127. c.wildcardModifiers = append(c.wildcardModifiers, mod)
  128. }
  129. }
  130. func processDest(c *AttributeConfig, includeEnabled bool, dc *AttributeDestinationConfig, d destinationSet) {
  131. if !dc.Enabled {
  132. c.disabledDestinations |= d
  133. }
  134. if includeEnabled {
  135. for _, match := range dc.Include {
  136. addModifier(c, match, includeExclude{include: d})
  137. }
  138. }
  139. for _, match := range dc.Exclude {
  140. addModifier(c, match, includeExclude{exclude: d})
  141. }
  142. }
  143. // AttributeConfigInput is used as the input to CreateAttributeConfig: it
  144. // transforms newrelic.Config settings into an AttributeConfig.
  145. type AttributeConfigInput struct {
  146. Attributes AttributeDestinationConfig
  147. ErrorCollector AttributeDestinationConfig
  148. TransactionEvents AttributeDestinationConfig
  149. browserMonitoring AttributeDestinationConfig
  150. TransactionTracer AttributeDestinationConfig
  151. }
  152. var (
  153. sampleAttributeConfigInput = AttributeConfigInput{
  154. Attributes: AttributeDestinationConfig{Enabled: true},
  155. ErrorCollector: AttributeDestinationConfig{Enabled: true},
  156. TransactionEvents: AttributeDestinationConfig{Enabled: true},
  157. TransactionTracer: AttributeDestinationConfig{Enabled: true},
  158. }
  159. )
  160. // CreateAttributeConfig creates a new AttributeConfig.
  161. func CreateAttributeConfig(input AttributeConfigInput, includeEnabled bool) *AttributeConfig {
  162. c := &AttributeConfig{
  163. exactMatchModifiers: make(map[string]*attributeModifier),
  164. wildcardModifiers: make([]*attributeModifier, 0, 64),
  165. }
  166. processDest(c, includeEnabled, &input.Attributes, DestAll)
  167. processDest(c, includeEnabled, &input.ErrorCollector, destError)
  168. processDest(c, includeEnabled, &input.TransactionEvents, destTxnEvent)
  169. processDest(c, includeEnabled, &input.TransactionTracer, destTxnTrace)
  170. processDest(c, includeEnabled, &input.browserMonitoring, destBrowser)
  171. sort.Sort(byMatch(c.wildcardModifiers))
  172. c.agentDests = calculateAgentAttributeDests(c)
  173. return c
  174. }
  175. type userAttribute struct {
  176. value interface{}
  177. dests destinationSet
  178. }
  179. // Attributes are key value pairs attached to the various collected data types.
  180. type Attributes struct {
  181. config *AttributeConfig
  182. user map[string]userAttribute
  183. Agent agentAttributes
  184. }
  185. type agentAttributes struct {
  186. HostDisplayName string
  187. RequestMethod string
  188. RequestAcceptHeader string
  189. RequestContentType string
  190. RequestContentLength int
  191. RequestHeadersHost string
  192. RequestHeadersUserAgent string
  193. RequestHeadersReferer string
  194. ResponseHeadersContentType string
  195. ResponseHeadersContentLength int
  196. ResponseCode string
  197. }
  198. type agentAttributeDests struct {
  199. HostDisplayName destinationSet
  200. RequestMethod destinationSet
  201. RequestAcceptHeader destinationSet
  202. RequestContentType destinationSet
  203. RequestContentLength destinationSet
  204. RequestHeadersHost destinationSet
  205. RequestHeadersUserAgent destinationSet
  206. RequestHeadersReferer destinationSet
  207. ResponseHeadersContentType destinationSet
  208. ResponseHeadersContentLength destinationSet
  209. ResponseCode destinationSet
  210. }
  211. func calculateAgentAttributeDests(c *AttributeConfig) agentAttributeDests {
  212. usual := DestAll &^ destBrowser
  213. traces := destTxnTrace | destError
  214. return agentAttributeDests{
  215. HostDisplayName: applyAttributeConfig(c, hostDisplayName, usual),
  216. RequestMethod: applyAttributeConfig(c, requestMethod, usual),
  217. RequestAcceptHeader: applyAttributeConfig(c, requestAccept, usual),
  218. RequestContentType: applyAttributeConfig(c, requestContentType, usual),
  219. RequestContentLength: applyAttributeConfig(c, requestContentLength, usual),
  220. RequestHeadersHost: applyAttributeConfig(c, requestHost, usual),
  221. RequestHeadersUserAgent: applyAttributeConfig(c, requestUserAgent, traces),
  222. RequestHeadersReferer: applyAttributeConfig(c, requestReferer, traces),
  223. ResponseHeadersContentType: applyAttributeConfig(c, responseContentType, usual),
  224. ResponseHeadersContentLength: applyAttributeConfig(c, responseContentLength, usual),
  225. ResponseCode: applyAttributeConfig(c, responseCode, usual),
  226. }
  227. }
  228. type agentAttributeWriter struct {
  229. jsonFieldsWriter
  230. d destinationSet
  231. }
  232. func (w *agentAttributeWriter) writeString(name string, val string, d destinationSet) {
  233. if "" != val && 0 != w.d&d {
  234. w.stringField(name, truncateStringValueIfLong(val))
  235. }
  236. }
  237. func (w *agentAttributeWriter) writeInt(name string, val int, d destinationSet) {
  238. if val >= 0 && 0 != w.d&d {
  239. w.intField(name, int64(val))
  240. }
  241. }
  242. func writeAgentAttributes(buf *bytes.Buffer, d destinationSet, values agentAttributes, dests agentAttributeDests) {
  243. w := &agentAttributeWriter{
  244. jsonFieldsWriter: jsonFieldsWriter{buf: buf},
  245. d: d,
  246. }
  247. buf.WriteByte('{')
  248. w.writeString(hostDisplayName, values.HostDisplayName, dests.HostDisplayName)
  249. w.writeString(requestMethod, values.RequestMethod, dests.RequestMethod)
  250. w.writeString(requestAccept, values.RequestAcceptHeader, dests.RequestAcceptHeader)
  251. w.writeString(requestContentType, values.RequestContentType, dests.RequestContentType)
  252. w.writeInt(requestContentLength, values.RequestContentLength, dests.RequestContentLength)
  253. w.writeString(requestHost, values.RequestHeadersHost, dests.RequestHeadersHost)
  254. w.writeString(requestUserAgent, values.RequestHeadersUserAgent, dests.RequestHeadersUserAgent)
  255. w.writeString(requestReferer, values.RequestHeadersReferer, dests.RequestHeadersReferer)
  256. w.writeString(responseContentType, values.ResponseHeadersContentType, dests.ResponseHeadersContentType)
  257. w.writeInt(responseContentLength, values.ResponseHeadersContentLength, dests.ResponseHeadersContentLength)
  258. w.writeString(responseCode, values.ResponseCode, dests.ResponseCode)
  259. buf.WriteByte('}')
  260. }
  261. // NewAttributes creates a new Attributes.
  262. func NewAttributes(config *AttributeConfig) *Attributes {
  263. return &Attributes{
  264. config: config,
  265. Agent: agentAttributes{
  266. RequestContentLength: -1,
  267. ResponseHeadersContentLength: -1,
  268. },
  269. }
  270. }
  271. // ErrInvalidAttributeType is returned when the value is not valid.
  272. type ErrInvalidAttributeType struct {
  273. key string
  274. val interface{}
  275. }
  276. func (e ErrInvalidAttributeType) Error() string {
  277. return fmt.Sprintf("attribute '%s' value of type %T is invalid", e.key, e.val)
  278. }
  279. type invalidAttributeKeyErr struct{ key string }
  280. func (e invalidAttributeKeyErr) Error() string {
  281. return fmt.Sprintf("attribute key '%.32s...' exceeds length limit %d",
  282. e.key, attributeKeyLengthLimit)
  283. }
  284. type userAttributeLimitErr struct{ key string }
  285. func (e userAttributeLimitErr) Error() string {
  286. return fmt.Sprintf("attribute '%s' discarded: limit of %d reached", e.key,
  287. attributeUserLimit)
  288. }
  289. func truncateStringValueIfLong(val string) string {
  290. if len(val) > attributeValueLengthLimit {
  291. return StringLengthByteLimit(val, attributeValueLengthLimit)
  292. }
  293. return val
  294. }
  295. // ValidateUserAttribute validates a user attribute.
  296. func ValidateUserAttribute(key string, val interface{}) (interface{}, error) {
  297. if str, ok := val.(string); ok {
  298. val = interface{}(truncateStringValueIfLong(str))
  299. }
  300. switch val.(type) {
  301. case string, bool, nil,
  302. uint8, uint16, uint32, uint64, int8, int16, int32, int64,
  303. float32, float64, uint, int, uintptr:
  304. default:
  305. return nil, ErrInvalidAttributeType{
  306. key: key,
  307. val: val,
  308. }
  309. }
  310. // Attributes whose keys are excessively long are dropped rather than
  311. // truncated to avoid worrying about the application of configuration to
  312. // truncated values or performing the truncation after configuration.
  313. if len(key) > attributeKeyLengthLimit {
  314. return nil, invalidAttributeKeyErr{key: key}
  315. }
  316. return val, nil
  317. }
  318. // AddUserAttribute adds a user attribute.
  319. func AddUserAttribute(a *Attributes, key string, val interface{}, d destinationSet) error {
  320. val, err := ValidateUserAttribute(key, val)
  321. if nil != err {
  322. return err
  323. }
  324. dests := applyAttributeConfig(a.config, key, d)
  325. if destNone == dests {
  326. return nil
  327. }
  328. if nil == a.user {
  329. a.user = make(map[string]userAttribute)
  330. }
  331. if _, exists := a.user[key]; !exists && len(a.user) >= attributeUserLimit {
  332. return userAttributeLimitErr{key}
  333. }
  334. // Note: Duplicates are overridden: last attribute in wins.
  335. a.user[key] = userAttribute{
  336. value: val,
  337. dests: dests,
  338. }
  339. return nil
  340. }
  341. func writeAttributeValueJSON(w *jsonFieldsWriter, key string, val interface{}) {
  342. switch v := val.(type) {
  343. case nil:
  344. w.rawField(key, `null`)
  345. case string:
  346. w.stringField(key, v)
  347. case bool:
  348. if v {
  349. w.rawField(key, `true`)
  350. } else {
  351. w.rawField(key, `false`)
  352. }
  353. case uint8:
  354. w.intField(key, int64(v))
  355. case uint16:
  356. w.intField(key, int64(v))
  357. case uint32:
  358. w.intField(key, int64(v))
  359. case uint64:
  360. w.intField(key, int64(v))
  361. case uint:
  362. w.intField(key, int64(v))
  363. case uintptr:
  364. w.intField(key, int64(v))
  365. case int8:
  366. w.intField(key, int64(v))
  367. case int16:
  368. w.intField(key, int64(v))
  369. case int32:
  370. w.intField(key, int64(v))
  371. case int64:
  372. w.intField(key, v)
  373. case int:
  374. w.intField(key, int64(v))
  375. case float32:
  376. w.floatField(key, float64(v))
  377. case float64:
  378. w.floatField(key, v)
  379. default:
  380. w.stringField(key, fmt.Sprintf("%T", v))
  381. }
  382. }
  383. func agentAttributesJSON(a *Attributes, buf *bytes.Buffer, d destinationSet) {
  384. if nil == a {
  385. buf.WriteString("{}")
  386. return
  387. }
  388. writeAgentAttributes(buf, d, a.Agent, a.config.agentDests)
  389. }
  390. func userAttributesJSON(a *Attributes, buf *bytes.Buffer, d destinationSet, extraAttributes map[string]interface{}) {
  391. buf.WriteByte('{')
  392. if nil != a {
  393. w := jsonFieldsWriter{buf: buf}
  394. for key, val := range extraAttributes {
  395. outputDest := applyAttributeConfig(a.config, key, d)
  396. if 0 != outputDest&d {
  397. writeAttributeValueJSON(&w, key, val)
  398. }
  399. }
  400. for name, atr := range a.user {
  401. if 0 != atr.dests&d {
  402. if _, found := extraAttributes[name]; found {
  403. continue
  404. }
  405. writeAttributeValueJSON(&w, name, atr.value)
  406. }
  407. }
  408. }
  409. buf.WriteByte('}')
  410. }
  411. // userAttributesStringJSON is only used for testing.
  412. func userAttributesStringJSON(a *Attributes, d destinationSet, extraAttributes map[string]interface{}) string {
  413. estimate := len(a.user) * 128
  414. buf := bytes.NewBuffer(make([]byte, 0, estimate))
  415. userAttributesJSON(a, buf, d, extraAttributes)
  416. return buf.String()
  417. }
  418. // RequestAgentAttributes gathers agent attributes out of the request.
  419. func RequestAgentAttributes(a *Attributes, r *http.Request) {
  420. a.Agent.RequestMethod = r.Method
  421. h := r.Header
  422. if nil == h {
  423. return
  424. }
  425. a.Agent.RequestAcceptHeader = h.Get("Accept")
  426. a.Agent.RequestContentType = h.Get("Content-Type")
  427. a.Agent.RequestHeadersHost = h.Get("Host")
  428. a.Agent.RequestHeadersUserAgent = h.Get("User-Agent")
  429. a.Agent.RequestHeadersReferer = SafeURLFromString(h.Get("Referer"))
  430. // Per NewAttributes(), the default for this field is -1 (which is also what
  431. // GetContentLengthFromHeader() returns if no content length is found), so we
  432. // can just use the return value unconditionally.
  433. a.Agent.RequestContentLength = int(GetContentLengthFromHeader(h))
  434. }
  435. // ResponseHeaderAttributes gather agent attributes from the response headers.
  436. func ResponseHeaderAttributes(a *Attributes, h http.Header) {
  437. if nil == h {
  438. return
  439. }
  440. a.Agent.ResponseHeadersContentType = h.Get("Content-Type")
  441. // Per NewAttributes(), the default for this field is -1 (which is also what
  442. // GetContentLengthFromHeader() returns if no content length is found), so we
  443. // can just use the return value unconditionally.
  444. a.Agent.ResponseHeadersContentLength = int(GetContentLengthFromHeader(h))
  445. }
  446. var (
  447. // statusCodeLookup avoids a strconv.Itoa call.
  448. statusCodeLookup = map[int]string{
  449. 100: "100", 101: "101",
  450. 200: "200", 201: "201", 202: "202", 203: "203", 204: "204", 205: "205", 206: "206",
  451. 300: "300", 301: "301", 302: "302", 303: "303", 304: "304", 305: "305", 307: "307",
  452. 400: "400", 401: "401", 402: "402", 403: "403", 404: "404", 405: "405", 406: "406",
  453. 407: "407", 408: "408", 409: "409", 410: "410", 411: "411", 412: "412", 413: "413",
  454. 414: "414", 415: "415", 416: "416", 417: "417", 418: "418", 428: "428", 429: "429",
  455. 431: "431", 451: "451",
  456. 500: "500", 501: "501", 502: "502", 503: "503", 504: "504", 505: "505", 511: "511",
  457. }
  458. )
  459. // ResponseCodeAttribute sets the response code agent attribute.
  460. func ResponseCodeAttribute(a *Attributes, code int) {
  461. a.Agent.ResponseCode = statusCodeLookup[code]
  462. if a.Agent.ResponseCode == "" {
  463. a.Agent.ResponseCode = strconv.Itoa(code)
  464. }
  465. }