github.com/adacta-ru/mattermost-server/v6@v6.0.0/mlog/human/parser.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package human
     5  
     6  import (
     7  	"encoding/json"
     8  	"errors"
     9  	"fmt"
    10  	"io"
    11  	"strconv"
    12  	"strings"
    13  	"time"
    14  
    15  	"github.com/adacta-ru/mattermost-server/v6/mlog"
    16  )
    17  
    18  func ParseLogMessage(msg string) LogEntry {
    19  	result, err := parseLogMessage(msg)
    20  	if err != nil {
    21  		// If failed to parse, just output a LogEntry where all fields are blank, but Message is the original string
    22  		var result2 LogEntry
    23  		result2.Message = msg
    24  		return result2
    25  	}
    26  	return result
    27  }
    28  
    29  func parseLogMessage(msg string) (result LogEntry, err error) {
    30  
    31  	// Note: This implementation uses a custom json decoding loop.
    32  	// The primary advantage of this versus decoding directly into a map is to
    33  	// preserve the order of the fields. This can be simplified if we end up
    34  	// having the formatter sort fields alphabetically (logrus does by default)
    35  
    36  	dec := json.NewDecoder(strings.NewReader(msg))
    37  
    38  	// look for an initial "{"
    39  	token, err := dec.Token()
    40  	if err != nil {
    41  		return result, err
    42  	}
    43  	d, ok := token.(json.Delim)
    44  	if !ok || d != '{' {
    45  		return result, fmt.Errorf("input is not a JSON object, found: %v", token)
    46  	}
    47  
    48  	// read all key-value pairs
    49  	for dec.More() {
    50  		key, err2 := dec.Token()
    51  		if err2 != nil {
    52  			return result, err2
    53  		}
    54  		skey, ok2 := key.(string)
    55  		if !ok2 {
    56  			return result, errors.New("key is not a value string")
    57  		}
    58  		if !dec.More() {
    59  			return result, errors.New("missing value pair")
    60  		}
    61  
    62  		switch skey {
    63  		case "ts":
    64  			var ts json.Number
    65  			if err2 := dec.Decode(&ts); err2 != nil {
    66  				return result, err2
    67  			}
    68  			timeVal, err2 := numberToTime(ts)
    69  			if err2 != nil {
    70  				return result, err2
    71  			}
    72  			result.Time = timeVal
    73  
    74  		case "level":
    75  			s, err2 := decodeAsString(dec)
    76  			if err2 != nil {
    77  				return result, err2
    78  			}
    79  			result.Level = s
    80  
    81  		case "msg":
    82  			s, err2 := decodeAsString(dec)
    83  			if err2 != nil {
    84  				return result, err2
    85  			}
    86  			result.Message = s
    87  
    88  		case "caller":
    89  			s, err2 := decodeAsString(dec)
    90  			if err2 != nil {
    91  				return result, err2
    92  			}
    93  			result.Caller = s
    94  
    95  		default:
    96  			var p interface{}
    97  			if err2 := dec.Decode(&p); err2 != nil {
    98  				return result, err2
    99  			}
   100  			var f mlog.Field
   101  			f.Key = skey
   102  			f.Interface = p
   103  			result.Fields = append(result.Fields, f)
   104  		}
   105  	}
   106  
   107  	// read the "}"
   108  	token, err = dec.Token()
   109  	if err != nil {
   110  		return result, err
   111  	}
   112  	d, ok = token.(json.Delim)
   113  	if !ok || d != '}' {
   114  		return result, fmt.Errorf("failed to read '}', read: %v", token)
   115  	}
   116  
   117  	// make sure nothing else trailing
   118  	if token, err := dec.Token(); err != io.EOF {
   119  		return result, err
   120  	} else if token != nil {
   121  		return result, errors.New("found trailing data")
   122  	}
   123  
   124  	return result, nil
   125  }
   126  
   127  // Translate a number into a time
   128  func numberToTime(v json.Number) (time.Time, error) {
   129  	// Using floating point math to extract the nanoseconds leads to a time that doesn't exactly match the input
   130  	// Instead, parse out the components from the string representation
   131  
   132  	var t time.Time
   133  
   134  	// First make sure it is a number...
   135  	flt, err := v.Float64()
   136  	if err != nil {
   137  		return t, err
   138  	}
   139  
   140  	s := v.String()
   141  
   142  	if strings.ContainsAny(s, "eE") {
   143  		// input is in scientific notation. Convert to standard decimal notation
   144  		s = strconv.FormatFloat(flt, 'f', -1, 64)
   145  	}
   146  
   147  	// extract the seconds and nanoseconds separately
   148  	var nanos, sec int64
   149  
   150  	parts := strings.SplitN(s, ".", 2)
   151  	sec, err = strconv.ParseInt(parts[0], 10, 64)
   152  	if err != nil {
   153  		return t, err
   154  	}
   155  
   156  	if len(parts) == 2 {
   157  		nanosText := parts[1] + "000000000"
   158  		nanosText = nanosText[:9]
   159  		nanos, err = strconv.ParseInt(nanosText, 10, 64)
   160  		if err != nil {
   161  			return t, err
   162  		}
   163  	}
   164  
   165  	t = time.Unix(sec, nanos)
   166  	return t, nil
   167  }
   168  
   169  // Decodes a value from JSON, coercing it to a string value as necessary
   170  func decodeAsString(dec *json.Decoder) (s string, err error) {
   171  	var v interface{}
   172  	if err = dec.Decode(&v); err != nil {
   173  		return s, err
   174  	}
   175  	var ok bool
   176  	if s, ok = v.(string); ok {
   177  		return s, err
   178  	}
   179  	s = fmt.Sprint(v)
   180  	return s, err
   181  }