github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/logql/log/fmt.go (about) 1 package log 2 3 import ( 4 "bytes" 5 "fmt" 6 "strings" 7 "text/template" 8 "text/template/parse" 9 "time" 10 11 "github.com/Masterminds/sprig/v3" 12 "github.com/grafana/regexp" 13 14 "github.com/grafana/loki/pkg/logqlmodel" 15 ) 16 17 const ( 18 functionLineName = "__line__" 19 functionTimestampName = "__timestamp__" 20 ) 21 22 var ( 23 _ Stage = &LineFormatter{} 24 _ Stage = &LabelsFormatter{} 25 26 // Available map of functions for the text template engine. 27 functionMap = template.FuncMap{ 28 // olds functions deprecated. 29 "ToLower": strings.ToLower, 30 "ToUpper": strings.ToUpper, 31 "Replace": strings.Replace, 32 "Trim": strings.Trim, 33 "TrimLeft": strings.TrimLeft, 34 "TrimRight": strings.TrimRight, 35 "TrimPrefix": strings.TrimPrefix, 36 "TrimSuffix": strings.TrimSuffix, 37 "TrimSpace": strings.TrimSpace, 38 "regexReplaceAll": func(regex string, s string, repl string) string { 39 r := regexp.MustCompile(regex) 40 return r.ReplaceAllString(s, repl) 41 }, 42 "regexReplaceAllLiteral": func(regex string, s string, repl string) string { 43 r := regexp.MustCompile(regex) 44 return r.ReplaceAllLiteralString(s, repl) 45 }, 46 } 47 48 // sprig template functions 49 templateFunctions = []string{ 50 "lower", 51 "upper", 52 "title", 53 "trunc", 54 "substr", 55 "contains", 56 "hasPrefix", 57 "hasSuffix", 58 "indent", 59 "nindent", 60 "replace", 61 "repeat", 62 "trim", 63 "trimAll", 64 "trimSuffix", 65 "trimPrefix", 66 "int", 67 "float64", 68 "add", 69 "sub", 70 "mul", 71 "div", 72 "mod", 73 "addf", 74 "subf", 75 "mulf", 76 "divf", 77 "max", 78 "min", 79 "maxf", 80 "minf", 81 "ceil", 82 "floor", 83 "round", 84 "fromJson", 85 "date", 86 "toDate", 87 "now", 88 "unixEpoch", 89 "default", 90 } 91 ) 92 93 func init() { 94 sprigFuncMap := sprig.GenericFuncMap() 95 for _, v := range templateFunctions { 96 if function, ok := sprigFuncMap[v]; ok { 97 functionMap[v] = function 98 } 99 } 100 } 101 102 type LineFormatter struct { 103 *template.Template 104 buf *bytes.Buffer 105 106 currentLine []byte 107 currentTs int64 108 } 109 110 // NewFormatter creates a new log line formatter from a given text template. 111 func NewFormatter(tmpl string) (*LineFormatter, error) { 112 lf := &LineFormatter{ 113 buf: bytes.NewBuffer(make([]byte, 4096)), 114 } 115 functions := make(map[string]interface{}, len(functionMap)+1) 116 for k, v := range functionMap { 117 functions[k] = v 118 } 119 functions[functionLineName] = func() string { 120 return unsafeGetString(lf.currentLine) 121 } 122 functions[functionTimestampName] = func() time.Time { 123 return time.Unix(0, lf.currentTs) 124 } 125 t, err := template.New("line").Option("missingkey=zero").Funcs(functions).Parse(tmpl) 126 if err != nil { 127 return nil, fmt.Errorf("invalid line template: %w", err) 128 } 129 lf.Template = t 130 return lf, nil 131 } 132 133 func (lf *LineFormatter) Process(ts int64, line []byte, lbs *LabelsBuilder) ([]byte, bool) { 134 lf.buf.Reset() 135 lf.currentLine = line 136 lf.currentTs = ts 137 138 if err := lf.Template.Execute(lf.buf, lbs.Map()); err != nil { 139 lbs.SetErr(errTemplateFormat) 140 lbs.SetErrorDetails(err.Error()) 141 return line, true 142 } 143 return lf.buf.Bytes(), true 144 } 145 146 func (lf *LineFormatter) RequiredLabelNames() []string { 147 return uniqueString(listNodeFields([]parse.Node{lf.Root})) 148 } 149 150 func listNodeFields(nodes []parse.Node) []string { 151 var res []string 152 for _, node := range nodes { 153 switch node.Type() { 154 case parse.NodePipe: 155 res = append(res, listNodeFieldsFromPipe(node.(*parse.PipeNode))...) 156 case parse.NodeAction: 157 res = append(res, listNodeFieldsFromPipe(node.(*parse.ActionNode).Pipe)...) 158 case parse.NodeList: 159 res = append(res, listNodeFields(node.(*parse.ListNode).Nodes)...) 160 case parse.NodeCommand: 161 res = append(res, listNodeFields(node.(*parse.CommandNode).Args)...) 162 case parse.NodeIf, parse.NodeWith, parse.NodeRange: 163 res = append(res, listNodeFieldsFromBranch(node)...) 164 case parse.NodeField: 165 res = append(res, node.(*parse.FieldNode).Ident...) 166 } 167 } 168 return res 169 } 170 171 func listNodeFieldsFromBranch(node parse.Node) []string { 172 var res []string 173 var b parse.BranchNode 174 switch node.Type() { 175 case parse.NodeIf: 176 b = node.(*parse.IfNode).BranchNode 177 case parse.NodeWith: 178 b = node.(*parse.WithNode).BranchNode 179 case parse.NodeRange: 180 b = node.(*parse.RangeNode).BranchNode 181 default: 182 return res 183 } 184 if b.Pipe != nil { 185 res = append(res, listNodeFieldsFromPipe(b.Pipe)...) 186 } 187 if b.List != nil { 188 res = append(res, listNodeFields(b.List.Nodes)...) 189 } 190 if b.ElseList != nil { 191 res = append(res, listNodeFields(b.ElseList.Nodes)...) 192 } 193 return res 194 } 195 196 func listNodeFieldsFromPipe(p *parse.PipeNode) []string { 197 var res []string 198 for _, c := range p.Cmds { 199 res = append(res, listNodeFields(c.Args)...) 200 } 201 return res 202 } 203 204 // LabelFmt is a configuration struct for formatting a label. 205 type LabelFmt struct { 206 Name string 207 Value string 208 209 Rename bool 210 } 211 212 // NewRenameLabelFmt creates a configuration to rename a label. 213 func NewRenameLabelFmt(dst, target string) LabelFmt { 214 return LabelFmt{ 215 Name: dst, 216 Rename: true, 217 Value: target, 218 } 219 } 220 221 // NewTemplateLabelFmt creates a configuration to format a label using text template. 222 func NewTemplateLabelFmt(dst, template string) LabelFmt { 223 return LabelFmt{ 224 Name: dst, 225 Rename: false, 226 Value: template, 227 } 228 } 229 230 type labelFormatter struct { 231 tmpl *template.Template 232 LabelFmt 233 } 234 235 type LabelsFormatter struct { 236 formats []labelFormatter 237 buf *bytes.Buffer 238 } 239 240 // NewLabelsFormatter creates a new formatter that can format multiple labels at once. 241 // Either by renaming or using text template. 242 // It is not allowed to reformat the same label twice within the same formatter. 243 func NewLabelsFormatter(fmts []LabelFmt) (*LabelsFormatter, error) { 244 if err := validate(fmts); err != nil { 245 return nil, err 246 } 247 formats := make([]labelFormatter, 0, len(fmts)) 248 249 for _, fm := range fmts { 250 toAdd := labelFormatter{LabelFmt: fm} 251 if !fm.Rename { 252 t, err := template.New("label").Option("missingkey=zero").Funcs(functionMap).Parse(fm.Value) 253 if err != nil { 254 return nil, fmt.Errorf("invalid template for label '%s': %s", fm.Name, err) 255 } 256 toAdd.tmpl = t 257 } 258 formats = append(formats, toAdd) 259 } 260 return &LabelsFormatter{ 261 formats: formats, 262 buf: bytes.NewBuffer(make([]byte, 1024)), 263 }, nil 264 } 265 266 func validate(fmts []LabelFmt) error { 267 // it would be too confusing to rename and change the same label value. 268 // To avoid confusion we allow to have a label name only once per stage. 269 uniqueLabelName := map[string]struct{}{} 270 for _, f := range fmts { 271 if f.Name == logqlmodel.ErrorLabel { 272 return fmt.Errorf("%s cannot be formatted", f.Name) 273 } 274 if _, ok := uniqueLabelName[f.Name]; ok { 275 return fmt.Errorf("multiple label name '%s' not allowed in a single format operation", f.Name) 276 } 277 uniqueLabelName[f.Name] = struct{}{} 278 } 279 return nil 280 } 281 282 func (lf *LabelsFormatter) Process(_ int64, l []byte, lbs *LabelsBuilder) ([]byte, bool) { 283 var data interface{} 284 for _, f := range lf.formats { 285 if f.Rename { 286 v, ok := lbs.Get(f.Value) 287 if ok { 288 lbs.Set(f.Name, v) 289 lbs.Del(f.Value) 290 } 291 continue 292 } 293 lf.buf.Reset() 294 if data == nil { 295 data = lbs.Map() 296 } 297 if err := f.tmpl.Execute(lf.buf, data); err != nil { 298 lbs.SetErr(errTemplateFormat) 299 lbs.SetErrorDetails(err.Error()) 300 continue 301 } 302 lbs.Set(f.Name, lf.buf.String()) 303 } 304 return l, true 305 } 306 307 func (lf *LabelsFormatter) RequiredLabelNames() []string { 308 var names []string 309 for _, fm := range lf.formats { 310 if fm.Rename { 311 names = append(names, fm.Value) 312 continue 313 } 314 names = append(names, listNodeFields([]parse.Node{fm.tmpl.Root})...) 315 } 316 return uniqueString(names) 317 } 318 319 func trunc(c int, s string) string { 320 runes := []rune(s) 321 l := len(runes) 322 if c < 0 && l+c > 0 { 323 return string(runes[l+c:]) 324 } 325 if c >= 0 && l > c { 326 return string(runes[:c]) 327 } 328 return s 329 } 330 331 // substring creates a substring of the given string. 332 // 333 // If start is < 0, this calls string[:end]. 334 // 335 // If start is >= 0 and end < 0 or end bigger than s length, this calls string[start:] 336 // 337 // Otherwise, this calls string[start, end]. 338 func substring(start, end int, s string) string { 339 runes := []rune(s) 340 l := len(runes) 341 if end > l { 342 end = l 343 } 344 if start > l { 345 start = l 346 } 347 if start < 0 { 348 if end < 0 { 349 return "" 350 } 351 return string(runes[:end]) 352 } 353 if end < 0 { 354 return string(runes[start:]) 355 } 356 if start > end { 357 return "" 358 } 359 return string(runes[start:end]) 360 }