1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2024-06-10 18:00:43 +00:00

Refine log settings

This commit is contained in:
v2ray 2015-12-05 21:10:14 +01:00
parent 32c3565681
commit db7d48e48f
10 changed files with 229 additions and 111 deletions

View File

@ -1,12 +1,5 @@
package log
import (
"log"
"os"
"github.com/v2ray/v2ray-core/common/platform"
)
// AccessStatus is the status of an access request from clients.
type AccessStatus string
@ -15,16 +8,9 @@ const (
AccessRejected = AccessStatus("rejected")
)
type accessLogger interface {
Log(from, to string, status AccessStatus, reason string)
}
type noOpAccessLogger struct {
}
func (this *noOpAccessLogger) Log(from, to string, status AccessStatus, reason string) {
// Swallow
}
var (
accessLoggerInstance logWriter = &noOpLogWriter{}
)
type accessLog struct {
From string
@ -33,60 +19,27 @@ type accessLog struct {
Reason string
}
type fileAccessLogger struct {
queue chan *accessLog
logger *log.Logger
file *os.File
func (this *accessLog) String() string {
return this.From + " " + string(this.Status) + " " + this.To + " " + this.Reason
}
func (this *fileAccessLogger) close() {
this.file.Close()
}
func (logger *fileAccessLogger) Log(from, to string, status AccessStatus, reason string) {
select {
case logger.queue <- &accessLog{
From: from,
To: to,
Status: status,
Reason: reason,
}:
default:
// We don't expect this to happen, but don't want to block main thread as well.
}
}
func (this *fileAccessLogger) Run() {
for entry := range this.queue {
this.logger.Println(entry.From + " " + string(entry.Status) + " " + entry.To + " " + entry.Reason)
}
}
func newFileAccessLogger(path string) accessLogger {
file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
log.Printf("Unable to create or open file (%s): %v%s", path, err, platform.LineSeparator())
return nil
}
return &fileAccessLogger{
queue: make(chan *accessLog, 16),
logger: log.New(file, "", log.Ldate|log.Ltime),
file: file,
}
}
var accessLoggerInstance accessLogger = &noOpAccessLogger{}
// InitAccessLogger initializes the access logger to write into the give file.
func InitAccessLogger(file string) {
logger := newFileAccessLogger(file)
if logger != nil {
go logger.(*fileAccessLogger).Run()
accessLoggerInstance = logger
func InitAccessLogger(file string) error {
logger, err := newFileLogWriter(file)
if err != nil {
Error("Failed to create access logger on file (%s): %v", file, err)
return err
}
accessLoggerInstance = logger
return nil
}
// Access writes an access log.
func Access(from, to string, status AccessStatus, reason string) {
accessLoggerInstance.Log(from, to, status, reason)
accessLoggerInstance.Log(&accessLog{
From: from,
To: to,
Status: status,
Reason: reason,
})
}

View File

@ -3,10 +3,10 @@ package log
import (
"io/ioutil"
"os"
"strings"
"testing"
"time"
"github.com/v2ray/v2ray-core/common/serial"
v2testing "github.com/v2ray/v2ray-core/testing"
"github.com/v2ray/v2ray-core/testing/assert"
)
@ -22,14 +22,15 @@ func TestAccessLog(t *testing.T) {
Access("test_from", "test_to", AccessAccepted, "test_reason")
<-time.After(2 * time.Second)
accessLoggerInstance.(*fileAccessLogger).close()
accessLoggerInstance = &noOpAccessLogger{}
accessLoggerInstance.(*fileLogWriter).close()
accessLoggerInstance = &noOpLogWriter{}
content, err := ioutil.ReadFile(filename)
assert.Error(err).IsNil()
assert.Bool(strings.Contains(string(content), "test_from")).IsTrue()
assert.Bool(strings.Contains(string(content), "test_to")).IsTrue()
assert.Bool(strings.Contains(string(content), "test_reason")).IsTrue()
assert.Bool(strings.Contains(string(content), "accepted")).IsTrue()
contentStr := serial.StringLiteral(content)
assert.String(contentStr).Contains(serial.StringLiteral("test_from"))
assert.String(contentStr).Contains(serial.StringLiteral("test_to"))
assert.String(contentStr).Contains(serial.StringLiteral("test_reason"))
assert.String(contentStr).Contains(serial.StringLiteral("accepted"))
}

View File

@ -2,8 +2,6 @@ package log
import (
"fmt"
"log"
"os"
)
const (
@ -13,36 +11,25 @@ const (
ErrorLevel = LogLevel(3)
)
type logger interface {
WriteLog(prefix, format string, v ...interface{})
type errorLog struct {
prefix string
format string
values []interface{}
}
type noOpLogger struct {
}
func (this *noOpLogger) WriteLog(prefix, format string, v ...interface{}) {
// Swallow
}
type streamLogger struct {
logger *log.Logger
}
func (this *streamLogger) WriteLog(prefix, format string, v ...interface{}) {
func (this *errorLog) String() string {
var data string
if v == nil || len(v) == 0 {
data = format
if len(this.values) == 0 {
data = this.format
} else {
data = fmt.Sprintf(format, v...)
data = fmt.Sprintf(this.format, this.values...)
}
this.logger.Println(prefix + data)
return this.prefix + data
}
var (
noOpLoggerInstance logger = &noOpLogger{}
streamLoggerInstance logger = &streamLogger{
logger: log.New(os.Stdout, "", log.Ldate|log.Ltime),
}
noOpLoggerInstance logWriter = &noOpLogWriter{}
streamLoggerInstance logWriter = newStdOutLogWriter()
debugLogger = noOpLoggerInstance
infoLogger = noOpLoggerInstance
@ -74,22 +61,48 @@ func SetLogLevel(level LogLevel) {
}
}
func InitErrorLogger(file string) error {
logger, err := newFileLogWriter(file)
if err != nil {
Error("Failed to create error logger on file (%s): %v", file, err)
return err
}
streamLoggerInstance = logger
return nil
}
// Debug outputs a debug log with given format and optional arguments.
func Debug(format string, v ...interface{}) {
debugLogger.WriteLog("[Debug]", format, v...)
debugLogger.Log(&errorLog{
prefix: "[Debug]",
format: format,
values: v,
})
}
// Info outputs an info log with given format and optional arguments.
func Info(format string, v ...interface{}) {
infoLogger.WriteLog("[Info]", format, v...)
infoLogger.Log(&errorLog{
prefix: "[Info]",
format: format,
values: v,
})
}
// Warning outputs a warning log with given format and optional arguments.
func Warning(format string, v ...interface{}) {
warningLogger.WriteLog("[Warning]", format, v...)
warningLogger.Log(&errorLog{
prefix: "[Warning]",
format: format,
values: v,
})
}
// Error outputs an error log with given format and optional arguments.
func Error(format string, v ...interface{}) {
errorLogger.WriteLog("[Error]", format, v...)
errorLogger.Log(&errorLog{
prefix: "[Error]",
format: format,
values: v,
})
}

View File

@ -25,13 +25,14 @@ func TestStreamLogger(t *testing.T) {
v2testing.Current(t)
buffer := bytes.NewBuffer(make([]byte, 0, 1024))
logger := &streamLogger{
infoLogger = &stdOutLogWriter{
logger: log.New(buffer, "", 0),
}
logger.WriteLog("TestPrefix: ", "Test %s Format", "Stream Logger")
assert.Bytes(buffer.Bytes()).Equals([]byte("TestPrefix: Test Stream Logger Format\n"))
Info("Test %s Format", "Stream Logger")
assert.Bytes(buffer.Bytes()).Equals([]byte("[Info]Test Stream Logger Format\n"))
buffer.Reset()
logger.WriteLog("TestPrefix: ", "Test No Format")
assert.Bytes(buffer.Bytes()).Equals([]byte("TestPrefix: Test No Format\n"))
errorLogger = infoLogger
Error("Test No Format")
assert.Bytes(buffer.Bytes()).Equals([]byte("[Error]Test No Format\n"))
}

77
common/log/log_writer.go Normal file
View File

@ -0,0 +1,77 @@
package log
import (
"io"
"log"
"os"
"github.com/v2ray/v2ray-core/common/platform"
"github.com/v2ray/v2ray-core/common/serial"
)
func createLogger(writer io.Writer) *log.Logger {
return log.New(writer, "", log.Ldate|log.Ltime)
}
type logWriter interface {
Log(serial.String)
}
type noOpLogWriter struct {
}
func (this *noOpLogWriter) Log(serial.String) {
// Swallow
}
type stdOutLogWriter struct {
logger *log.Logger
}
func newStdOutLogWriter() logWriter {
return &stdOutLogWriter{
logger: createLogger(os.Stdout),
}
}
func (this *stdOutLogWriter) Log(log serial.String) {
this.logger.Print(log.String() + platform.LineSeparator())
}
type fileLogWriter struct {
queue chan serial.String
logger *log.Logger
file *os.File
}
func (this *fileLogWriter) Log(log serial.String) {
select {
case this.queue <- log:
default:
// We don't expect this to happen, but don't want to block main thread as well.
}
}
func (this *fileLogWriter) run() {
for entry := range this.queue {
this.logger.Print(entry.String() + platform.LineSeparator())
}
}
func (this *fileLogWriter) close() {
this.file.Close()
}
func newFileLogWriter(path string) (*fileLogWriter, error) {
file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return nil, err
}
logger := &fileLogWriter{
queue: make(chan serial.String, 16),
logger: log.New(file, "", log.Ldate|log.Ltime),
file: file,
}
go logger.run()
return logger, nil
}

View File

@ -2,6 +2,7 @@ package config
import (
routerconfig "github.com/v2ray/v2ray-core/app/router/config"
"github.com/v2ray/v2ray-core/common/log"
v2net "github.com/v2ray/v2ray-core/common/net"
)
@ -12,6 +13,8 @@ type ConnectionConfig interface {
type LogConfig interface {
AccessLog() string
ErrorLog() string
LogLevel() log.LogLevel
}
type InboundDetourConfig interface {

View File

@ -1,9 +1,35 @@
package json
import (
"strings"
"github.com/v2ray/v2ray-core/common/log"
)
type LogConfig struct {
AccessLogValue string `json:"access"`
ErrorLogValue string `json:"error"`
LogLevelValue string `json:"loglevel"`
}
func (config *LogConfig) AccessLog() string {
return config.AccessLogValue
func (this *LogConfig) AccessLog() string {
return this.AccessLogValue
}
func (this *LogConfig) ErrorLog() string {
return this.ErrorLogValue
}
func (this *LogConfig) LogLevel() log.LogLevel {
level := strings.ToLower(this.LogLevelValue)
switch level {
case "debug":
return log.DebugLevel
case "info":
return log.InfoLevel
case "error":
return log.ErrorLevel
default:
return log.WarningLevel
}
}

View File

@ -3,6 +3,7 @@ package mocks
import (
routerconfig "github.com/v2ray/v2ray-core/app/router/config"
routertestingconfig "github.com/v2ray/v2ray-core/app/router/config/testing"
"github.com/v2ray/v2ray-core/common/log"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/shell/point/config"
)
@ -22,6 +23,20 @@ func (config *ConnectionConfig) Settings() interface{} {
type LogConfig struct {
AccessLogValue string
ErrorLogValue string
LogLevelValue log.LogLevel
}
func (config *LogConfig) AccessLog() string {
return config.AccessLogValue
}
func (this *LogConfig) ErrorLog() string {
return this.ErrorLogValue
}
func (this *LogConfig) LogLevel() log.LogLevel {
return this.LogLevelValue
}
type PortRange struct {
@ -55,10 +70,6 @@ func (this *OutboundDetourConfig) Tag() string {
return this.TagValue
}
func (config *LogConfig) AccessLog() string {
return config.AccessLogValue
}
type Config struct {
PortValue v2net.Port
LogConfigValue *LogConfig

View File

@ -30,6 +30,25 @@ func NewPoint(pConfig config.PointConfig) (*Point, error) {
var vpoint = new(Point)
vpoint.port = pConfig.Port()
if pConfig.LogConfig() != nil {
logConfig := pConfig.LogConfig()
if len(logConfig.AccessLog()) > 0 {
err := log.InitAccessLogger(logConfig.AccessLog())
if err != nil {
return nil, err
}
}
if len(logConfig.ErrorLog()) > 0 {
err := log.InitErrorLogger(logConfig.ErrorLog())
if err != nil {
return nil, err
}
}
log.SetLogLevel(logConfig.LogLevel())
}
ichFactory := connhandler.GetInboundConnectionHandlerFactory(pConfig.InboundConfig().Protocol())
if ichFactory == nil {
log.Error("Unknown inbound connection handler factory %s", pConfig.InboundConfig().Protocol())

View File

@ -1,6 +1,8 @@
package assert
import (
"strings"
"github.com/v2ray/v2ray-core/common/serial"
)
@ -31,3 +33,15 @@ func (subject *StringSubject) Equals(expectation string) {
subject.Fail(subject.DisplayString(), "is equal to", serial.StringLiteral(expectation))
}
}
func (subject *StringSubject) Contains(substring serial.String) {
if !strings.Contains(subject.value.String(), substring.String()) {
subject.Fail(subject.DisplayString(), "contains", substring)
}
}
func (subject *StringSubject) NotContains(substring serial.String) {
if strings.Contains(subject.value.String(), substring.String()) {
subject.Fail(subject.DisplayString(), "doesn't contain", substring)
}
}