...

Source file src/cdr.dev/slog/map.go

Documentation: cdr.dev/slog

     1  package slog
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"reflect"
     8  	"strings"
     9  
    10  	"golang.org/x/xerrors"
    11  )
    12  
    13  // Map represents an ordered map of fields.
    14  type Map []Field
    15  
    16  var _ json.Marshaler = Map(nil)
    17  
    18  // MarshalJSON implements json.Marshaler.
    19  //
    20  // It is guaranteed to return a nil error.
    21  // Any error marshalling a field will become the field's value.
    22  //
    23  // Every field value is encoded with the following process:
    24  //
    25  // 1. json.Marshaller is handled.
    26  //
    27  // 2. xerrors.Formatter is handled.
    28  //
    29  // 3. structs that have a field with a json tag are encoded with json.Marshal.
    30  //
    31  // 4. error and fmt.Stringer is handled.
    32  //
    33  // 5. slices and arrays go through the encode function for every element.
    34  //
    35  // 6. For values that cannot be encoded with json.Marshal, fmt.Sprintf("%+v") is used.
    36  //
    37  // 7. json.Marshal(v) is used for all other values.
    38  func (m Map) MarshalJSON() ([]byte, error) {
    39  	b := &bytes.Buffer{}
    40  	b.WriteByte('{')
    41  	for i, f := range m {
    42  		b.WriteByte('\n')
    43  		b.Write(encode(f.Name))
    44  		b.WriteByte(':')
    45  		b.Write(encode(f.Value))
    46  
    47  		if i < len(m)-1 {
    48  			b.WriteByte(',')
    49  		}
    50  	}
    51  	b.WriteByte('}')
    52  
    53  	return b.Bytes(), nil
    54  }
    55  
    56  func marshalList(rv reflect.Value) []byte {
    57  	b := &bytes.Buffer{}
    58  	b.WriteByte('[')
    59  	for i := 0; i < rv.Len(); i++ {
    60  		b.WriteByte('\n')
    61  		b.Write(encode(rv.Index(i).Interface()))
    62  
    63  		if i < rv.Len()-1 {
    64  			b.WriteByte(',')
    65  		}
    66  	}
    67  	b.WriteByte(']')
    68  
    69  	return b.Bytes()
    70  }
    71  
    72  func encode(v interface{}) []byte {
    73  	switch v := v.(type) {
    74  	case json.Marshaler:
    75  		return encodeJSON(v)
    76  	case xerrors.Formatter:
    77  		return encode(errorChain(v))
    78  	}
    79  
    80  	rv := reflect.Indirect(reflect.ValueOf(v))
    81  	if !rv.IsValid() {
    82  		return encodeJSON(v)
    83  	}
    84  
    85  	if rv.Kind() == reflect.Struct {
    86  		b, ok := encodeStruct(rv)
    87  		if ok {
    88  			return b
    89  		}
    90  	}
    91  
    92  	switch v.(type) {
    93  	case error, fmt.Stringer:
    94  		return encode(fmt.Sprint(v))
    95  	}
    96  
    97  	switch rv.Type().Kind() {
    98  	case reflect.Slice:
    99  		if !rv.IsNil() {
   100  			return marshalList(rv)
   101  		}
   102  	case reflect.Array:
   103  		return marshalList(rv)
   104  	case reflect.Struct, reflect.Chan, reflect.Complex64, reflect.Complex128, reflect.Func:
   105  		// These types cannot be directly encoded with json.Marshal.
   106  		// See https://golang.org/pkg/encoding/json/#Marshal
   107  		return encodeJSON(fmt.Sprintf("%+v", v))
   108  	}
   109  
   110  	return encodeJSON(v)
   111  }
   112  
   113  func encodeStruct(rv reflect.Value) ([]byte, bool) {
   114  	if rv.Kind() == reflect.Struct {
   115  		for i := 0; i < rv.NumField(); i++ {
   116  			ft := rv.Type().Field(i)
   117  			// Found a field with a json tag.
   118  			if ft.Tag.Get("json") != "" {
   119  				return encodeJSON(rv.Interface()), true
   120  			}
   121  		}
   122  	}
   123  
   124  	return nil, false
   125  }
   126  
   127  func encodeJSON(v interface{}) []byte {
   128  	b, err := json.Marshal(v)
   129  	if err != nil {
   130  		return encode(M(
   131  			Error(xerrors.Errorf("failed to marshal to JSON: %w", err)),
   132  			F("type", reflect.TypeOf(v)),
   133  			F("value", fmt.Sprintf("%+v", v)),
   134  		))
   135  	}
   136  	return b
   137  }
   138  
   139  func errorChain(f xerrors.Formatter) []interface{} {
   140  	var errs []interface{}
   141  
   142  	next := error(f)
   143  	for {
   144  		f, ok := next.(xerrors.Formatter)
   145  		if !ok {
   146  			errs = append(errs, next)
   147  			return errs
   148  		}
   149  
   150  		p := &xerrorPrinter{}
   151  		next = f.FormatError(p)
   152  		errs = append(errs, p.e)
   153  	}
   154  }
   155  
   156  type wrapError struct {
   157  	Msg string `json:"msg"`
   158  	Fun string `json:"fun"`
   159  	// file:line
   160  	Loc string `json:"loc"`
   161  }
   162  
   163  type xerrorPrinter struct {
   164  	e wrapError
   165  }
   166  
   167  func (p *xerrorPrinter) Print(v ...interface{}) {
   168  	s := fmt.Sprint(v...)
   169  	p.write(s)
   170  }
   171  
   172  func (p *xerrorPrinter) Printf(f string, v ...interface{}) {
   173  	s := fmt.Sprintf(f, v...)
   174  	p.write(s)
   175  }
   176  
   177  func (p *xerrorPrinter) Detail() bool {
   178  	return true
   179  }
   180  
   181  func (p *xerrorPrinter) write(s string) {
   182  	s = strings.TrimSpace(s)
   183  	switch {
   184  	case p.e.Msg == "":
   185  		p.e.Msg = s
   186  	case p.e.Fun == "":
   187  		p.e.Fun = s
   188  	case p.e.Loc == "":
   189  		p.e.Loc = s
   190  	}
   191  }
   192  
   193  func (m Map) append(m2 Map) Map {
   194  	m3 := make(Map, 0, len(m)+len(m2))
   195  	m3 = append(m3, m...)
   196  	m3 = append(m3, m2...)
   197  	return m3
   198  }
   199  

View as plain text