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