From 30b5bffad4362dc7c5fa0226cc3f0bb748650fd5 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 6 Dec 2018 17:37:05 +0100 Subject: [PATCH] support custom log handler --- app/log/log.go | 32 ++++++++++---------------- app/log/log_creator.go | 51 +++++++++++++++++++++++++++++++++++++++++ app/log/log_test.go | 52 ++++++++++++++++++++++++++++++++++++++++++ mocks.go | 1 + testing/mocks/log.go | 44 +++++++++++++++++++++++++++++++++++ 5 files changed, 160 insertions(+), 20 deletions(-) create mode 100644 app/log/log_creator.go create mode 100644 app/log/log_test.go create mode 100644 testing/mocks/log.go diff --git a/app/log/log.go b/app/log/log.go index 93f3fa968..726e02c4c 100644 --- a/app/log/log.go +++ b/app/log/log.go @@ -31,32 +31,24 @@ func New(ctx context.Context, config *Config) (*Instance, error) { } func (g *Instance) initAccessLogger() error { - switch g.config.AccessLogType { - case LogType_File: - creator, err := log.CreateFileLogWriter(g.config.AccessLogPath) - if err != nil { - return err - } - g.accessLogger = log.NewLogger(creator) - case LogType_Console: - g.accessLogger = log.NewLogger(log.CreateStdoutLogWriter()) - default: + handler, err := createHandler(g.config.AccessLogType, HandlerCreatorOptions{ + Path: g.config.AccessLogPath, + }) + if err != nil { + return err } + g.accessLogger = handler return nil } func (g *Instance) initErrorLogger() error { - switch g.config.ErrorLogType { - case LogType_File: - creator, err := log.CreateFileLogWriter(g.config.ErrorLogPath) - if err != nil { - return err - } - g.errorLogger = log.NewLogger(creator) - case LogType_Console: - g.errorLogger = log.NewLogger(log.CreateStdoutLogWriter()) - default: + handler, err := createHandler(g.config.ErrorLogType, HandlerCreatorOptions{ + Path: g.config.ErrorLogPath, + }) + if err != nil { + return err } + g.errorLogger = handler return nil } diff --git a/app/log/log_creator.go b/app/log/log_creator.go new file mode 100644 index 000000000..ade60b533 --- /dev/null +++ b/app/log/log_creator.go @@ -0,0 +1,51 @@ +package log + +import ( + "v2ray.com/core/common" + "v2ray.com/core/common/log" +) + +type HandlerCreatorOptions struct { + Path string +} + +type HandlerCreator func(LogType, HandlerCreatorOptions) (log.Handler, error) + +var ( + handlerCreatorMap = make(map[LogType]HandlerCreator) +) + +func RegisterHandlerCreator(logType LogType, f HandlerCreator) error { + if f == nil { + return newError("nil HandlerCreator") + } + + handlerCreatorMap[logType] = f + return nil +} + +func createHandler(logType LogType, options HandlerCreatorOptions) (log.Handler, error) { + creator, found := handlerCreatorMap[logType] + if !found { + return nil, newError("unable to create log handler for ", logType) + } + return creator(logType, options) +} + +func init() { + common.Must(RegisterHandlerCreator(LogType_Console, func(lt LogType, options HandlerCreatorOptions) (log.Handler, error) { + return log.NewLogger(log.CreateStdoutLogWriter()), nil + })) + + common.Must(RegisterHandlerCreator(LogType_File, func(lt LogType, options HandlerCreatorOptions) (log.Handler, error) { + creator, err := log.CreateFileLogWriter(options.Path) + if err != nil { + return nil, err + } + return log.NewLogger(creator), nil + })) + + common.Must(RegisterHandlerCreator(LogType_None, func(lt LogType, options HandlerCreatorOptions) (log.Handler, error) { + return nil, nil + })) +} diff --git a/app/log/log_test.go b/app/log/log_test.go new file mode 100644 index 000000000..41bb7e832 --- /dev/null +++ b/app/log/log_test.go @@ -0,0 +1,52 @@ +package log_test + +import ( + "context" + "testing" + + "github.com/golang/mock/gomock" + "v2ray.com/core/app/log" + "v2ray.com/core/common" + clog "v2ray.com/core/common/log" + "v2ray.com/core/testing/mocks" +) + +func TestCustomLogHandler(t *testing.T) { + mockCtl := gomock.NewController(t) + defer mockCtl.Finish() + + var loggedValue []string + + mockHandler := mocks.NewLogHandler(mockCtl) + mockHandler.EXPECT().Handle(gomock.Any()).AnyTimes().DoAndReturn(func(msg clog.Message) { + loggedValue = append(loggedValue, msg.String()) + }) + + log.RegisterHandlerCreator(log.LogType_Console, func(lt log.LogType, options log.HandlerCreatorOptions) (clog.Handler, error) { + return mockHandler, nil + }) + + logger, err := log.New(context.Background(), &log.Config{ + ErrorLogLevel: clog.Severity_Debug, + ErrorLogType: log.LogType_Console, + AccessLogType: log.LogType_None, + }) + common.Must(err) + + common.Must(logger.Start()) + + clog.Record(&clog.GeneralMessage{ + Severity: clog.Severity_Debug, + Content: "test", + }) + + if len(loggedValue) < 2 { + t.Fatal("expected 2 log messages, but actually ", loggedValue) + } + + if loggedValue[1] != "[Debug] test" { + t.Fatal("expected '[Debug] test', but actually ", loggedValue[1]) + } + + common.Must(logger.Close()) +} diff --git a/mocks.go b/mocks.go index 8fff62637..c50c6f512 100644 --- a/mocks.go +++ b/mocks.go @@ -4,6 +4,7 @@ package core //go:generate go install github.com/golang/mock/mockgen //go:generate mockgen -package mocks -destination testing/mocks/io.go -mock_names Reader=Reader,Writer=Writer io Reader,Writer +//go:generate mockgen -package mocks -destination testing/mocks/log.go -mock_names Handler=LogHandler v2ray.com/core/common/log Handler //go:generate mockgen -package mocks -destination testing/mocks/mux.go -mock_names ClientWorkerFactory=MuxClientWorkerFactory v2ray.com/core/common/mux ClientWorkerFactory //go:generate mockgen -package mocks -destination testing/mocks/dns.go -mock_names Client=DNSClient v2ray.com/core/features/dns Client //go:generate mockgen -package mocks -destination testing/mocks/outbound.go -mock_names Manager=OutboundManager,HandlerSelector=OutboundHandlerSelector v2ray.com/core/features/outbound Manager,HandlerSelector diff --git a/testing/mocks/log.go b/testing/mocks/log.go new file mode 100644 index 000000000..f3d6ed1d5 --- /dev/null +++ b/testing/mocks/log.go @@ -0,0 +1,44 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: v2ray.com/core/common/log (interfaces: Handler) + +// Package mocks is a generated GoMock package. +package mocks + +import ( + gomock "github.com/golang/mock/gomock" + reflect "reflect" + log "v2ray.com/core/common/log" +) + +// LogHandler is a mock of Handler interface +type LogHandler struct { + ctrl *gomock.Controller + recorder *LogHandlerMockRecorder +} + +// LogHandlerMockRecorder is the mock recorder for LogHandler +type LogHandlerMockRecorder struct { + mock *LogHandler +} + +// NewLogHandler creates a new mock instance +func NewLogHandler(ctrl *gomock.Controller) *LogHandler { + mock := &LogHandler{ctrl: ctrl} + mock.recorder = &LogHandlerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *LogHandler) EXPECT() *LogHandlerMockRecorder { + return m.recorder +} + +// Handle mocks base method +func (m *LogHandler) Handle(arg0 log.Message) { + m.ctrl.Call(m, "Handle", arg0) +} + +// Handle indicates an expected call of Handle +func (mr *LogHandlerMockRecorder) Handle(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Handle", reflect.TypeOf((*LogHandler)(nil).Handle), arg0) +}