func (l *intLogger) log(t time.Time, level Level, msg string, args ...interface{}) {
	l.writer.WriteString(t.Format(l.timeFormat))
	l.writer.WriteByte(' ')
	s, ok := _levelToBracket[level]
	if ok {
		l.writer.WriteString(s)
	} else {
		l.writer.WriteString("[?????]")
	}
	if l.caller {
		if _, file, line, ok := runtime.Caller(3); ok {
			l.writer.WriteByte(' ')
			l.writer.WriteString(trimCallerPath(file))
			l.writer.WriteByte(':')
			l.writer.WriteString(strconv.Itoa(line))
			l.writer.WriteByte(':')
		}
	}
	l.writer.WriteByte(' ')
	if l.name != "" {
		l.writer.WriteString(l.name)
		l.writer.WriteString(": ")
	}
	l.writer.WriteString(msg)
	args = append(l.implied, args...)
	var stacktrace CapturedStacktrace
	if args != nil && len(args) > 0 {
		if len(args)%2 != 0 {
			cs, ok := args[len(args)-1].(CapturedStacktrace)
			if ok {
				args = args[:len(args)-1]
				stacktrace = cs
			} else {
				args = append(args, "<unknown>")
			}
		}
		l.writer.WriteByte(':')
	FOR:
		for i := 0; i < len(args); i = i + 2 {
			var (
				val string
				raw bool
			)
			switch st := args[i+1].(type) {
			case string:
				val = st
			case int:
				val = strconv.FormatInt(int64(st), 10)
			case int64:
				val = strconv.FormatInt(int64(st), 10)
			case int32:
				val = strconv.FormatInt(int64(st), 10)
			case int16:
				val = strconv.FormatInt(int64(st), 10)
			case int8:
				val = strconv.FormatInt(int64(st), 10)
			case uint:
				val = strconv.FormatUint(uint64(st), 10)
			case uint64:
				val = strconv.FormatUint(uint64(st), 10)
			case uint32:
				val = strconv.FormatUint(uint64(st), 10)
			case uint16:
				val = strconv.FormatUint(uint64(st), 10)
			case uint8:
				val = strconv.FormatUint(uint64(st), 10)
			case CapturedStacktrace:
				stacktrace = st
				continue FOR
			case Format:
				val = fmt.Sprintf(st[0].(string), st[1:]...)
			default:
				v := reflect.ValueOf(st)
				if v.Kind() == reflect.Slice {
					val = l.renderSlice(v)
					raw = true
				} else {
					val = fmt.Sprintf("%v", st)
				}
			}
			l.writer.WriteByte(' ')
			l.writer.WriteString(args[i].(string))
			l.writer.WriteByte('=')
			if !raw && strings.ContainsAny(val, " \t\n\r") {
				l.writer.WriteByte('"')
				l.writer.WriteString(val)
				l.writer.WriteByte('"')
			} else {
				l.writer.WriteString(val)
			}
		}
	}
	l.writer.WriteString("\n")
	if stacktrace != "" {
		l.writer.WriteString(string(stacktrace))
	}
}
Non-JSON logging format function
package hclog
import (
	"bytes"
	"encoding"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"reflect"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)
const TimeFormat = "2006-01-02T15:04:05.000Z0700"
const errJsonUnsupportedTypeMsg = "logging contained values that don't serialize to json"
var (
	_levelToBracket = map[Level]string{
		Debug: "[DEBUG]",
		Trace: "[TRACE]",
		Info:  "[INFO] ",
		Warn:  "[WARN] ",
		Error: "[ERROR]",
	}
)
var _ Logger = &intLogger{}
type intLogger struct {
	json       bool
	caller     bool
	name       string
	timeFormat string
	mutex  *sync.Mutex
	writer *writer
	level  *int32
	implied []interface{}
}
func New(opts *LoggerOptions) Logger {
	if opts == nil {
		opts = &LoggerOptions{}
	}
	output := opts.Output
	if output == nil {
		output = DefaultOutput
	}
	level := opts.Level
	if level == NoLevel {
		level = DefaultLevel
	}
	mutex := opts.Mutex
	if mutex == nil {
		mutex = new(sync.Mutex)
	}
	l := &intLogger{
		json:       opts.JSONFormat,
		caller:     opts.IncludeLocation,
		name:       opts.Name,
		timeFormat: TimeFormat,
		mutex:      mutex,
		writer:     newWriter(output),
		level:      new(int32),
	}
	if opts.TimeFormat != "" {
		l.timeFormat = opts.TimeFormat
	}
	atomic.StoreInt32(l.level, int32(level))
	return l
}
func (l *intLogger) Log(level Level, msg string, args ...interface{}) {
	if level < Level(atomic.LoadInt32(l.level)) {
		return
	}
	t := time.Now()
	l.mutex.Lock()
	defer l.mutex.Unlock()
	if l.json {
		l.logJSON(t, level, msg, args...)
	} else {
		l.log(t, level, msg, args...)
	}
	l.writer.Flush(level)
}
func trimCallerPath(path string) string {
	idx := strings.LastIndexByte(path, '/')
	if idx == -1 {
		return path
	}
	idx = strings.LastIndexByte(path[:idx], '/')
	if idx == -1 {
		return path
	}
	return path[idx+1:]
}