github.com/yaling888/clash@v1.53.0/log/log.go (about)

     1  package log
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"sync"
     7  	"time"
     8  	_ "unsafe"
     9  
    10  	logger "github.com/phuslu/log"
    11  
    12  	"github.com/yaling888/clash/common/observable"
    13  )
    14  
    15  var (
    16  	textCh     = make(chan Event)
    17  	jsonCh     = make(chan Event)
    18  	textSource = observable.NewObservable[Event](textCh)
    19  	jsonSource = observable.NewObservable[Event](jsonCh)
    20  
    21  	level       = INFO
    22  	tracing     = false
    23  	enabledText = false
    24  	enabledJson = false
    25  
    26  	bbPool = sync.Pool{
    27  		New: func() any {
    28  			return new(bb)
    29  		},
    30  	}
    31  )
    32  
    33  func init() {
    34  	var (
    35  		timeFormat  = time.RFC3339
    36  		colorOutput = false
    37  	)
    38  	if logger.IsTerminal(os.Stdout.Fd()) {
    39  		timeFormat = "15:04:05"
    40  		colorOutput = true
    41  	}
    42  
    43  	logger.DefaultLogger = logger.Logger{
    44  		Level:      logger.DebugLevel,
    45  		TimeFormat: timeFormat,
    46  		// Caller:     1,
    47  		Writer: &multiWriter{
    48  			textWriter:    &logger.IOWriter{Writer: &apiWriter{isJson: false}},
    49  			jsonWriter:    &logger.IOWriter{Writer: &apiWriter{isJson: true}},
    50  			consoleWriter: &logger.ConsoleWriter{ColorOutput: colorOutput, Writer: os.Stdout},
    51  			consoleLevel:  logger.InfoLevel,
    52  		},
    53  	}
    54  }
    55  
    56  func SubscribeText() observable.Subscription[Event] {
    57  	sub, _ := textSource.Subscribe()
    58  	enabledText = true
    59  	logger.DefaultLogger.SetLevel(logger.DebugLevel)
    60  	return sub
    61  }
    62  
    63  func UnSubscribeText(sub observable.Subscription[Event]) {
    64  	textSource.UnSubscribe(sub)
    65  	if !textSource.HasSubscriber() {
    66  		enabledText = false
    67  		if !jsonSource.HasSubscriber() {
    68  			logger.DefaultLogger.SetLevel(logger.DefaultLogger.Writer.(*multiWriter).consoleLevel)
    69  		}
    70  	}
    71  }
    72  
    73  func SubscribeJson() observable.Subscription[Event] {
    74  	sub, _ := jsonSource.Subscribe()
    75  	enabledJson = true
    76  	logger.DefaultLogger.SetLevel(logger.DebugLevel)
    77  	return sub
    78  }
    79  
    80  func UnSubscribeJson(sub observable.Subscription[Event]) {
    81  	jsonSource.UnSubscribe(sub)
    82  	if !jsonSource.HasSubscriber() {
    83  		enabledJson = false
    84  		if !textSource.HasSubscriber() {
    85  			logger.DefaultLogger.SetLevel(logger.DefaultLogger.Writer.(*multiWriter).consoleLevel)
    86  		}
    87  	}
    88  }
    89  
    90  func Level() LogLevel {
    91  	return level
    92  }
    93  
    94  func SetTracing(t bool) {
    95  	tracing = t
    96  }
    97  
    98  type Event struct {
    99  	LogLevel LogLevel
   100  	Payload  string
   101  }
   102  
   103  func (e *Event) Type() string {
   104  	return e.LogLevel.String()
   105  }
   106  
   107  type bb struct {
   108  	B []byte
   109  }
   110  
   111  type apiWriter struct {
   112  	isJson bool
   113  }
   114  
   115  func (aw *apiWriter) Write(p []byte) (n int, err error) {
   116  	b := bbPool.Get().(*bb)
   117  	b.B = b.B[:0]
   118  	defer bbPool.Put(b)
   119  
   120  	b.B = append(b.B, p...)
   121  
   122  	var (
   123  		args     logger.FormatterArgs
   124  		logLevel LogLevel
   125  	)
   126  
   127  	parseFormatterArgs(b.B, &args)
   128  
   129  	switch args.Level {
   130  	case "debug":
   131  		logLevel = DEBUG
   132  	case "info":
   133  		logLevel = INFO
   134  	case "warn":
   135  		logLevel = WARNING
   136  	case "error":
   137  		logLevel = ERROR
   138  	case "fatal":
   139  		logLevel = FATAL
   140  	default:
   141  		logLevel = SILENT
   142  	}
   143  
   144  	if aw.isJson {
   145  		formatJson(logLevel, p)
   146  	} else {
   147  		formatText(logLevel, &args)
   148  	}
   149  	return
   150  }
   151  
   152  type multiWriter struct {
   153  	textWriter    *logger.IOWriter
   154  	jsonWriter    *logger.IOWriter
   155  	consoleWriter *logger.ConsoleWriter
   156  	consoleLevel  logger.Level
   157  }
   158  
   159  func (mw *multiWriter) Close() (err error) {
   160  	return mw.consoleWriter.Close()
   161  }
   162  
   163  func (mw *multiWriter) WriteEntry(entry *logger.Entry) (n int, err error) {
   164  	if tracing {
   165  		if enabledText {
   166  			_, _ = mw.textWriter.WriteEntry(entry)
   167  		}
   168  		if enabledJson {
   169  			_, _ = mw.jsonWriter.WriteEntry(entry)
   170  		}
   171  	}
   172  
   173  	if entry.Level >= mw.consoleLevel {
   174  		_, _ = mw.consoleWriter.WriteEntry(entry)
   175  	}
   176  	return
   177  }
   178  
   179  func formatText(logLevel LogLevel, args *logger.FormatterArgs) {
   180  	b := bbPool.Get().(*bb)
   181  	b.B = b.B[:0]
   182  	defer bbPool.Put(b)
   183  
   184  	b.B = fmt.Appendf(b.B, " %s", args.Message)
   185  
   186  	for _, kv := range args.KeyValues {
   187  		b.B = fmt.Appendf(b.B, " %s=%s", kv.Key, kv.Value)
   188  	}
   189  
   190  	event := Event{
   191  		LogLevel: logLevel,
   192  		Payload:  string(b.B),
   193  	}
   194  
   195  	textCh <- event
   196  }
   197  
   198  func formatJson(logLevel LogLevel, p []byte) {
   199  	event := Event{
   200  		LogLevel: logLevel,
   201  		Payload:  string(p),
   202  	}
   203  
   204  	jsonCh <- event
   205  }
   206  
   207  //go:linkname parseFormatterArgs github.com/phuslu/log.parseFormatterArgs
   208  func parseFormatterArgs(_ []byte, _ *logger.FormatterArgs)