From c48fa50ab1d145906ba6cc4c3f14761e9def5955 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 14 Feb 2018 17:35:09 +0100 Subject: [PATCH] logger service --- app/log/command/command.go | 50 ++++++++++ app/log/command/command_test.go | 31 ++++++ app/log/command/config.pb.go | 143 ++++++++++++++++++++++++++++ app/log/command/config.proto | 18 ++++ app/log/command/errors.generated.go | 5 + app/log/log.go | 57 +++++++---- common/interfaces.go | 6 ++ common/log/logger.go | 8 ++ v2ray.go | 12 +++ 9 files changed, 313 insertions(+), 17 deletions(-) create mode 100644 app/log/command/command.go create mode 100644 app/log/command/command_test.go create mode 100644 app/log/command/config.pb.go create mode 100644 app/log/command/config.proto create mode 100644 app/log/command/errors.generated.go diff --git a/app/log/command/command.go b/app/log/command/command.go new file mode 100644 index 00000000..84d3078c --- /dev/null +++ b/app/log/command/command.go @@ -0,0 +1,50 @@ +package command + +//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg command -path App,Log,Command + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" + + "v2ray.com/core" + "v2ray.com/core/app/log" + "v2ray.com/core/common" +) + +type LoggerServer struct { + V *core.Instance +} + +func (s *LoggerServer) RestartLogger(ctx context.Context, request *RestartLoggerRequest) (*RestartLoggerResponse, error) { + logger := s.V.GetFeature((*log.Instance)(nil)) + if logger == nil { + return nil, newError("unable to get logger instance") + } + if err := logger.Close(); err != nil { + return nil, newError("failed to close logger").Base(err) + } + if err := logger.Start(); err != nil { + return nil, newError("failed to start logger").Base(err) + } + return &RestartLoggerResponse{}, nil +} + +type service struct { + v *core.Instance +} + +func (s *service) Register(server *grpc.Server) { + RegisterLoggerServiceServer(server, &LoggerServer{ + V: s.v, + }) +} + +func init() { + common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) { + s := core.FromContext(ctx) + if s == nil { + return nil, newError("V is not in context.") + } + return &service{v: s}, nil + })) +} diff --git a/app/log/command/command_test.go b/app/log/command/command_test.go new file mode 100644 index 00000000..44ebb15f --- /dev/null +++ b/app/log/command/command_test.go @@ -0,0 +1,31 @@ +package command_test + +import ( + "context" + "testing" + + "v2ray.com/core" + "v2ray.com/core/app/log" + . "v2ray.com/core/app/log/command" + "v2ray.com/core/common/serial" + . "v2ray.com/ext/assert" +) + +func TestLoggerRestart(t *testing.T) { + assert := With(t) + + v, err := core.New(&core.Config{ + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&log.Config{}), + }, + }) + + assert(err, IsNil) + assert(v.Start(), IsNil) + + server := &LoggerServer{ + V: v, + } + _, err = server.RestartLogger(context.Background(), &RestartLoggerRequest{}) + assert(err, IsNil) +} diff --git a/app/log/command/config.pb.go b/app/log/command/config.pb.go new file mode 100644 index 00000000..c7f0f542 --- /dev/null +++ b/app/log/command/config.pb.go @@ -0,0 +1,143 @@ +package command + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type Config struct { +} + +func (m *Config) Reset() { *m = Config{} } +func (m *Config) String() string { return proto.CompactTextString(m) } +func (*Config) ProtoMessage() {} +func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +type RestartLoggerRequest struct { +} + +func (m *RestartLoggerRequest) Reset() { *m = RestartLoggerRequest{} } +func (m *RestartLoggerRequest) String() string { return proto.CompactTextString(m) } +func (*RestartLoggerRequest) ProtoMessage() {} +func (*RestartLoggerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +type RestartLoggerResponse struct { +} + +func (m *RestartLoggerResponse) Reset() { *m = RestartLoggerResponse{} } +func (m *RestartLoggerResponse) String() string { return proto.CompactTextString(m) } +func (*RestartLoggerResponse) ProtoMessage() {} +func (*RestartLoggerResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func init() { + proto.RegisterType((*Config)(nil), "v2ray.core.app.log.command.Config") + proto.RegisterType((*RestartLoggerRequest)(nil), "v2ray.core.app.log.command.RestartLoggerRequest") + proto.RegisterType((*RestartLoggerResponse)(nil), "v2ray.core.app.log.command.RestartLoggerResponse") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for LoggerService service + +type LoggerServiceClient interface { + RestartLogger(ctx context.Context, in *RestartLoggerRequest, opts ...grpc.CallOption) (*RestartLoggerResponse, error) +} + +type loggerServiceClient struct { + cc *grpc.ClientConn +} + +func NewLoggerServiceClient(cc *grpc.ClientConn) LoggerServiceClient { + return &loggerServiceClient{cc} +} + +func (c *loggerServiceClient) RestartLogger(ctx context.Context, in *RestartLoggerRequest, opts ...grpc.CallOption) (*RestartLoggerResponse, error) { + out := new(RestartLoggerResponse) + err := grpc.Invoke(ctx, "/v2ray.core.app.log.command.LoggerService/RestartLogger", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for LoggerService service + +type LoggerServiceServer interface { + RestartLogger(context.Context, *RestartLoggerRequest) (*RestartLoggerResponse, error) +} + +func RegisterLoggerServiceServer(s *grpc.Server, srv LoggerServiceServer) { + s.RegisterService(&_LoggerService_serviceDesc, srv) +} + +func _LoggerService_RestartLogger_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RestartLoggerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LoggerServiceServer).RestartLogger(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v2ray.core.app.log.command.LoggerService/RestartLogger", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LoggerServiceServer).RestartLogger(ctx, req.(*RestartLoggerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _LoggerService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "v2ray.core.app.log.command.LoggerService", + HandlerType: (*LoggerServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "RestartLogger", + Handler: _LoggerService_RestartLogger_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "v2ray.com/core/app/log/command/config.proto", +} + +func init() { proto.RegisterFile("v2ray.com/core/app/log/command/config.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 210 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x2e, 0x33, 0x2a, 0x4a, + 0xac, 0xd4, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0x4a, 0xd5, 0x4f, 0x2c, 0x28, 0xd0, 0xcf, + 0xc9, 0x4f, 0xd7, 0x4f, 0xce, 0xcf, 0xcd, 0x4d, 0xcc, 0x4b, 0xd1, 0x4f, 0xce, 0xcf, 0x4b, 0xcb, + 0x4c, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x82, 0x29, 0x2e, 0x4a, 0xd5, 0x4b, 0x2c, + 0x28, 0xd0, 0xcb, 0xc9, 0x4f, 0xd7, 0x83, 0x2a, 0x54, 0xe2, 0xe0, 0x62, 0x73, 0x06, 0xab, 0x55, + 0x12, 0xe3, 0x12, 0x09, 0x4a, 0x2d, 0x2e, 0x49, 0x2c, 0x2a, 0xf1, 0xc9, 0x4f, 0x4f, 0x4f, 0x2d, + 0x0a, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x51, 0x12, 0xe7, 0x12, 0x45, 0x13, 0x2f, 0x2e, 0xc8, + 0xcf, 0x2b, 0x4e, 0x35, 0x6a, 0x67, 0xe4, 0xe2, 0x85, 0x08, 0x05, 0xa7, 0x16, 0x95, 0x65, 0x26, + 0xa7, 0x0a, 0x95, 0x71, 0xf1, 0xa2, 0x28, 0x15, 0x32, 0xd0, 0xc3, 0x6d, 0xb5, 0x1e, 0x36, 0xdb, + 0xa4, 0x0c, 0x49, 0xd0, 0x01, 0x71, 0x87, 0x12, 0x83, 0x93, 0x07, 0x97, 0x5c, 0x72, 0x7e, 0x2e, + 0x1e, 0x9d, 0x01, 0x8c, 0x51, 0xec, 0x50, 0xe6, 0x2a, 0x26, 0xa9, 0x30, 0xa3, 0xa0, 0xc4, 0x4a, + 0x3d, 0x67, 0x90, 0x3a, 0xc7, 0x82, 0x02, 0x3d, 0x9f, 0xfc, 0x74, 0x3d, 0x67, 0x88, 0x64, 0x12, + 0x1b, 0x38, 0xc4, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x37, 0xc7, 0xfc, 0xda, 0x60, 0x01, + 0x00, 0x00, +} diff --git a/app/log/command/config.proto b/app/log/command/config.proto new file mode 100644 index 00000000..39e2f33f --- /dev/null +++ b/app/log/command/config.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package v2ray.core.app.log.command; +option csharp_namespace = "V2Ray.Core.App.Log.Command"; +option go_package = "command"; +option java_package = "com.v2ray.core.app.log.command"; +option java_multiple_files = true; + +message Config { +} + +message RestartLoggerRequest {} + +message RestartLoggerResponse{} + +service LoggerService { + rpc RestartLogger(RestartLoggerRequest) returns (RestartLoggerResponse) {} +} \ No newline at end of file diff --git a/app/log/command/errors.generated.go b/app/log/command/errors.generated.go new file mode 100644 index 00000000..64481685 --- /dev/null +++ b/app/log/command/errors.generated.go @@ -0,0 +1,5 @@ +package command + +import "v2ray.com/core/common/errors" + +func newError(values ...interface{}) *errors.Error { return errors.New(values...).Path("App", "Log", "Command") } diff --git a/app/log/log.go b/app/log/log.go index c2dc5e57..30041f86 100644 --- a/app/log/log.go +++ b/app/log/log.go @@ -6,6 +6,7 @@ import ( "context" "sync" + "v2ray.com/core" "v2ray.com/core/common" "v2ray.com/core/common/log" ) @@ -26,13 +27,10 @@ func New(ctx context.Context, config *Config) (*Instance, error) { active: true, } - if err := g.initAccessLogger(); err != nil { - return nil, newError("failed to initialize access logger").Base(err).AtWarning() + v := core.FromContext(ctx) + if v != nil { + common.Must(v.RegisterFeature((*log.Handler)(nil), g)) } - if err := g.initErrorLogger(); err != nil { - return nil, newError("failed to initialize error logger").Base(err).AtWarning() - } - log.RegisterHandler(g) return g, nil } @@ -67,24 +65,40 @@ func (g *Instance) initErrorLogger() error { return nil } -// Start implements app.Application.Start(). -func (g *Instance) Start() error { - g.Lock() - defer g.Unlock() - g.active = true - return nil +func (*Instance) Type() interface{} { + return (*Instance)(nil) } -func (g *Instance) isActive() bool { - g.RLock() - defer g.RUnlock() +// Start implements app.Application.Start(). +func (g *Instance) Start() error { + newError("Logger starting").AtDebug().WriteToLog() - return g.active + g.Lock() + defer g.Unlock() + + if g.active { + return nil + } + + g.active = true + + if err := g.initAccessLogger(); err != nil { + return newError("failed to initialize access logger").Base(err).AtWarning() + } + if err := g.initErrorLogger(); err != nil { + return newError("failed to initialize error logger").Base(err).AtWarning() + } + log.RegisterHandler(g) + + return nil } // Handle implements log.Handler. func (g *Instance) Handle(msg log.Message) { - if !g.isActive() { + g.RLock() + defer g.RUnlock() + + if !g.active { return } @@ -104,11 +118,20 @@ func (g *Instance) Handle(msg log.Message) { // Close implement app.Application.Close(). func (g *Instance) Close() error { + newError("Logger closing").AtDebug().WriteToLog() + g.Lock() defer g.Unlock() + if !g.active { + return nil + } + g.active = false + common.Close(g.accessLogger) + common.Close(g.errorLogger) + return nil } diff --git a/common/interfaces.go b/common/interfaces.go index d5cc177e..f08235de 100644 --- a/common/interfaces.go +++ b/common/interfaces.go @@ -21,3 +21,9 @@ type Runnable interface { Closable } + +// HasType is the interface for objects that knows its type. +type HasType interface { + // Type returns the type of the object. + Type() interface{} +} diff --git a/common/log/logger.go b/common/log/logger.go index a07100bc..b3008720 100644 --- a/common/log/logger.go +++ b/common/log/logger.go @@ -23,6 +23,7 @@ type generalLogger struct { creator WriterCreator buffer chan Message access *signal.Semaphore + done *signal.Done } // NewLogger returns a generic log handler that can handle all type of messages. @@ -31,6 +32,7 @@ func NewLogger(logWriterCreator WriterCreator) Handler { creator: logWriterCreator, buffer: make(chan Message, 16), access: signal.NewSemaphore(1), + done: signal.NewDone(), } } @@ -49,6 +51,8 @@ func (l *generalLogger) run() { for { select { + case <-l.done.C(): + return case msg := <-l.buffer: logger.Write(msg.String() + platform.LineSeparator()) dataWritten = true @@ -74,6 +78,10 @@ func (l *generalLogger) Handle(msg Message) { } } +func (l *generalLogger) Close() error { + return l.done.Close() +} + type consoleLogWriter struct { logger *log.Logger } diff --git a/v2ray.go b/v2ray.go index a95a14b5..1d35af7c 100644 --- a/v2ray.go +++ b/v2ray.go @@ -163,6 +163,18 @@ func (s *Instance) RegisterFeature(feature interface{}, instance Feature) error return nil } +// GetFeature returns a feature that was registered in this Instance. Nil if not found. +func (s *Instance) GetFeature(featureType interface{}) Feature { + for _, f := range s.features { + if hasType, ok := f.(common.HasType); ok { + if hasType.Type() == featureType { + return f + } + } + } + return nil +} + // DNSClient returns the DNSClient used by this Instance. The returned DNSClient is always functional. func (s *Instance) DNSClient() DNSClient { return &(s.dnsClient)