123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- package internal
- // https://newrelic.atlassian.net/wiki/display/eng/Language+agent+transaction+segment+terms+rules
- import (
- "encoding/json"
- "strings"
- )
- const (
- placeholder = "*"
- separator = "/"
- )
- type segmentRule struct {
- Prefix string `json:"prefix"`
- Terms []string `json:"terms"`
- TermsMap map[string]struct{}
- }
- // segmentRules is keyed by each segmentRule's Prefix field with any trailing
- // slash removed.
- type segmentRules map[string]*segmentRule
- func buildTermsMap(terms []string) map[string]struct{} {
- m := make(map[string]struct{}, len(terms))
- for _, t := range terms {
- m[t] = struct{}{}
- }
- return m
- }
- func (rules *segmentRules) UnmarshalJSON(b []byte) error {
- var raw []*segmentRule
- if err := json.Unmarshal(b, &raw); nil != err {
- return err
- }
- rs := make(map[string]*segmentRule)
- for _, rule := range raw {
- prefix := strings.TrimSuffix(rule.Prefix, "/")
- if len(strings.Split(prefix, "/")) != 2 {
- // TODO
- // Warn("invalid segment term rule prefix",
- // {"prefix": rule.Prefix})
- continue
- }
- if nil == rule.Terms {
- // TODO
- // Warn("segment term rule has missing terms",
- // {"prefix": rule.Prefix})
- continue
- }
- rule.TermsMap = buildTermsMap(rule.Terms)
- rs[prefix] = rule
- }
- *rules = rs
- return nil
- }
- func (rule *segmentRule) apply(name string) string {
- if !strings.HasPrefix(name, rule.Prefix) {
- return name
- }
- s := strings.TrimPrefix(name, rule.Prefix)
- leadingSlash := ""
- if strings.HasPrefix(s, separator) {
- leadingSlash = separator
- s = strings.TrimPrefix(s, separator)
- }
- if "" != s {
- segments := strings.Split(s, separator)
- for i, segment := range segments {
- _, whitelisted := rule.TermsMap[segment]
- if whitelisted {
- segments[i] = segment
- } else {
- segments[i] = placeholder
- }
- }
- segments = collapsePlaceholders(segments)
- s = strings.Join(segments, separator)
- }
- return rule.Prefix + leadingSlash + s
- }
- func (rules segmentRules) apply(name string) string {
- if nil == rules {
- return name
- }
- rule, ok := rules[firstTwoSegments(name)]
- if !ok {
- return name
- }
- return rule.apply(name)
- }
- func firstTwoSegments(name string) string {
- firstSlashIdx := strings.Index(name, separator)
- if firstSlashIdx == -1 {
- return name
- }
- secondSlashIdx := strings.Index(name[firstSlashIdx+1:], separator)
- if secondSlashIdx == -1 {
- return name
- }
- return name[0 : firstSlashIdx+secondSlashIdx+1]
- }
- func collapsePlaceholders(segments []string) []string {
- j := 0
- prevStar := false
- for i := 0; i < len(segments); i++ {
- segment := segments[i]
- if placeholder == segment {
- if prevStar {
- continue
- }
- segments[j] = placeholder
- j++
- prevStar = true
- } else {
- segments[j] = segment
- j++
- prevStar = false
- }
- }
- return segments[0:j]
- }
|