Source file 
src/log/slog/json_handler.go
     1  
     2  
     3  
     4  
     5  package slog
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"encoding/json"
    11  	"errors"
    12  	"fmt"
    13  	"io"
    14  	"log/slog/internal/buffer"
    15  	"strconv"
    16  	"sync"
    17  	"time"
    18  	"unicode/utf8"
    19  )
    20  
    21  
    22  
    23  type JSONHandler struct {
    24  	*commonHandler
    25  }
    26  
    27  
    28  
    29  
    30  func NewJSONHandler(w io.Writer, opts *HandlerOptions) *JSONHandler {
    31  	if opts == nil {
    32  		opts = &HandlerOptions{}
    33  	}
    34  	return &JSONHandler{
    35  		&commonHandler{
    36  			json: true,
    37  			w:    w,
    38  			opts: *opts,
    39  			mu:   &sync.Mutex{},
    40  		},
    41  	}
    42  }
    43  
    44  
    45  
    46  func (h *JSONHandler) Enabled(_ context.Context, level Level) bool {
    47  	return h.commonHandler.enabled(level)
    48  }
    49  
    50  
    51  
    52  func (h *JSONHandler) WithAttrs(attrs []Attr) Handler {
    53  	return &JSONHandler{commonHandler: h.commonHandler.withAttrs(attrs)}
    54  }
    55  
    56  func (h *JSONHandler) WithGroup(name string) Handler {
    57  	return &JSONHandler{commonHandler: h.commonHandler.withGroup(name)}
    58  }
    59  
    60  
    61  
    62  
    63  
    64  
    65  
    66  
    67  
    68  
    69  
    70  
    71  
    72  
    73  
    74  
    75  
    76  
    77  
    78  
    79  
    80  
    81  
    82  
    83  
    84  
    85  
    86  
    87  
    88  func (h *JSONHandler) Handle(_ context.Context, r Record) error {
    89  	return h.commonHandler.handle(r)
    90  }
    91  
    92  
    93  func appendJSONTime(s *handleState, t time.Time) {
    94  	if y := t.Year(); y < 0 || y >= 10000 {
    95  		
    96  		
    97  		s.appendError(errors.New("time.Time year outside of range [0,9999]"))
    98  	}
    99  	s.buf.WriteByte('"')
   100  	*s.buf = t.AppendFormat(*s.buf, time.RFC3339Nano)
   101  	s.buf.WriteByte('"')
   102  }
   103  
   104  func appendJSONValue(s *handleState, v Value) error {
   105  	switch v.Kind() {
   106  	case KindString:
   107  		s.appendString(v.str())
   108  	case KindInt64:
   109  		*s.buf = strconv.AppendInt(*s.buf, v.Int64(), 10)
   110  	case KindUint64:
   111  		*s.buf = strconv.AppendUint(*s.buf, v.Uint64(), 10)
   112  	case KindFloat64:
   113  		
   114  		
   115  		
   116  		if err := appendJSONMarshal(s.buf, v.Float64()); err != nil {
   117  			return err
   118  		}
   119  	case KindBool:
   120  		*s.buf = strconv.AppendBool(*s.buf, v.Bool())
   121  	case KindDuration:
   122  		
   123  		*s.buf = strconv.AppendInt(*s.buf, int64(v.Duration()), 10)
   124  	case KindTime:
   125  		s.appendTime(v.Time())
   126  	case KindAny:
   127  		a := v.Any()
   128  		_, jm := a.(json.Marshaler)
   129  		if err, ok := a.(error); ok && !jm {
   130  			s.appendString(err.Error())
   131  		} else {
   132  			return appendJSONMarshal(s.buf, a)
   133  		}
   134  	default:
   135  		panic(fmt.Sprintf("bad kind: %s", v.Kind()))
   136  	}
   137  	return nil
   138  }
   139  
   140  func appendJSONMarshal(buf *buffer.Buffer, v any) error {
   141  	
   142  	var bb bytes.Buffer
   143  	enc := json.NewEncoder(&bb)
   144  	enc.SetEscapeHTML(false)
   145  	if err := enc.Encode(v); err != nil {
   146  		return err
   147  	}
   148  	bs := bb.Bytes()
   149  	buf.Write(bs[:len(bs)-1]) 
   150  	return nil
   151  }
   152  
   153  
   154  
   155  
   156  
   157  
   158  func appendEscapedJSONString(buf []byte, s string) []byte {
   159  	char := func(b byte) { buf = append(buf, b) }
   160  	str := func(s string) { buf = append(buf, s...) }
   161  
   162  	start := 0
   163  	for i := 0; i < len(s); {
   164  		if b := s[i]; b < utf8.RuneSelf {
   165  			if safeSet[b] {
   166  				i++
   167  				continue
   168  			}
   169  			if start < i {
   170  				str(s[start:i])
   171  			}
   172  			char('\\')
   173  			switch b {
   174  			case '\\', '"':
   175  				char(b)
   176  			case '\n':
   177  				char('n')
   178  			case '\r':
   179  				char('r')
   180  			case '\t':
   181  				char('t')
   182  			default:
   183  				
   184  				str(`u00`)
   185  				char(hex[b>>4])
   186  				char(hex[b&0xF])
   187  			}
   188  			i++
   189  			start = i
   190  			continue
   191  		}
   192  		c, size := utf8.DecodeRuneInString(s[i:])
   193  		if c == utf8.RuneError && size == 1 {
   194  			if start < i {
   195  				str(s[start:i])
   196  			}
   197  			str(`\ufffd`)
   198  			i += size
   199  			start = i
   200  			continue
   201  		}
   202  		
   203  		
   204  		
   205  		
   206  		
   207  		
   208  		
   209  		if c == '\u2028' || c == '\u2029' {
   210  			if start < i {
   211  				str(s[start:i])
   212  			}
   213  			str(`\u202`)
   214  			char(hex[c&0xF])
   215  			i += size
   216  			start = i
   217  			continue
   218  		}
   219  		i += size
   220  	}
   221  	if start < len(s) {
   222  		str(s[start:])
   223  	}
   224  	return buf
   225  }
   226  
   227  const hex = "0123456789abcdef"
   228  
   229  
   230  
   231  
   232  
   233  
   234  
   235  
   236  
   237  var safeSet = [utf8.RuneSelf]bool{
   238  	' ':      true,
   239  	'!':      true,
   240  	'"':      false,
   241  	'#':      true,
   242  	'$':      true,
   243  	'%':      true,
   244  	'&':      true,
   245  	'\'':     true,
   246  	'(':      true,
   247  	')':      true,
   248  	'*':      true,
   249  	'+':      true,
   250  	',':      true,
   251  	'-':      true,
   252  	'.':      true,
   253  	'/':      true,
   254  	'0':      true,
   255  	'1':      true,
   256  	'2':      true,
   257  	'3':      true,
   258  	'4':      true,
   259  	'5':      true,
   260  	'6':      true,
   261  	'7':      true,
   262  	'8':      true,
   263  	'9':      true,
   264  	':':      true,
   265  	';':      true,
   266  	'<':      true,
   267  	'=':      true,
   268  	'>':      true,
   269  	'?':      true,
   270  	'@':      true,
   271  	'A':      true,
   272  	'B':      true,
   273  	'C':      true,
   274  	'D':      true,
   275  	'E':      true,
   276  	'F':      true,
   277  	'G':      true,
   278  	'H':      true,
   279  	'I':      true,
   280  	'J':      true,
   281  	'K':      true,
   282  	'L':      true,
   283  	'M':      true,
   284  	'N':      true,
   285  	'O':      true,
   286  	'P':      true,
   287  	'Q':      true,
   288  	'R':      true,
   289  	'S':      true,
   290  	'T':      true,
   291  	'U':      true,
   292  	'V':      true,
   293  	'W':      true,
   294  	'X':      true,
   295  	'Y':      true,
   296  	'Z':      true,
   297  	'[':      true,
   298  	'\\':     false,
   299  	']':      true,
   300  	'^':      true,
   301  	'_':      true,
   302  	'`':      true,
   303  	'a':      true,
   304  	'b':      true,
   305  	'c':      true,
   306  	'd':      true,
   307  	'e':      true,
   308  	'f':      true,
   309  	'g':      true,
   310  	'h':      true,
   311  	'i':      true,
   312  	'j':      true,
   313  	'k':      true,
   314  	'l':      true,
   315  	'm':      true,
   316  	'n':      true,
   317  	'o':      true,
   318  	'p':      true,
   319  	'q':      true,
   320  	'r':      true,
   321  	's':      true,
   322  	't':      true,
   323  	'u':      true,
   324  	'v':      true,
   325  	'w':      true,
   326  	'x':      true,
   327  	'y':      true,
   328  	'z':      true,
   329  	'{':      true,
   330  	'|':      true,
   331  	'}':      true,
   332  	'~':      true,
   333  	'\u007f': true,
   334  }
   335  
View as plain text