v2ray преди 10 години
родител
ревизия
db7d48e48f

+ 18 - 65
common/log/access.go

@@ -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 *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,
-	}
+func (this *accessLog) String() string {
+	return this.From + " " + string(this.Status) + " " + this.To + " " + this.Reason
 }
 
-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,
+	})
 }

+ 8 - 7
common/log/access_test.go

@@ -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"))
 }

+ 41 - 28
common/log/log.go

@@ -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,
+	})
 }

+ 6 - 5
common/log/log_test.go

@@ -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 - 0
common/log/log_writer.go

@@ -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
+}

+ 3 - 0
shell/point/config/config.go

@@ -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 {

+ 28 - 2
shell/point/config/json/log.go

@@ -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 (this *LogConfig) AccessLog() string {
+	return this.AccessLogValue
+}
+
+func (this *LogConfig) ErrorLog() string {
+	return this.ErrorLogValue
 }
 
-func (config *LogConfig) AccessLog() string {
-	return config.AccessLogValue
+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
+	}
 }

+ 15 - 4
shell/point/config/testing/mocks/config.go

@@ -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

+ 19 - 0
shell/point/point.go

@@ -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())

+ 14 - 0
testing/assert/stringsubject.go

@@ -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)
+	}
+}