github.com/xtls/xray-core@v1.8.12-0.20240518155711-3168d27b0bdb/common/log/logger.go (about)

     1  package log
     2  
     3  import (
     4  	"io"
     5  	"log"
     6  	"os"
     7  	"time"
     8  
     9  	"github.com/xtls/xray-core/common/platform"
    10  	"github.com/xtls/xray-core/common/signal/done"
    11  	"github.com/xtls/xray-core/common/signal/semaphore"
    12  )
    13  
    14  // Writer is the interface for writing logs.
    15  type Writer interface {
    16  	Write(string) error
    17  	io.Closer
    18  }
    19  
    20  // WriterCreator is a function to create LogWriters.
    21  type WriterCreator func() Writer
    22  
    23  type generalLogger struct {
    24  	creator WriterCreator
    25  	buffer  chan Message
    26  	access  *semaphore.Instance
    27  	done    *done.Instance
    28  }
    29  
    30  type serverityLogger struct {
    31  	inner    *generalLogger
    32  	logLevel Severity
    33  }
    34  
    35  // NewLogger returns a generic log handler that can handle all type of messages.
    36  func NewLogger(logWriterCreator WriterCreator) Handler {
    37  	return &generalLogger{
    38  		creator: logWriterCreator,
    39  		buffer:  make(chan Message, 16),
    40  		access:  semaphore.New(1),
    41  		done:    done.New(),
    42  	}
    43  }
    44  
    45  func ReplaceWithSeverityLogger(serverity Severity) {
    46  	w := CreateStdoutLogWriter()
    47  	g := &generalLogger{
    48  		creator: w,
    49  		buffer:  make(chan Message, 16),
    50  		access:  semaphore.New(1),
    51  		done:    done.New(),
    52  	}
    53  	s := &serverityLogger{
    54  		inner:    g,
    55  		logLevel: serverity,
    56  	}
    57  	RegisterHandler(s)
    58  }
    59  
    60  func (l *serverityLogger) Handle(msg Message) {
    61  	switch msg := msg.(type) {
    62  	case *GeneralMessage:
    63  		if msg.Severity <= l.logLevel {
    64  			l.inner.Handle(msg)
    65  		}
    66  	default:
    67  		l.inner.Handle(msg)
    68  	}
    69  }
    70  
    71  func (l *generalLogger) run() {
    72  	defer l.access.Signal()
    73  
    74  	dataWritten := false
    75  	ticker := time.NewTicker(time.Minute)
    76  	defer ticker.Stop()
    77  
    78  	logger := l.creator()
    79  	if logger == nil {
    80  		return
    81  	}
    82  	defer logger.Close()
    83  
    84  	for {
    85  		select {
    86  		case <-l.done.Wait():
    87  			return
    88  		case msg := <-l.buffer:
    89  			logger.Write(msg.String() + platform.LineSeparator())
    90  			dataWritten = true
    91  		case <-ticker.C:
    92  			if !dataWritten {
    93  				return
    94  			}
    95  			dataWritten = false
    96  		}
    97  	}
    98  }
    99  
   100  func (l *generalLogger) Handle(msg Message) {
   101  
   102  	select {
   103  	case l.buffer <- msg:
   104  	default:
   105  	}
   106  
   107  	select {
   108  	case <-l.access.Wait():
   109  		go l.run()
   110  	default:
   111  	}
   112  }
   113  
   114  func (l *generalLogger) Close() error {
   115  	return l.done.Close()
   116  }
   117  
   118  type consoleLogWriter struct {
   119  	logger *log.Logger
   120  }
   121  
   122  func (w *consoleLogWriter) Write(s string) error {
   123  	w.logger.Print(s)
   124  	return nil
   125  }
   126  
   127  func (w *consoleLogWriter) Close() error {
   128  	return nil
   129  }
   130  
   131  type fileLogWriter struct {
   132  	file   *os.File
   133  	logger *log.Logger
   134  }
   135  
   136  func (w *fileLogWriter) Write(s string) error {
   137  	w.logger.Print(s)
   138  	return nil
   139  }
   140  
   141  func (w *fileLogWriter) Close() error {
   142  	return w.file.Close()
   143  }
   144  
   145  // CreateStdoutLogWriter returns a LogWriterCreator that creates LogWriter for stdout.
   146  func CreateStdoutLogWriter() WriterCreator {
   147  	return func() Writer {
   148  		return &consoleLogWriter{
   149  			logger: log.New(os.Stdout, "", log.Ldate|log.Ltime),
   150  		}
   151  	}
   152  }
   153  
   154  // CreateStderrLogWriter returns a LogWriterCreator that creates LogWriter for stderr.
   155  func CreateStderrLogWriter() WriterCreator {
   156  	return func() Writer {
   157  		return &consoleLogWriter{
   158  			logger: log.New(os.Stderr, "", log.Ldate|log.Ltime),
   159  		}
   160  	}
   161  }
   162  
   163  // CreateFileLogWriter returns a LogWriterCreator that creates LogWriter for the given file.
   164  func CreateFileLogWriter(path string) (WriterCreator, error) {
   165  	file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o600)
   166  	if err != nil {
   167  		return nil, err
   168  	}
   169  	file.Close()
   170  	return func() Writer {
   171  		file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o600)
   172  		if err != nil {
   173  			return nil
   174  		}
   175  		return &fileLogWriter{
   176  			file:   file,
   177  			logger: log.New(file, "", log.Ldate|log.Ltime),
   178  		}
   179  	}, nil
   180  }
   181  
   182  func init() {
   183  	RegisterHandler(NewLogger(CreateStdoutLogWriter()))
   184  }