package starlog import ( "context" "errors" "fmt" "io" "os" "path/filepath" "runtime" "sort" "strconv" "strings" "sync/atomic" "time" "unicode" "unicode/utf8" ) func generateCoreLogStr(skip int, logstr string) string { var line int = 0 var funcname, fileName string now := time.Now() pc, fName, codeln, ok := runtime.Caller(skip) if !ok { return "" } line = codeln funcname = runtime.FuncForPC(pc).Name() funcname = filepath.Ext(funcname) funcname = strings.TrimPrefix(funcname, ".") fileName = filepath.Base(fName) y, m, d := now.Date() h, i, s := now.Clock() micro := now.Nanosecond() / 1e3 logStr := fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d.%06d", y, m, d, h, i, s, micro) logStr += " " + fileName + ":" + strconv.Itoa(line) logStr += " <" + funcname + ">" logStr += " " + logstr return logStr } func (logger *starlog) levelName(level int) string { levelName, ok := levels[level] if !ok { return strconv.Itoa(level) } return levelName } func (logger *starlog) levelColor(level int) *Color { color, ok := logger.colorMe[level] if ok && color != nil { return color } return NewColor(FgWhite) } func (logger *starlog) levelAttrs(level int) []Attr { attrs, ok := logger.colorList[level] if !ok { return []Attr{FgWhite} } cloned := make([]Attr, len(attrs)) copy(cloned, attrs) return cloned } func cloneLevelAttrsMap(source map[int][]Attr) map[int][]Attr { if len(source) == 0 { return map[int][]Attr{} } cloned := make(map[int][]Attr, len(source)) for level, attrs := range source { attrCopy := make([]Attr, len(attrs)) copy(attrCopy, attrs) cloned[level] = attrCopy } return cloned } func cloneLevelColorMap(source map[int][]Attr) map[int]*Color { if len(source) == 0 { return map[int]*Color{} } cloned := make(map[int]*Color, len(source)) for level, attrs := range source { cloned[level] = NewColor(attrs...) } return cloned } func cloneStringSlice(source []string) []string { if len(source) == 0 { return nil } cloned := make([]string, len(source)) copy(cloned, source) return cloned } func cloneKeywordColorizers(source map[string]*Color) map[string]*Color { if len(source) == 0 { return nil } cloned := make(map[string]*Color, len(source)) for key, colorizer := range source { cloned[key] = colorizer } return cloned } type keywordPattern struct { keyword string keywordLower string byteLen int colorizer *Color } type keywordMatcher struct { byFirstRune map[rune][]keywordPattern byFirstRuneFold map[rune][]keywordPattern } func buildKeywordMatcher(order []string, colorizers map[string]*Color) *keywordMatcher { if len(order) == 0 { return nil } matcher := &keywordMatcher{ byFirstRune: make(map[rune][]keywordPattern), byFirstRuneFold: make(map[rune][]keywordPattern), } for _, keyword := range order { if keyword == "" { continue } colorizer := colorizers[keyword] if colorizer == nil { continue } firstRune, _ := utf8.DecodeRuneInString(keyword) if firstRune == utf8.RuneError { continue } pattern := keywordPattern{ keyword: keyword, keywordLower: strings.ToLower(keyword), byteLen: len(keyword), colorizer: colorizer, } matcher.byFirstRune[firstRune] = append(matcher.byFirstRune[firstRune], pattern) matcher.byFirstRuneFold[unicode.ToLower(firstRune)] = append(matcher.byFirstRuneFold[unicode.ToLower(firstRune)], pattern) } if len(matcher.byFirstRune) == 0 { return nil } return matcher } func (logger *starlog) rebuildKeywordCachesLocked() { if logger.keywordColors == nil { logger.keywordColors = make(map[string][]Attr) } keywords := make([]string, 0, len(logger.keywordColors)) var colorizers map[string]*Color for keyword, attrs := range logger.keywordColors { if keyword == "" { continue } keywords = append(keywords, keyword) if len(attrs) > 0 { if colorizers == nil { colorizers = make(map[string]*Color, len(logger.keywordColors)) } colorizers[keyword] = NewColor(attrs...) } } sort.Slice(keywords, func(i, j int) bool { return len(keywords[i]) > len(keywords[j]) }) if len(keywords) == 0 { keywords = nil } logger.keywordOrder = keywords logger.keywordColorizers = colorizers logger.keywordMatcher = buildKeywordMatcher(keywords, colorizers) } func (logger *starlog) snapshotForBuildLocked() *starlog { colorAttrs := cloneLevelAttrsMap(logger.colorList) return &starlog{ minLevel: logger.minLevel, errOutputLevel: logger.errOutputLevel, showFuncName: logger.showFuncName, showThread: logger.showThread, showLevel: logger.showLevel, showDeatilFile: logger.showDeatilFile, showColor: logger.showColor, onlyColorLevel: logger.onlyColorLevel, autoAppendNewline: logger.autoAppendNewline, stopWriter: logger.stopWriter, name: logger.name, colorList: colorAttrs, colorMe: cloneLevelColorMap(colorAttrs), keywordColors: nil, keywordOrder: nil, keywordColorizers: nil, keywordMatcher: logger.keywordMatcher, keywordMatchOptions: logger.keywordMatchOptions, showFieldColor: logger.showFieldColor, fieldKeyColor: cloneAttrs(logger.fieldKeyColor), fieldTypeColors: cloneColorMap(logger.fieldTypeColors), fieldValueColors: cloneColorMap(logger.fieldValueColors), entryHandler: logger.entryHandler, redactor: logger.redactor, redactRules: cloneRedactRules(logger.redactRules), redactFailMode: logger.redactFailMode, redactMaskToken: logger.redactMaskToken, entryHandlerTimeout: logger.entryHandlerTimeout, formatter: logger.formatter, contextFields: logger.contextFields, } } func (logger *starlog) formatTime(now time.Time) string { y, m, d := now.Date() h, i, s := now.Clock() micro := now.Nanosecond() / 1e3 return fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d.%06d", y, m, d, h, i, s, micro) } func (logger *starlog) buildMeta(fileName string, line int, funcname string, thread string) string { parts := make([]string, 0, 3) if logger.showDeatilFile { parts = append(parts, fileName+":"+strconv.Itoa(line)) } if logger.showFuncName { parts = append(parts, "<"+funcname+">") } if logger.showThread { parts = append(parts, "|"+thread+"|") } return strings.Join(parts, " ") } func (logger *starlog) composeLine(timestamp string, meta string, levelTag string, message string) string { parts := make([]string, 0, 4) parts = append(parts, timestamp) if meta != "" { parts = append(parts, meta) } if logger.showLevel && levelTag != "" { parts = append(parts, levelTag) } parts = append(parts, message) return strings.Join(parts, " ") } func appendNewlineIfNeeded(text string, autoAppend bool) string { if !autoAppend || text == "" { return text } if strings.HasSuffix(text, "\n") { return text } return text + "\n" } func (logger *starlog) highlightKeywords(text string) string { if logger.keywordMatcher != nil { if highlighted, changed := logger.keywordMatcher.highlight(text, logger.keywordMatchOptions); changed { return highlighted } return text } if len(logger.keywordColors) == 0 { return text } keywords := logger.keywordOrder if len(keywords) == 0 { keywords = make([]string, 0, len(logger.keywordColors)) for keyword := range logger.keywordColors { if keyword == "" { continue } keywords = append(keywords, keyword) } if len(keywords) == 0 { return text } sort.Slice(keywords, func(i, j int) bool { return len(keywords[i]) > len(keywords[j]) }) } highlighted := text for _, keyword := range keywords { attrs := logger.keywordColors[keyword] if len(attrs) == 0 { continue } colorizer := logger.keywordColorizers[keyword] highlighted = highlightKeywordText(highlighted, keyword, attrs, logger.keywordMatchOptions, colorizer) } return highlighted } func isWordRune(r rune) bool { return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) } func hasWordBoundary(text string, start int, end int) bool { if start < 0 || end < start || end > len(text) { return false } leftBoundary := true if start > 0 { r, _ := utf8.DecodeLastRuneInString(text[:start]) leftBoundary = !isWordRune(r) } rightBoundary := true if end < len(text) { r, _ := utf8.DecodeRuneInString(text[end:]) rightBoundary = !isWordRune(r) } return leftBoundary && rightBoundary } func (matcher *keywordMatcher) highlight(text string, opts KeywordMatchOptions) (string, bool) { if matcher == nil || text == "" { return text, false } var builder strings.Builder builder.Grow(len(text) + 32) last := 0 changed := false for idx := 0; idx < len(text); { r, size := utf8.DecodeRuneInString(text[idx:]) if size <= 0 { size = 1 } next := idx + size var patterns []keywordPattern if opts.IgnoreCase { patterns = matcher.byFirstRuneFold[unicode.ToLower(r)] } else { patterns = matcher.byFirstRune[r] } matched := false for _, pattern := range patterns { matchEnd := idx + pattern.byteLen if matchEnd > len(text) { continue } candidate := text[idx:matchEnd] ok := false if opts.IgnoreCase { ok = strings.EqualFold(candidate, pattern.keyword) } else { ok = candidate == pattern.keyword } if !ok { continue } if opts.WholeWord && !hasWordBoundary(text, idx, matchEnd) { continue } builder.WriteString(text[last:idx]) if pattern.colorizer != nil { builder.WriteString(pattern.colorizer.Sprint(candidate)) } else { builder.WriteString(candidate) } last = matchEnd idx = matchEnd matched = true changed = true break } if matched { continue } if r == utf8.RuneError && size == 1 { idx++ continue } idx = next } if !changed { return text, false } builder.WriteString(text[last:]) return builder.String(), true } func highlightKeywordText(text string, keyword string, attrs []Attr, opts KeywordMatchOptions, colorizer *Color) string { if text == "" || keyword == "" || len(attrs) == 0 { return text } if colorizer == nil { colorizer = NewColor(attrs...) } if !opts.IgnoreCase && !opts.WholeWord { return strings.ReplaceAll(text, keyword, colorizer.Sprint(keyword)) } var builder strings.Builder builder.Grow(len(text) + 32) changed := false last := 0 for idx := 0; idx < len(text); { r, size := utf8.DecodeRuneInString(text[idx:]) if size <= 0 { size = 1 } next := idx + size matchEnd := idx + len(keyword) if matchEnd <= len(text) { candidate := text[idx:matchEnd] matched := false if opts.IgnoreCase { matched = strings.EqualFold(candidate, keyword) } else { matched = candidate == keyword } if matched { if !opts.WholeWord || hasWordBoundary(text, idx, matchEnd) { builder.WriteString(text[last:idx]) builder.WriteString(colorizer.Sprint(candidate)) last = matchEnd idx = matchEnd changed = true continue } } } if r == utf8.RuneError && size == 1 { idx++ continue } idx = next } if !changed { return text } builder.WriteString(text[last:]) return builder.String() } func (logger *starlog) formatPlainFromEntry(entry *Entry, defaultLine string) string { if logger.formatter == nil { return defaultLine } formatted, err := logger.formatter.Format(entry) if err != nil || len(formatted) == 0 { return defaultLine } return string(formatted) } func (logger *starlog) renderEntryMessage(entry *Entry) string { if entry == nil { return "" } parts := make([]string, 0, 3) if entry.Message != "" { parts = append(parts, entry.Message) } if entry.Err != nil { parts = append(parts, "error="+entry.Err.Error()) } fieldText := renderFields(entry.Fields) if fieldText != "" { parts = append(parts, fieldText) } return strings.Join(parts, " ") } func fieldTypeName(value interface{}) string { switch value.(type) { case nil: return FieldTypeNil case bool: return FieldTypeBool case string: return FieldTypeString case error: return FieldTypeError case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64: return FieldTypeNumber default: return FieldTypeOther } } func (logger *starlog) renderDisplayFieldValue(key string, value interface{}) string { raw := fmt.Sprintf("%v", value) if !logger.showFieldColor { return raw } if attrs, ok := logger.fieldValueColors[key]; ok && len(attrs) > 0 { return NewColor(attrs...).Sprint(raw) } typeName := fieldTypeName(value) if attrs, ok := logger.fieldTypeColors[typeName]; ok && len(attrs) > 0 { return NewColor(attrs...).Sprint(raw) } return raw } func (logger *starlog) renderDisplayFields(fields Fields) string { if len(fields) == 0 { return "" } keys := make([]string, 0, len(fields)) for key := range fields { keys = append(keys, key) } sort.Strings(keys) items := make([]string, 0, len(keys)) for _, key := range keys { keyText := key if logger.showFieldColor && len(logger.fieldKeyColor) > 0 { keyText = NewColor(logger.fieldKeyColor...).Sprint(keyText) } valueText := logger.renderDisplayFieldValue(key, fields[key]) items = append(items, keyText+"="+valueText) } return strings.Join(items, " ") } func (logger *starlog) renderDisplayMessage(entry *Entry) string { if entry == nil { return "" } parts := make([]string, 0, 2) base := entry.Message if entry.Err != nil { if base != "" { base += " " } base += "error=" + entry.Err.Error() } if base != "" { parts = append(parts, logger.highlightKeywords(base)) } fieldText := logger.renderDisplayFields(entry.Fields) if fieldText != "" { parts = append(parts, fieldText) } return strings.Join(parts, " ") } func (logger *starlog) flushPendingWritesLocked(name string, colors []Attr) { if logger.switching { return } for len(logger.pendingWrites) > 0 { if logger.switching { return } logStr := logger.pendingWrites[0] logger.pendingWrites = logger.pendingWrites[1:] logger.signalPendingCondLocked() if err := logger.writeDirect(logStr); err != nil { reportWriteError(err, LogData{ Name: name, Log: logStr, Colors: colors, }) } } } func (logger *starlog) signalPendingCondLocked() { if logger.pendingCond != nil { logger.pendingCond.Broadcast() } } func (logger *starlog) updatePendingPeakLocked() { current := uint64(len(logger.pendingWrites)) for { peak := atomic.LoadUint64(&logger.pendingPeakLen) if current <= peak { return } if atomic.CompareAndSwapUint64(&logger.pendingPeakLen, peak, current) { return } } } func (logger *starlog) writeDirect(logStr string) error { if logger.sink != nil { return logger.sink.Write([]byte(logStr)) } if logger.output == nil { return nil } _, err := logger.output.Write([]byte(logStr)) return err } func (logger *starlog) enqueuePendingWriteLocked(logStr string, data LogData) bool { for logger.switching && logger.pendingWriteLimit > 0 && len(logger.pendingWrites) >= logger.pendingWriteLimit { switch logger.pendingDropPolicy { case PendingDropNewest: atomic.AddUint64(&logger.pendingDropCount, 1) reportWriteError(ErrPendingWriteDropped, data) return true case PendingBlock: atomic.AddUint64(&logger.pendingBlockCount, 1) if logger.pendingCond == nil { atomic.AddUint64(&logger.pendingDropCount, 1) reportWriteError(ErrPendingWriteDropped, data) return true } logger.pendingCond.Wait() default: atomic.AddUint64(&logger.pendingDropCount, 1) dropData := data dropData.Log = logger.pendingWrites[0] logger.pendingWrites = logger.pendingWrites[1:] logger.signalPendingCondLocked() reportWriteError(ErrPendingWriteDropped, dropData) } } if !logger.switching { return false } logger.pendingWrites = append(logger.pendingWrites, logStr) logger.updatePendingPeakLocked() return true } func (logger *starlog) invokeEntryHandler(handler Handler, timeout time.Duration, entry *Entry, data LogData) { if handler == nil || entry == nil { return } handlerCtx := entry.Context if handlerCtx == nil { handlerCtx = context.Background() } run := func() { defer func() { if panicErr := recover(); panicErr != nil { reportAsyncDrop(fmt.Errorf("%w: %v", ErrAsyncHandlerPanic, panicErr), data) } }() if err := handler.Handle(handlerCtx, entry); err != nil { reportWriteError(err, data) } } if timeout <= 0 { run() return } done := make(chan struct{}, 1) go func() { run() done <- struct{}{} }() select { case <-done: case <-time.After(timeout): reportAsyncDrop(ErrAsyncHandlerTimeout, data) } } func (logger *starlog) enqueueAsyncTransfer(transfer logTransfer, fallbackSync bool) { StartStacks() if stacks == nil { reportAsyncDrop(io.ErrClosedPipe, transfer.LogData) if fallbackSync && GetAsyncFallbackToSync() { invokeAsyncHandlerSafely(transfer.handlerFunc, transfer.LogData) } return } if err := stacks.TryPush(transfer); err != nil { if errors.Is(err, errStackFull) { reportAsyncDrop(ErrAsyncQueueFull, transfer.LogData) } else { reportAsyncDrop(err, transfer.LogData) } if fallbackSync && GetAsyncFallbackToSync() { invokeAsyncHandlerSafely(transfer.handlerFunc, transfer.LogData) } } } func (logger *starlog) build(thread string, isStd bool, isShow bool, handler func(data LogData), level int, logDetail string, fields Fields, logErr error, ctx context.Context) { logger.mu.Lock() snapshot := logger.snapshotForBuildLocked() logger.mu.Unlock() if level < snapshot.minLevel { return } var skip, line int = 3, 0 var funcname, fileName string now := time.Now() if isStd { skip++ } if snapshot.showDeatilFile || snapshot.showFuncName { pc, fName, codeln, ok := runtime.Caller(skip) if !ok { return } line = codeln funcname = runtime.FuncForPC(pc).Name() funcname = filepath.Ext(funcname) funcname = strings.TrimPrefix(funcname, ".") fileName = filepath.Base(fName) } levelName := snapshot.levelName(level) levelTag := "[" + levelName + "]" timestamp := snapshot.formatTime(now) meta := snapshot.buildMeta(fileName, line, funcname, thread) contextFields := Fields(nil) if ctx != nil && snapshot.contextFields != nil { contextFields = snapshot.contextFields(ctx) } mergedFields := mergeFields(fields, contextFields) entry := &Entry{ Time: now, Level: level, LevelName: levelName, LoggerName: snapshot.name, Thread: thread, File: fileName, Line: line, Func: funcname, Message: logDetail, Fields: mergedFields, Err: logErr, Context: ctx, } if !logger.applyRedaction(snapshot, entry) { return } if !logger.allowByDedup(entry) { return } if !logger.allowBySampling(entry) { return } if !logger.allowByRateLimit(entry) { return } messageText := snapshot.renderEntryMessage(entry) defaultPlain := snapshot.composeLine(timestamp, meta, levelTag, messageText) plainLine := snapshot.formatPlainFromEntry(entry, defaultPlain) plainLine = appendNewlineIfNeeded(plainLine, snapshot.autoAppendNewline) displayLine := plainLine logData := LogData{ Log: plainLine, Colors: snapshot.levelAttrs(level), Name: snapshot.name, } if isShow && snapshot.showColor { if snapshot.onlyColorLevel && snapshot.formatter == nil { levelSegment := levelTag if snapshot.showLevel { levelSegment = snapshot.levelColor(level).Sprint(levelTag) } messageSegment := snapshot.renderDisplayMessage(entry) displayLine = snapshot.composeLine(timestamp, meta, levelSegment, messageSegment) } else { displayLine = snapshot.levelColor(level).Sprint(plainLine) } } if isShow { if level < snapshot.errOutputLevel { if snapshot.showColor { if _, err := fmt.Fprint(stdScreen, displayLine); err != nil { reportWriteError(err, logData) } } else { if _, err := fmt.Fprint(os.Stdout, displayLine); err != nil { reportWriteError(err, logData) } } } else { if snapshot.showColor { if _, err := fmt.Fprint(errScreen, displayLine); err != nil { reportWriteError(err, logData) } } else { if _, err := fmt.Fprint(os.Stderr, displayLine); err != nil { reportWriteError(err, logData) } } } } if handler != nil { logger.enqueueAsyncTransfer(logTransfer{ handlerFunc: handler, LogData: logData, }, true) } if snapshot.entryHandler != nil { entryCopy := *entry logger.enqueueAsyncTransfer(logTransfer{ handlerFunc: func(data LogData) { entryVal := entryCopy logger.invokeEntryHandler(snapshot.entryHandler, snapshot.entryHandlerTimeout, &entryVal, data) }, LogData: logData, }, false) } if !snapshot.stopWriter { logger.writeWithData(plainLine, logData) } } func (logger *starlog) write(logStr string) { logger.writeWithData(logStr, LogData{Log: logStr}) } func (logger *starlog) writeWithData(logStr string, data LogData) { logger.mu.Lock() defer logger.mu.Unlock() if logger.stopWriter { return } if data.Name == "" { data.Name = logger.name } logStr = appendNewlineIfNeeded(logStr, logger.autoAppendNewline) data.Log = logStr if logger.switching { if logger.enqueuePendingWriteLocked(logStr, data) { return } } logger.flushPendingWritesLocked(data.Name, data.Colors) if err := logger.writeDirect(logStr); err != nil { reportWriteError(err, data) } } func (logger *starlog) writePendingLocked() { if logger.stopWriter { logger.pendingWrites = nil logger.signalPendingCondLocked() return } logger.flushPendingWritesLocked(logger.name, nil) logger.signalPendingCondLocked() } func (logger *starlog) print(str ...interface{}) string { return fmt.Sprint(str...) } func (logger *starlog) printf(format string, str ...interface{}) string { return fmt.Sprintf(format, str...) } func (logger *starlog) println(str ...interface{}) string { return fmt.Sprintln(str...) } func (logger *starlog) Debug(thread string, isStd bool, handler func(LogData), str ...interface{}) { strs := fmt.Sprint(str...) logger.build(thread, isStd, logger.showStd, handler, LvDebug, strs, nil, nil, nil) } func (logger *starlog) Debugf(thread string, isStd bool, handler func(LogData), format string, str ...interface{}) { strs := fmt.Sprintf(format, str...) logger.build(thread, isStd, logger.showStd, handler, LvDebug, strs, nil, nil, nil) } func (logger *starlog) Debugln(thread string, isStd bool, handler func(LogData), str ...interface{}) { strs := fmt.Sprintln(str...) logger.build(thread, isStd, logger.showStd, handler, LvDebug, strs, nil, nil, nil) } func (logger *starlog) Info(thread string, isStd bool, handler func(LogData), str ...interface{}) { strs := fmt.Sprint(str...) logger.build(thread, isStd, logger.showStd, handler, LvInfo, strs, nil, nil, nil) } func (logger *starlog) Infof(thread string, isStd bool, handler func(LogData), format string, str ...interface{}) { strs := fmt.Sprintf(format, str...) logger.build(thread, isStd, logger.showStd, handler, LvInfo, strs, nil, nil, nil) } func (logger *starlog) Infoln(thread string, isStd bool, handler func(LogData), str ...interface{}) { strs := fmt.Sprintln(str...) logger.build(thread, isStd, logger.showStd, handler, LvInfo, strs, nil, nil, nil) } func (logger *starlog) Notice(thread string, isStd bool, handler func(LogData), str ...interface{}) { strs := fmt.Sprint(str...) logger.build(thread, isStd, logger.showStd, handler, LvNotice, strs, nil, nil, nil) } func (logger *starlog) Noticef(thread string, isStd bool, handler func(LogData), format string, str ...interface{}) { strs := fmt.Sprintf(format, str...) logger.build(thread, isStd, logger.showStd, handler, LvNotice, strs, nil, nil, nil) } func (logger *starlog) Noticeln(thread string, isStd bool, handler func(LogData), str ...interface{}) { strs := fmt.Sprintln(str...) logger.build(thread, isStd, logger.showStd, handler, LvNotice, strs, nil, nil, nil) } func (logger *starlog) Warning(thread string, isStd bool, handler func(LogData), str ...interface{}) { strs := fmt.Sprint(str...) logger.build(thread, isStd, logger.showStd, handler, LvWarning, strs, nil, nil, nil) } func (logger *starlog) Warningf(thread string, isStd bool, handler func(LogData), format string, str ...interface{}) { strs := fmt.Sprintf(format, str...) logger.build(thread, isStd, logger.showStd, handler, LvWarning, strs, nil, nil, nil) } func (logger *starlog) Warningln(thread string, isStd bool, handler func(LogData), str ...interface{}) { strs := fmt.Sprintln(str...) logger.build(thread, isStd, logger.showStd, handler, LvWarning, strs, nil, nil, nil) } func (logger *starlog) Error(thread string, isStd bool, handler func(LogData), str ...interface{}) { strs := fmt.Sprint(str...) logger.build(thread, isStd, logger.showStd, handler, LvError, strs, nil, nil, nil) } func (logger *starlog) Errorf(thread string, isStd bool, handler func(LogData), format string, str ...interface{}) { strs := fmt.Sprintf(format, str...) logger.build(thread, isStd, logger.showStd, handler, LvError, strs, nil, nil, nil) } func (logger *starlog) Errorln(thread string, isStd bool, handler func(LogData), str ...interface{}) { strs := fmt.Sprintln(str...) logger.build(thread, isStd, logger.showStd, handler, LvError, strs, nil, nil, nil) } func (logger *starlog) Critical(thread string, isStd bool, handler func(LogData), str ...interface{}) { strs := fmt.Sprint(str...) logger.build(thread, isStd, logger.showStd, handler, LvCritical, strs, nil, nil, nil) } func (logger *starlog) Criticalf(thread string, isStd bool, handler func(LogData), format string, str ...interface{}) { strs := fmt.Sprintf(format, str...) logger.build(thread, isStd, logger.showStd, handler, LvCritical, strs, nil, nil, nil) } func (logger *starlog) Criticalln(thread string, isStd bool, handler func(LogData), str ...interface{}) { strs := fmt.Sprintln(str...) logger.build(thread, isStd, logger.showStd, handler, LvCritical, strs, nil, nil, nil) } func (logger *starlog) Fatal(thread string, isStd bool, handler func(LogData), str ...interface{}) { strs := fmt.Sprint(str...) logger.build(thread, isStd, logger.showStd, handler, LvFatal, strs, nil, nil, nil) os.Exit(9) } func (logger *starlog) Fatalf(thread string, isStd bool, handler func(LogData), format string, str ...interface{}) { strs := fmt.Sprintf(format, str...) logger.build(thread, isStd, logger.showStd, handler, LvFatal, strs, nil, nil, nil) os.Exit(9) } func (logger *starlog) Fatalln(thread string, isStd bool, handler func(LogData), str ...interface{}) { strs := fmt.Sprintln(str...) logger.build(thread, isStd, logger.showStd, handler, LvFatal, strs, nil, nil, nil) os.Exit(9) } func (logger *starlog) Panic(thread string, isStd bool, handler func(LogData), str ...interface{}) { strs := fmt.Sprint(str...) logger.build(thread, isStd, logger.showStd, handler, LvPanic, strs, nil, nil, nil) panic(str) } func (logger *starlog) Panicf(thread string, isStd bool, handler func(LogData), format string, str ...interface{}) { strs := fmt.Sprintf(format, str...) logger.build(thread, isStd, logger.showStd, handler, LvPanic, strs, nil, nil, nil) panic(fmt.Sprintf(format, str...)) } func (logger *starlog) Panicln(thread string, isStd bool, handler func(LogData), str ...interface{}) { strs := fmt.Sprintln(str...) logger.build(thread, isStd, logger.showStd, handler, LvPanic, strs, nil, nil, nil) panic(fmt.Sprintln(str...)) } func (logger *starlog) Print(thread string, isStd bool, isShow bool, handler func(LogData), str ...interface{}) { strs := fmt.Sprint(str...) if isShow { fmt.Print(strs) } logger.write(strs) } func (logger *starlog) Printf(thread string, isStd bool, isShow bool, handler func(LogData), format string, str ...interface{}) { strs := fmt.Sprintf(format, str...) if isShow { fmt.Print(strs) } logger.write(strs) } func (logger *starlog) Println(thread string, isStd bool, isShow bool, handler func(LogData), str ...interface{}) { strs := fmt.Sprintln(str...) if isShow { fmt.Print(strs) } logger.write(strs) } func (logger *starlog) Log(thread string, isStd bool, isShow bool, level int, handler func(LogData), str ...interface{}) { strs := fmt.Sprint(str...) logger.build(thread, isStd, isShow, handler, level, strs, nil, nil, nil) } func (logger *starlog) Logf(thread string, isStd bool, isShow bool, level int, handler func(LogData), format string, str ...interface{}) { strs := fmt.Sprintf(format, str...) logger.build(thread, isStd, isShow, handler, level, strs, nil, nil, nil) } func (logger *starlog) Logln(thread string, isStd bool, isShow bool, level int, handler func(LogData), str ...interface{}) { strs := fmt.Sprintln(str...) logger.build(thread, isStd, isShow, handler, level, strs, nil, nil, nil) } func (logger *starlog) Write(str ...interface{}) { strs := fmt.Sprint(str...) logger.write(strs) } func (logger *starlog) Writef(format string, str ...interface{}) { strs := fmt.Sprintf(format, str...) logger.Write(strs) } func (logger *starlog) Writeln(str ...interface{}) { strs := fmt.Sprintln(str...) logger.Write(strs) }