github.com/imannamdari/v2ray-core/v5@v5.0.5/common/log/logger.go (about)

     1  package log
     2  
     3  import (
     4  	"io"
     5  	"log"
     6  	"os"
     7  	"time"
     8  
     9  	"github.com/imannamdari/v2ray-core/v5/common/platform"
    10  	"github.com/imannamdari/v2ray-core/v5/common/signal/done"
    11  	"github.com/imannamdari/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  }