mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-20 05:39:07 +02:00
feat(ui): GUI debug logging follows daemon log level + debug bundle
When the daemon is set to debug/trace, the GUI now automatically writes a rotated gui-client.log in the user's config dir and the daemon's debug bundle collects it. The UI learns the level both at startup (daemon already in debug) and live, by piggybacking the existing SubscribeEvents stream: the daemon publishes a marked log-level-changed SystemEvent (and a per-subscription snapshot), which DaemonFeed routes to guilog.DebugLog instead of an OS toast. The UI registers its log path via a new RegisterUILog RPC so the root daemon, which can't resolve the user's config dir, knows where to find the file. Manual --log-file (any value) disables the daemon-driven file logging. Fix: client/ui SetLogLevel looked up proto.LogLevel_value with the lowercase logrus name, which never matched the uppercase enum keys and silently fell back to INFO — so trace/debug requests from the bundle flow had no effect.
This commit is contained in:
+454
-366
File diff suppressed because it is too large
Load Diff
@@ -85,6 +85,11 @@ service DaemonService {
|
||||
|
||||
rpc GetEvents(GetEventsRequest) returns (GetEventsResponse) {}
|
||||
|
||||
// RegisterUILog records the desktop UI's absolute log path so the daemon's
|
||||
// debug bundle can collect it (the daemon runs as root and can't resolve the
|
||||
// user's config dir).
|
||||
rpc RegisterUILog(RegisterUILogRequest) returns (RegisterUILogResponse) {}
|
||||
|
||||
rpc SwitchProfile(SwitchProfileRequest) returns (SwitchProfileResponse) {}
|
||||
|
||||
rpc SetConfig(SetConfigRequest) returns (SetConfigResponse) {}
|
||||
@@ -547,6 +552,13 @@ message SetLogLevelRequest {
|
||||
message SetLogLevelResponse {
|
||||
}
|
||||
|
||||
message RegisterUILogRequest {
|
||||
string path = 1;
|
||||
}
|
||||
|
||||
message RegisterUILogResponse {
|
||||
}
|
||||
|
||||
// State represents a daemon state entry
|
||||
message State {
|
||||
string name = 1;
|
||||
|
||||
@@ -68,6 +68,10 @@ type DaemonServiceClient interface {
|
||||
StopBundleCapture(ctx context.Context, in *StopBundleCaptureRequest, opts ...grpc.CallOption) (*StopBundleCaptureResponse, error)
|
||||
SubscribeEvents(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (DaemonService_SubscribeEventsClient, error)
|
||||
GetEvents(ctx context.Context, in *GetEventsRequest, opts ...grpc.CallOption) (*GetEventsResponse, error)
|
||||
// RegisterUILog records the desktop UI's absolute log path so the daemon's
|
||||
// debug bundle can collect it (the daemon runs as root and can't resolve the
|
||||
// user's config dir).
|
||||
RegisterUILog(ctx context.Context, in *RegisterUILogRequest, opts ...grpc.CallOption) (*RegisterUILogResponse, error)
|
||||
SwitchProfile(ctx context.Context, in *SwitchProfileRequest, opts ...grpc.CallOption) (*SwitchProfileResponse, error)
|
||||
SetConfig(ctx context.Context, in *SetConfigRequest, opts ...grpc.CallOption) (*SetConfigResponse, error)
|
||||
AddProfile(ctx context.Context, in *AddProfileRequest, opts ...grpc.CallOption) (*AddProfileResponse, error)
|
||||
@@ -404,6 +408,15 @@ func (c *daemonServiceClient) GetEvents(ctx context.Context, in *GetEventsReques
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *daemonServiceClient) RegisterUILog(ctx context.Context, in *RegisterUILogRequest, opts ...grpc.CallOption) (*RegisterUILogResponse, error) {
|
||||
out := new(RegisterUILogResponse)
|
||||
err := c.cc.Invoke(ctx, "/daemon.DaemonService/RegisterUILog", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *daemonServiceClient) SwitchProfile(ctx context.Context, in *SwitchProfileRequest, opts ...grpc.CallOption) (*SwitchProfileResponse, error) {
|
||||
out := new(SwitchProfileResponse)
|
||||
err := c.cc.Invoke(ctx, "/daemon.DaemonService/SwitchProfile", in, out, opts...)
|
||||
@@ -652,6 +665,10 @@ type DaemonServiceServer interface {
|
||||
StopBundleCapture(context.Context, *StopBundleCaptureRequest) (*StopBundleCaptureResponse, error)
|
||||
SubscribeEvents(*SubscribeRequest, DaemonService_SubscribeEventsServer) error
|
||||
GetEvents(context.Context, *GetEventsRequest) (*GetEventsResponse, error)
|
||||
// RegisterUILog records the desktop UI's absolute log path so the daemon's
|
||||
// debug bundle can collect it (the daemon runs as root and can't resolve the
|
||||
// user's config dir).
|
||||
RegisterUILog(context.Context, *RegisterUILogRequest) (*RegisterUILogResponse, error)
|
||||
SwitchProfile(context.Context, *SwitchProfileRequest) (*SwitchProfileResponse, error)
|
||||
SetConfig(context.Context, *SetConfigRequest) (*SetConfigResponse, error)
|
||||
AddProfile(context.Context, *AddProfileRequest) (*AddProfileResponse, error)
|
||||
@@ -772,6 +789,9 @@ func (UnimplementedDaemonServiceServer) SubscribeEvents(*SubscribeRequest, Daemo
|
||||
func (UnimplementedDaemonServiceServer) GetEvents(context.Context, *GetEventsRequest) (*GetEventsResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetEvents not implemented")
|
||||
}
|
||||
func (UnimplementedDaemonServiceServer) RegisterUILog(context.Context, *RegisterUILogRequest) (*RegisterUILogResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method RegisterUILog not implemented")
|
||||
}
|
||||
func (UnimplementedDaemonServiceServer) SwitchProfile(context.Context, *SwitchProfileRequest) (*SwitchProfileResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SwitchProfile not implemented")
|
||||
}
|
||||
@@ -1283,6 +1303,24 @@ func _DaemonService_GetEvents_Handler(srv interface{}, ctx context.Context, dec
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _DaemonService_RegisterUILog_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RegisterUILogRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(DaemonServiceServer).RegisterUILog(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/daemon.DaemonService/RegisterUILog",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(DaemonServiceServer).RegisterUILog(ctx, req.(*RegisterUILogRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _DaemonService_SwitchProfile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SwitchProfileRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@@ -1719,6 +1757,10 @@ var DaemonService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "GetEvents",
|
||||
Handler: _DaemonService_GetEvents_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "RegisterUILog",
|
||||
Handler: _DaemonService_RegisterUILog_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SwitchProfile",
|
||||
Handler: _DaemonService_SwitchProfile_Handler,
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package proto
|
||||
|
||||
// SystemEvent metadata markers. The daemon stamps these on internal control
|
||||
// events it publishes over SubscribeEvents (profile-list refresh, log-level
|
||||
// change); the desktop UI recognises them and acts on them instead of
|
||||
// surfacing them as user-facing notifications.
|
||||
//
|
||||
// These live in the proto package — the shared contract both the daemon
|
||||
// (client/server) and the UI (client/ui/services) already import — so producer
|
||||
// and consumer reference the same constant rather than duplicating literals.
|
||||
// This file is hand-written and not touched by protoc.
|
||||
const (
|
||||
// MetadataKindKey is the SystemEvent.metadata key carrying the event-kind
|
||||
// marker (one of the MetadataKind* values below).
|
||||
MetadataKindKey = "kind"
|
||||
|
||||
// MetadataKindProfileListChanged marks a CLI-driven profile add/remove that
|
||||
// should nudge the UI's profile views to refresh.
|
||||
MetadataKindProfileListChanged = "profile-list-changed"
|
||||
// MetadataKindLogLevelChanged marks a daemon log-level change (or the
|
||||
// per-subscription snapshot) that drives the GUI's file logging on/off.
|
||||
MetadataKindLogLevelChanged = "log-level-changed"
|
||||
|
||||
// MetadataProfileKey carries the profile name for
|
||||
// MetadataKindProfileListChanged.
|
||||
MetadataProfileKey = "profile"
|
||||
// MetadataLevelKey carries the lowercase logrus level name for
|
||||
// MetadataKindLogLevelChanged.
|
||||
MetadataLevelKey = "level"
|
||||
)
|
||||
Reference in New Issue
Block a user