(tokens []string)
| 133 | } |
| 134 | |
| 135 | func (d *Drain) findMatchingClusterForTokens(tokens []string) *LogCluster { |
| 136 | tokenCount := len(tokens) |
| 137 | |
| 138 | // at first level, children are grouped by token (word) count |
| 139 | curNode, ok := d.rootNode.keyToChildNode[strconv.Itoa(tokenCount)] |
| 140 | if !ok { |
| 141 | // no template with same token count yet |
| 142 | return nil |
| 143 | } |
| 144 | |
| 145 | // we always end the token list with an <END> token, so <2 tokens is a |
| 146 | // special case for an empty input string. In this case, we return the |
| 147 | // single cluster in that group. |
| 148 | if tokenCount < 2 { |
| 149 | return d.idToCluster.Get(curNode.clusterIDs[0]) |
| 150 | } |
| 151 | |
| 152 | // otherwise, we need to find the leaf node for this log. |
| 153 | curNodeDepth := 1 |
| 154 | for _, token := range tokens { |
| 155 | // at max depth |
| 156 | if curNodeDepth >= d.maxNodeDepth { |
| 157 | break |
| 158 | } |
| 159 | |
| 160 | // this is last token |
| 161 | if curNodeDepth == tokenCount { |
| 162 | break |
| 163 | } |
| 164 | |
| 165 | keyToChildNode := curNode.keyToChildNode |
| 166 | curNode, ok = keyToChildNode[d.config.ParamString] |
| 167 | if !ok { // no wildcard node, try exact match |
| 168 | curNode, ok = keyToChildNode[token] |
| 169 | } |
| 170 | if !ok { // no existing path |
| 171 | return nil |
| 172 | } |
| 173 | curNodeDepth++ |
| 174 | } |
| 175 | |
| 176 | // get best match among all clusters with same prefix, or None if no match is above sim_th |
| 177 | cluster := d.findBestClusterForTokens(curNode.clusterIDs, tokens) |
| 178 | return cluster |
| 179 | } |
| 180 | |
| 181 | // findBestClusterForTokens finds the best match for a log message (represented |
| 182 | // as tokens) versus a list of clusters. A Match is considered better if it has |
no test coverage detected