package logrus

import (
	
	
	
	
	
	
	
	
	
	
)

const (
	red    = 31
	yellow = 33
	blue   = 36
	gray   = 37
)

var baseTimestamp time.Time

func () {
	baseTimestamp = time.Now()
}

// TextFormatter formats logs into text
type TextFormatter struct {
	// Set to true to bypass checking for a TTY before outputting colors.
	ForceColors bool

	// Force disabling colors.
	DisableColors bool

	// Force quoting of all values
	ForceQuote bool

	// DisableQuote disables quoting for all values.
	// DisableQuote will have a lower priority than ForceQuote.
	// If both of them are set to true, quote will be forced on all values.
	DisableQuote bool

	// Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/
	EnvironmentOverrideColors bool

	// Disable timestamp logging. useful when output is redirected to logging
	// system that already adds timestamps.
	DisableTimestamp bool

	// Enable logging the full timestamp when a TTY is attached instead of just
	// the time passed since beginning of execution.
	FullTimestamp bool

	// TimestampFormat to use for display when a full timestamp is printed.
	// The format to use is the same than for time.Format or time.Parse from the standard
	// library.
	// The standard Library already provides a set of predefined format.
	TimestampFormat string

	// The fields are sorted by default for a consistent output. For applications
	// that log extremely frequently and don't use the JSON formatter this may not
	// be desired.
	DisableSorting bool

	// The keys sorting function, when uninitialized it uses sort.Strings.
	SortingFunc func([]string)

	// Disables the truncation of the level text to 4 characters.
	DisableLevelTruncation bool

	// PadLevelText Adds padding the level text so that all the levels output at the same length
	// PadLevelText is a superset of the DisableLevelTruncation option
	PadLevelText bool

	// QuoteEmptyFields will wrap empty fields in quotes if true
	QuoteEmptyFields bool

	// Whether the logger's out is to a terminal
	isTerminal bool

	// FieldMap allows users to customize the names of keys for default fields.
	// As an example:
	// formatter := &TextFormatter{
	//     FieldMap: FieldMap{
	//         FieldKeyTime:  "@timestamp",
	//         FieldKeyLevel: "@level",
	//         FieldKeyMsg:   "@message"}}
	FieldMap FieldMap

	// CallerPrettyfier can be set by the user to modify the content
	// of the function and file keys in the data when ReportCaller is
	// activated. If any of the returned value is the empty string the
	// corresponding key will be removed from fields.
	CallerPrettyfier func(*runtime.Frame) (function string, file string)

	terminalInitOnce sync.Once

	// The max length of the level text, generated dynamically on init
	levelTextMaxLength int
}

func ( *TextFormatter) ( *Entry) {
	if .Logger != nil {
		.isTerminal = checkIfTerminal(.Logger.Out)
	}
	// Get the max length of the level text
	for ,  := range AllLevels {
		 := utf8.RuneCount([]byte(.String()))
		if  > .levelTextMaxLength {
			.levelTextMaxLength = 
		}
	}
}

func ( *TextFormatter) () bool {
	 := .ForceColors || (.isTerminal && (runtime.GOOS != "windows"))

	if .EnvironmentOverrideColors {
		switch ,  := os.LookupEnv("CLICOLOR_FORCE"); {
		case  &&  != "0":
			 = true
		case  &&  == "0", os.Getenv("CLICOLOR") == "0":
			 = false
		}
	}

	return  && !.DisableColors
}

