github.com/sagernet/sing-box@v1.9.0-rc.20/log/format.go (about)

     1  package log
     2  
     3  import (
     4  	"context"
     5  	"strconv"
     6  	"strings"
     7  	"time"
     8  
     9  	F "github.com/sagernet/sing/common/format"
    10  
    11  	"github.com/logrusorgru/aurora"
    12  )
    13  
    14  type Formatter struct {
    15  	BaseTime         time.Time
    16  	DisableColors    bool
    17  	DisableTimestamp bool
    18  	FullTimestamp    bool
    19  	TimestampFormat  string
    20  	DisableLineBreak bool
    21  }
    22  
    23  func (f Formatter) Format(ctx context.Context, level Level, tag string, message string, timestamp time.Time) string {
    24  	levelString := strings.ToUpper(FormatLevel(level))
    25  	if !f.DisableColors {
    26  		switch level {
    27  		case LevelDebug, LevelTrace:
    28  			levelString = aurora.White(levelString).String()
    29  		case LevelInfo:
    30  			levelString = aurora.Cyan(levelString).String()
    31  		case LevelWarn:
    32  			levelString = aurora.Yellow(levelString).String()
    33  		case LevelError, LevelFatal, LevelPanic:
    34  			levelString = aurora.Red(levelString).String()
    35  		}
    36  	}
    37  	if tag != "" {
    38  		message = tag + ": " + message
    39  	}
    40  	var id ID
    41  	var hasId bool
    42  	if ctx != nil {
    43  		id, hasId = IDFromContext(ctx)
    44  	}
    45  	if hasId {
    46  		activeDuration := formatDuration(time.Since(id.CreatedAt))
    47  		if !f.DisableColors {
    48  			var color aurora.Color
    49  			color = aurora.Color(uint8(id.ID))
    50  			color %= 215
    51  			row := uint(color / 36)
    52  			column := uint(color % 36)
    53  
    54  			var r, g, b float32
    55  			r = float32(row * 51)
    56  			g = float32(column / 6 * 51)
    57  			b = float32((column % 6) * 51)
    58  			luma := 0.2126*r + 0.7152*g + 0.0722*b
    59  			if luma < 60 {
    60  				row = 5 - row
    61  				column = 35 - column
    62  				color = aurora.Color(row*36 + column)
    63  			}
    64  			color += 16
    65  			color = color << 16
    66  			color |= 1 << 14
    67  			message = F.ToString("[", aurora.Colorize(id.ID, color).String(), " ", activeDuration, "] ", message)
    68  		} else {
    69  			message = F.ToString("[", id.ID, " ", activeDuration, "] ", message)
    70  		}
    71  	}
    72  	switch {
    73  	case f.DisableTimestamp:
    74  		message = levelString + " " + message
    75  	case f.FullTimestamp:
    76  		message = timestamp.Format(f.TimestampFormat) + " " + levelString + " " + message
    77  	default:
    78  		message = levelString + "[" + xd(int(timestamp.Sub(f.BaseTime)/time.Second), 4) + "] " + message
    79  	}
    80  	if f.DisableLineBreak {
    81  		if message[len(message)-1] == '\n' {
    82  			message = message[:len(message)-1]
    83  		}
    84  	} else {
    85  		if message[len(message)-1] != '\n' {
    86  			message += "\n"
    87  		}
    88  	}
    89  	return message
    90  }
    91  
    92  func (f Formatter) FormatWithSimple(ctx context.Context, level Level, tag string, message string, timestamp time.Time) (string, string) {
    93  	levelString := strings.ToUpper(FormatLevel(level))
    94  	if !f.DisableColors {
    95  		switch level {
    96  		case LevelDebug, LevelTrace:
    97  			levelString = aurora.White(levelString).String()
    98  		case LevelInfo:
    99  			levelString = aurora.Cyan(levelString).String()
   100  		case LevelWarn:
   101  			levelString = aurora.Yellow(levelString).String()
   102  		case LevelError, LevelFatal, LevelPanic:
   103  			levelString = aurora.Red(levelString).String()
   104  		}
   105  	}
   106  	if tag != "" {
   107  		message = tag + ": " + message
   108  	}
   109  	messageSimple := message
   110  	var id ID
   111  	var hasId bool
   112  	if ctx != nil {
   113  		id, hasId = IDFromContext(ctx)
   114  	}
   115  	if hasId {
   116  		activeDuration := formatDuration(time.Since(id.CreatedAt))
   117  		if !f.DisableColors {
   118  			var color aurora.Color
   119  			color = aurora.Color(uint8(id.ID))
   120  			color %= 215
   121  			row := uint(color / 36)
   122  			column := uint(color % 36)
   123  
   124  			var r, g, b float32
   125  			r = float32(row * 51)
   126  			g = float32(column / 6 * 51)
   127  			b = float32((column % 6) * 51)
   128  			luma := 0.2126*r + 0.7152*g + 0.0722*b
   129  			if luma < 60 {
   130  				row = 5 - row
   131  				column = 35 - column
   132  				color = aurora.Color(row*36 + column)
   133  			}
   134  			color += 16
   135  			color = color << 16
   136  			color |= 1 << 14
   137  			message = F.ToString("[", aurora.Colorize(id.ID, color).String(), " ", activeDuration, "] ", message)
   138  		} else {
   139  			message = F.ToString("[", id.ID, " ", activeDuration, "] ", message)
   140  		}
   141  		messageSimple = F.ToString("[", id.ID, " ", activeDuration, "] ", messageSimple)
   142  
   143  	}
   144  	switch {
   145  	case f.DisableTimestamp:
   146  		message = levelString + " " + message
   147  	case f.FullTimestamp:
   148  		message = timestamp.Format(f.TimestampFormat) + " " + levelString + " " + message
   149  	default:
   150  		message = levelString + "[" + xd(int(timestamp.Sub(f.BaseTime)/time.Second), 4) + "] " + message
   151  	}
   152  	if message[len(message)-1] != '\n' {
   153  		message += "\n"
   154  	}
   155  	return message, messageSimple
   156  }
   157  
   158  func xd(value int, x int) string {
   159  	message := strconv.Itoa(value)
   160  	for len(message) < x {
   161  		message = "0" + message
   162  	}
   163  	return message
   164  }
   165  
   166  func formatDuration(duration time.Duration) string {
   167  	if duration < time.Second {
   168  		return F.ToString(duration.Milliseconds(), "ms")
   169  	} else if duration < time.Minute {
   170  		return F.ToString(int64(duration.Seconds()), ".", int64(duration.Seconds()*100)%100, "s")
   171  	} else {
   172  		return F.ToString(int64(duration.Minutes()), "m", int64(duration.Seconds())%60, "s")
   173  	}
   174  }