github.com/lulzWill/go-agent@v2.1.2+incompatible/internal/segment_terms.go (about) 1 package internal 2 3 // https://newrelic.atlassian.net/wiki/display/eng/Language+agent+transaction+segment+terms+rules 4 5 import ( 6 "encoding/json" 7 "strings" 8 ) 9 10 const ( 11 placeholder = "*" 12 separator = "/" 13 ) 14 15 type segmentRule struct { 16 Prefix string `json:"prefix"` 17 Terms []string `json:"terms"` 18 TermsMap map[string]struct{} 19 } 20 21 // segmentRules is keyed by each segmentRule's Prefix field with any trailing 22 // slash removed. 23 type segmentRules map[string]*segmentRule 24 25 func buildTermsMap(terms []string) map[string]struct{} { 26 m := make(map[string]struct{}, len(terms)) 27 for _, t := range terms { 28 m[t] = struct{}{} 29 } 30 return m 31 } 32 33 func (rules *segmentRules) UnmarshalJSON(b []byte) error { 34 var raw []*segmentRule 35 36 if err := json.Unmarshal(b, &raw); nil != err { 37 return err 38 } 39 40 rs := make(map[string]*segmentRule) 41 42 for _, rule := range raw { 43 prefix := strings.TrimSuffix(rule.Prefix, "/") 44 if len(strings.Split(prefix, "/")) != 2 { 45 // TODO 46 // Warn("invalid segment term rule prefix", 47 // {"prefix": rule.Prefix}) 48 continue 49 } 50 51 if nil == rule.Terms { 52 // TODO 53 // Warn("segment term rule has missing terms", 54 // {"prefix": rule.Prefix}) 55 continue 56 } 57 58 rule.TermsMap = buildTermsMap(rule.Terms) 59 60 rs[prefix] = rule 61 } 62 63 *rules = rs 64 return nil 65 } 66 67 func (rule *segmentRule) apply(name string) string { 68 if !strings.HasPrefix(name, rule.Prefix) { 69 return name 70 } 71 72 s := strings.TrimPrefix(name, rule.Prefix) 73 74 leadingSlash := "" 75 if strings.HasPrefix(s, separator) { 76 leadingSlash = separator 77 s = strings.TrimPrefix(s, separator) 78 } 79 80 if "" != s { 81 segments := strings.Split(s, separator) 82 83 for i, segment := range segments { 84 _, whitelisted := rule.TermsMap[segment] 85 if whitelisted { 86 segments[i] = segment 87 } else { 88 segments[i] = placeholder 89 } 90 } 91 92 segments = collapsePlaceholders(segments) 93 s = strings.Join(segments, separator) 94 } 95 96 return rule.Prefix + leadingSlash + s 97 } 98 99 func (rules segmentRules) apply(name string) string { 100 if nil == rules { 101 return name 102 } 103 104 rule, ok := rules[firstTwoSegments(name)] 105 if !ok { 106 return name 107 } 108 109 return rule.apply(name) 110 } 111 112 func firstTwoSegments(name string) string { 113 firstSlashIdx := strings.Index(name, separator) 114 if firstSlashIdx == -1 { 115 return name 116 } 117 118 secondSlashIdx := strings.Index(name[firstSlashIdx+1:], separator) 119 if secondSlashIdx == -1 { 120 return name 121 } 122 123 return name[0 : firstSlashIdx+secondSlashIdx+1] 124 } 125 126 func collapsePlaceholders(segments []string) []string { 127 j := 0 128 prevStar := false 129 for i := 0; i < len(segments); i++ { 130 segment := segments[i] 131 if placeholder == segment { 132 if prevStar { 133 continue 134 } 135 segments[j] = placeholder 136 j++ 137 prevStar = true 138 } else { 139 segments[j] = segment 140 j++ 141 prevStar = false 142 } 143 } 144 return segments[0:j] 145 }