// Format renders a single log entry
func ( *TextFormatter) ( *Entry) ([]byte, error) {
	 := make(Fields)
	for ,  := range .Data {
		[] = 
	}
	prefixFieldClashes(, .FieldMap, .HasCaller())
	 := make([]string, 0, len())
	for  := range  {
		 = append(, )
	}

	var ,  string

	 := make([]string, 0, 4+len())
	if !.DisableTimestamp {
		 = append(, .FieldMap.resolve(FieldKeyTime))
	}
	 = append(, .FieldMap.resolve(FieldKeyLevel))
	if .Message != "" {
		 = append(, .FieldMap.resolve(FieldKeyMsg))
	}
	if .err != "" {
		 = append(, .FieldMap.resolve(FieldKeyLogrusError))
	}
	if .HasCaller() {
		if .CallerPrettyfier != nil {
			,  = .CallerPrettyfier(.Caller)
		} else {
			 = .Caller.Function
			 = fmt.Sprintf("%s:%d", .Caller.File, .Caller.Line)
		}

		if  != "" {
			 = append(, .FieldMap.resolve(FieldKeyFunc))
		}
		if  != "" {
			 = append(, .FieldMap.resolve(FieldKeyFile))
		}
	}

	if !.DisableSorting {
		if .SortingFunc == nil {
			sort.Strings()
			 = append(, ...)
		} else {
			if !.isColored() {
				 = append(, ...)
				.SortingFunc()
			} else {
				.SortingFunc()
			}
		}
	} else {
		 = append(, ...)
	}

	var  *bytes.Buffer
	if .Buffer != nil {
		 = .Buffer
	} else {
		 = &bytes.Buffer{}
	}

	.terminalInitOnce.Do(func() { .init() })

	 := .TimestampFormat
	if  == "" {
		 = defaultTimestampFormat
	}
	if .isColored() {
		.printColored(, , , , )
	} else {

		for ,  := range  {
			var  interface{}
			switch {
			case  == .FieldMap.resolve(FieldKeyTime):
				 = .Time.Format()
			case  == .FieldMap.resolve(FieldKeyLevel):
				 = .Level.String()
			case  == .FieldMap.resolve(FieldKeyMsg):
				 = .Message
			case  == .FieldMap.resolve(FieldKeyLogrusError):
				 = .err
			case  == .FieldMap.resolve(FieldKeyFunc) && .HasCaller():
				 = 
			case  == .FieldMap.resolve(FieldKeyFile) && .HasCaller():
				 = 
			default:
				 = []
			}
			.appendKeyValue(, , )
		}
	}

	.WriteByte('\n')
	return .Bytes(), nil
}

func ( *TextFormatter) ( *bytes.Buffer,  *Entry,  []string,  Fields,  string) {
	var  int
	switch .Level {
	case DebugLevel, TraceLevel:
		 = gray
	case WarnLevel:
		 = yellow
	case ErrorLevel, FatalLevel, PanicLevel:
		 = red
	case InfoLevel:
		 = blue
	default:
		 = blue
	}

	 := strings.ToUpper(.Level.String())
	if !.DisableLevelTruncation && !.PadLevelText {
		 = [0:4]
	}
	if .PadLevelText {
		// Generates the format string used in the next line, for example "%-6s" or "%-7s".
		// Based on the max level text length.
		 := "%-" + strconv.Itoa(.levelTextMaxLength) + "s"
		// Formats the level text by appending spaces up to the max length, for example:
		// 	- "INFO   "
		//	- "WARNING"
		 = fmt.Sprintf(, )
	}

	// Remove a single newline if it already exists in the message to keep
	// the behavior of logrus text_formatter the same as the stdlib log package
	.Message = strings.TrimSuffix(.Message, "\n")

	 := ""
	if .HasCaller() {
		 := fmt.Sprintf("%s()", .Caller.Function)
		 := fmt.Sprintf("%s:%d", .Caller.File, .Caller.Line)

		if .CallerPrettyfier != nil {
			,  = .CallerPrettyfier(.Caller)
		}

		if  == "" {
			 = 
		} else if  == "" {
			 = 
		} else {
			 =  + " " + 
		}
	}

	switch {
	case .DisableTimestamp:
		fmt.Fprintf(, "\x1b[%dm%s\x1b[0m%s %-44s ", , , , .Message)
	case !.FullTimestamp:
		fmt.Fprintf(, "\x1b[%dm%s\x1b[0m[%04d]%s %-44s ", , , int(.Time.Sub(baseTimestamp)/time.Second), , .Message)
	default:
		fmt.Fprintf(, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", , , .Time.Format(), , .Message)
	}
	for ,  := range  {
		 := []
		fmt.Fprintf(, " \x1b[%dm%s\x1b[0m=", , )
		.appendValue(, )
	}
}

func ( *TextFormatter) ( string) bool {
	if .ForceQuote {
		return true
	}
	if .QuoteEmptyFields && len() == 0 {
		return true
	}
	if .DisableQuote {
		return false
	}
	for ,  := range  {
		if !(( >= 'a' &&  <= 'z') ||
			( >= 'A' &&  <= 'Z') ||
			( >= '0' &&  <= '9') ||
			 == '-' ||  == '.' ||  == '_' ||  == '/' ||  == '@' ||  == '^' ||  == '+') {
			return true
		}
	}
	return false
}

func ( *TextFormatter) ( *bytes.Buffer,  string,  interface{}) {
	if .Len() > 0 {
		.WriteByte(' ')
	}
	.WriteString()
	.WriteByte('=')
	.appendValue(, )
}

func ( *TextFormatter) ( *bytes.Buffer,  interface{}) {
	,  := .(string)
	if ! {
		 = fmt.Sprint()
	}

	if !.needsQuoting() {
		.WriteString()
	} else {
		.WriteString(fmt.Sprintf("%q", ))
	}
}