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  }