<commit_before>package ltsvlog

import (
	"bytes"
	"runtime"
)

type ErrKV struct {
	error
	lvs []LV
}

func Err(err error) *ErrKV {
	return &ErrKV{
		error: err,
		lvs: []LV{
			{"err", err},
			{"stack", fullstack(2)},
		},
	}
}

func (e *ErrKV) KV(key string, value interface{}) *ErrKV {
	e.lvs = append(e.lvs, LV{key, value})
	return e
}

func (e *ErrKV) Error() string {
	return e.error.Error()
}

func (e *ErrKV) GetError() error {
	return e.error
}

func (e *ErrKV) ToLVs() []LV {
	return e.lvs
}

// fullstack formats a stack trace of the calling goroutine into buf
// in one line format which suitable for LTSV logs.
func fullstack(skip int) string {
	buf := make([]byte, 8192)
	var n int
	for {
		n = runtime.Stack(buf, false)
		if n < len(buf) {
			break
		}
		buf = make([]byte, len(buf)*2)
	}
	buf = buf[:n]

	// NOTE: We reuse the same buffer here.
	p := buf[:0]

	for j := 0; j < 1+2*skip; j++ {
		i := bytes.IndexByte(buf, '\n')
		buf = buf[i+1:]
	}

	for len(buf) > 0 {
		p = append(p, '[')
		i := bytes.IndexByte(buf, '\n')
		p = append(p, buf[:i]...)
		p = append(p, ' ')
		buf = buf[i+2:]
		i = bytes.IndexByte(buf, '\n')
		p = append(p, buf[:i]...)
		p = append(p, ']')
		buf = buf[i+1:]
		if len(buf) > 0 {
			p = append(p, ',')
		}
	}
	return string(p)
}<commit_msg>Add Stack and Time to ErrLV<commit_after>package ltsvlog

import (
	"bytes"
	"runtime"
	"time"
)

type ErrLV interface {
	LV(key string, value interface{}) ErrLV
	Stack() ErrLV
	Time() ErrLV

	Error() string
	GetError() error
	ToLVs() []LV
}

type errLV struct {
	error
	lvs []LV
}

func Err(err error) ErrLV {
	e := &errLV{
		error: err,
		lvs:   make([]LV, 0, 8),
	}
	e.lvs = append(e.lvs, LV{"err", err})
	return e
}

func (e *errLV) Stack() ErrLV {
	e.lvs = append(e.lvs, LV{"stack", fullstack(2)})
	return e
}

func (e *errLV) Time() ErrLV {
	e.lvs = append(e.lvs, LV{"errtime", formatTime(time.Now())})
	return e
}

func (e *errLV) LV(key string, value interface{}) ErrLV {
	e.lvs = append(e.lvs, LV{key, value})
	return e
}

func (e *errLV) Error() string {
	return e.error.Error()
}

func (e *errLV) GetError() error {
	return e.error
}

func (e *errLV) ToLVs() []LV {
	return e.lvs
}

// fullstack formats a stack trace of the calling goroutine into buf
// in one line format which suitable for LTSV logs.
func fullstack(skip int) string {
	buf := make([]byte, 8192)
	var n int
	for {
		n = runtime.Stack(buf, false)
		if n < len(buf) {
			break
		}
		buf = make([]byte, len(buf)*2)
	}
	buf = buf[:n]

	// NOTE: We reuse the same buffer here.
	p := buf[:0]

	for j := 0; j < 1+2*skip; j++ {
		i := bytes.IndexByte(buf, '\n')
		buf = buf[i+1:]
	}

	for len(buf) > 0 {
		p = append(p, '[')
		i := bytes.IndexByte(buf, '\n')
		p = append(p, buf[:i]...)
		p = append(p, ' ')
		buf = buf[i+2:]
		i = bytes.IndexByte(buf, '\n')
		p = append(p, buf[:i]...)
		p = append(p, ']')
		buf = buf[i+1:]
		if len(buf) > 0 {
			p = append(p, ',')
		}
	}
	return string(p)
}

func formatTime(t time.Time) string {
	buf := []byte("0000-00-00T00:00:00.000000Z")
	year, month, day := t.Date()
	hour, min, sec := t.Clock()
	itoa(buf[:4], year, 4)
	itoa(buf[5:7], int(month), 2)
	itoa(buf[8:10], day, 2)
	itoa(buf[11:13], hour, 2)
	itoa(buf[14:16], min, 2)
	itoa(buf[17:19], sec, 2)
	itoa(buf[20:26], t.Nanosecond()/1e3, 6)
	return string(buf)
}
{'fraction_non_alphanumeric': 0.19813309244203553, 'fraction_numerical': 0.02499247214694369, 'mean_word_length': 3.1944444444444446, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7668110', 'n_tokens_mistral': 1607, 'n_tokens_neox': 1455, 'n_words': 397}