github.com/symfony-cli/symfony-cli@v0.0.0-20240514161054-ece2df437dfa/humanlog/symfony.go (about) 1 /* 2 * Copyright (c) 2021-present Fabien Potencier <fabien@symfony.com> 3 * 4 * This file is part of Symfony CLI project 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU Affero General Public License as 8 * published by the Free Software Foundation, either version 3 of the 9 * License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU Affero General Public License for more details. 15 * 16 * You should have received a copy of the GNU Affero General Public License 17 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 package humanlog 21 22 import ( 23 "encoding/json" 24 "regexp" 25 "strconv" 26 "strings" 27 "time" 28 29 "github.com/pkg/errors" 30 ) 31 32 // [2018-11-19 12:52:00] console.DEBUG: www {"xxx":"yyy","code":1} [] 33 // or [2019-11-13T07:16:50.260544+01:00] console.DEBUG: www {"xxx":"yyy","code":1} [] 34 var symfonyLogLineRegexp = regexp.MustCompile("^\\[(\\d{4}\\-\\d{2}\\-\\d{2} \\d{2}\\:\\d{2}\\:\\d{2}|\\d{4}\\-\\d{2}\\-\\d{2}T\\d{2}\\:\\d{2}\\:\\d{2}\\.\\d+\\+\\d{2}\\:\\d{2})\\] ([^\\.]+)\\.([^\\:]+)\\: (.+) (\\[.*?\\]|{.*?}) (\\[.*?\\]|{.*?})\\s*$") 35 36 func convertSymfonyLog(in []byte) (*line, error) { 37 allMatches := symfonyLogLineRegexp.FindAllSubmatch(in, -1) 38 if allMatches == nil { 39 return nil, nil 40 } 41 line := &line{ 42 fields: make(map[string]string), 43 } 44 var err error 45 matches := allMatches[0] 46 for i, m := range matches { 47 if i == 1 { 48 // convert date (2018-11-19 13:32:00) 49 line.time, err = time.Parse(`2006-01-02 15:04:05`, string(m)) 50 if err != nil { 51 // convert date (2019-11-13T07:16:50.260544+01:00) 52 line.time, err = time.Parse(time.RFC3339Nano, string(m)) 53 if err != nil { 54 return nil, errors.WithStack(err) 55 } 56 } 57 } else if i == 2 { 58 line.source = string(m) 59 } else if i == 3 { 60 line.level = strings.ToLower(string(m)) 61 } else if i == 4 { 62 line.message = string(m) 63 // unfortunately, we cannot really parse log lines with a regexp 64 // one problem is for very nested hash maps like exceptions 65 // this hack works because we don't want exception traces in logs anyway 66 if idx := strings.Index(line.message, ` {"exception":`); idx != -1 { 67 line.message = line.message[:idx] 68 } 69 } else if i == 5 || i == 6 { 70 args := make(map[string]interface{}) 71 if m[0] == '[' { 72 var values []interface{} 73 if err := errors.WithStack(json.Unmarshal(m, &values)); err != nil { 74 continue 75 } 76 for i, v := range values { 77 args[strconv.Itoa(i)] = v 78 } 79 } else { 80 if err := errors.WithStack(json.Unmarshal(m, &args)); err != nil { 81 continue 82 } 83 } 84 for k, v := range args { 85 if k == "exception" { 86 continue 87 } 88 line.fields[k] = convertAnyVal(v) 89 } 90 } 91 } 92 return line, nil 93 }