github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/common/log/logger.go (about) 1 package log 2 3 import ( 4 "io" 5 "log" 6 "os" 7 "time" 8 9 "github.com/v2fly/v2ray-core/v5/common/platform" 10 "github.com/v2fly/v2ray-core/v5/common/signal/done" 11 "github.com/v2fly/v2ray-core/v5/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 // NewLogger returns a generic log handler that can handle all type of messages. 31 func NewLogger(logWriterCreator WriterCreator) Handler { 32 return &generalLogger{ 33 creator: logWriterCreator, 34 buffer: make(chan Message, 16), 35 access: semaphore.New(1), 36 done: done.New(), 37 } 38 } 39 40 func (l *generalLogger) run() { 41 defer l.access.Signal() 42 43 dataWritten := false 44 ticker := time.NewTicker(time.Minute) 45 defer ticker.Stop() 46 47 logger := l.creator() 48 if logger == nil { 49 return 50 } 51 defer logger.Close() 52 53 for { 54 select { 55 case <-l.done.Wait(): 56 return 57 case msg := <-l.buffer: 58 logger.Write(msg.String() + platform.LineSeparator()) 59 dataWritten = true 60 case <-ticker.C: 61 if !dataWritten { 62 return 63 } 64 dataWritten = false 65 } 66 } 67 } 68 69 func (l *generalLogger) Handle(msg Message) { 70 select { 71 case l.buffer <- msg: 72 default: 73 } 74 75 select { 76 case <-l.access.Wait(): 77 go l.run() 78 default: 79 } 80 } 81 82 func (l *generalLogger) Close() error { 83 return l.done.Close() 84 } 85 86 type consoleLogWriter struct { 87 logger *log.Logger 88 } 89 90 func (w *consoleLogWriter) Write(s string) error { 91 w.logger.Print(s) 92 return nil 93 } 94 95 func (w *consoleLogWriter) Close() error { 96 return nil 97 } 98 99 type fileLogWriter struct { 100 file *os.File 101 logger *log.Logger 102 } 103 104 func (w *fileLogWriter) Write(s string) error { 105 w.logger.Print(s) 106 return nil 107 } 108 109 func (w *fileLogWriter) Close() error { 110 return w.file.Close() 111 } 112 113 // CreateStdoutLogWriter returns a LogWriterCreator that creates LogWriter for stdout. 114 func CreateStdoutLogWriter() WriterCreator { 115 return func() Writer { 116 return &consoleLogWriter{ 117 logger: log.New(os.Stdout, "", log.Ldate|log.Ltime), 118 } 119 } 120 } 121 122 // CreateStderrLogWriter returns a LogWriterCreator that creates LogWriter for stderr. 123 func CreateStderrLogWriter() WriterCreator { 124 return func() Writer { 125 return &consoleLogWriter{ 126 logger: log.New(os.Stderr, "", log.Ldate|log.Ltime), 127 } 128 } 129 } 130 131 // CreateFileLogWriter returns a LogWriterCreator that creates LogWriter for the given file. 132 func CreateFileLogWriter(path string) (WriterCreator, error) { 133 file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o600) 134 if err != nil { 135 return nil, err 136 } 137 file.Close() 138 return func() Writer { 139 file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o600) 140 if err != nil { 141 return nil 142 } 143 return &fileLogWriter{ 144 file: file, 145 logger: log.New(file, "", log.Ldate|log.Ltime), 146 } 147 }, nil 148 } 149 150 func init() { 151 RegisterHandler(NewLogger(CreateStdoutLogWriter())) 152 }