Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#5873] feat(gvfs-fuse): add debug log for FuseApiHandle #5905

Open
wants to merge 30 commits into
base: main
Choose a base branch
from

Conversation

unknowntpo
Copy link
Contributor

@unknowntpo unknowntpo commented Dec 18, 2024

What changes were proposed in this pull request?

Implement FuseApiHandleDebug so we can log all input arguments and return values of FuseApiHandle.
I use tracing::debug, tracing::error with structure logging to format the message in key value pair format, so we don't need to control format manually.

e.g.

        debug!(req.unique, ?req, parent = ?parent_stat.name, ?name, "lookup started");

Fix: #5873

Does this PR introduce any user-facing change?

No.

How was this patch tested?

tested with fuse_test.rs, along with MemoryFileSystem.

Now, FuseApiHandleDebug will wrap FuseApiHandle and log all input arguments and returned values, if return value is Result, we use match to extract and log the status of result, note that error will be logged if occured.

To test this PR, run:

cd ./clients/filesystem-fuse
RUST_LOG=gvfs_fuse::fuse_api_handle=debug make test

Here's some example logging generated by test_fuse_system_with_auto in fuse_test.rs:

2025-01-10T22:53:45.511134Z DEBUG gvfs_fuse::fuse_api_handle: get_modified_file_stat file_id=1 filename="" size=None atime= mtime=
2025-01-10T22:53:45.511157Z DEBUG gvfs_fuse::fuse_api_handle_debug: rmdir started req.unique=2 req=Request { unique: 2, uid: 501, gid: 20, pid: 90853 } parent="" name="test_dir"
2025-01-10T22:53:45.511192Z DEBUG gvfs_fuse::fuse_api_handle_debug: rmdir succeeded req.unique=2
2025-01-10T22:53:45.511297Z DEBUG gvfs_fuse::fuse_api_handle: get_modified_file_stat file_id=1 filename="" size=None atime= mtime=
2025-01-10T22:53:45.511316Z DEBUG gvfs_fuse::fuse_api_handle_debug: lookup started req.unique=7 req=Request { unique: 7, uid: 501, gid: 20, pid: 90853 } parent="" name="._test_dir"
2025-01-10T22:53:45.511337Z ERROR gvfs_fuse::fuse_api_handle_debug: lookup failed req.unique=7 e=Errno(2)
2025-01-10T22:53:45.511433Z DEBUG gvfs_fuse::fuse_api_handle: get_modified_file_stat file_id=1 filename="" size=None atime= mtime=
2025-01-10T22:53:45.511450Z DEBUG gvfs_fuse::fuse_api_handle_debug: lookup started req.unique=6 req=Request { unique: 6, uid: 501, gid: 20, pid: 90853 } parent="" name="test_dir"
2025-01-10T22:53:45.511469Z ERROR gvfs_fuse::fuse_api_handle_debug: lookup failed req.unique=6 e=Errno(2)
2025-01-10T22:53:45.511716Z DEBUG gvfs_fuse::fuse_api_handle_debug: destroy started req.unique=0 req=Request { unique: 0, uid: 0, gid: 0, pid: 0 }

@unknowntpo unknowntpo force-pushed the feat-fuse-debug-log branch 2 times, most recently from 9ae9ea0 to 3574bce Compare December 19, 2024 22:48
@unknowntpo unknowntpo marked this pull request as ready for review December 19, 2024 22:52
Copy link
Contributor

@diqiu50 diqiu50 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finally, we need to verify the log format to see if it meets expectations. This needs to be validated in the #5886, and you can wait for it to be merged.

inner: FuseApiHandle::new(fs, context),
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These functions that are not part of the Filesystem trait do not need to add debug logs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new is needed, we can use it to create a FuseApiHandleDebug, and it will create a FuseApiHandle as inner file system.

e.g.

    let fs = DefaultRawFileSystem::new(gvfs);
    FuseApiHandleDebug::new(fs, fs_context)

}

impl<T: RawFileSystem> Filesystem for FuseApiHandleDebug<T> {
async fn init(&self, req: Request) -> fuse3::Result<ReplyInit> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to implement all interfaces of the Filesystem. If they are not implemented yet, an error log should be recorded.

async fn init(&self, req: Request) -> fuse3::Result<ReplyInit> {
debug!("init: req: {:?}", req);
let result = self.inner.init(req).await;
debug!("init result: {:?}", result);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an asynchronous multithreaded model. Logs from two print statements within a single function may be interleaved with logs from other interfaces. It is necessary to identify them in the logs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I use Request.unique as the unique identifier of the request.

2024-12-28T12:23:01.966929Z DEBUG gvfs_fuse::fuse_api_handle: lookup [id=6]: error: Errno(2)    
2024-12-28T12:23:01.967037Z DEBUG gvfs_fuse::fuse_api_handle: lookup [id=7]: req: Request { unique: 7, uid: 501, gid: 20, pid: 18890 }, parent: 1, name: "test_create"    
2024-12-28T12:23:01.967056Z DEBUG gvfs_fuse::fuse_api_handle: lookup [id=7]: reply: ReplyEntry { ttl: 1s, attr: FileAttr { ino: 10001, size: 0, blocks: 0, atime: Timestamp { sec: 1735388581, nsec: 967051000 }, mtime: Timestamp { sec: 1735388581, nsec: 967051000 }, ctime: Timestamp { sec: 1735388581, nsec: 967051000 }, crtime: Timestamp { sec: 1735388581, nsec: 967051000 }, kind: RegularFile, perm: 420, nlink: 1, uid: 501, gid: 20, rdev: 0, flags: 0, blksize: 4096 }, generation: 0 }    

async fn lookup(&self, req: Request, parent: Inode, name: &OsStr) -> fuse3::Result<ReplyEntry> {
debug!("lookup: req: {:?}, parent: {:?}, name: {:?}", req, parent, name);
let result = self.inner.lookup(req, parent, name).await;
debug!("lookup result: {:?}", result);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If an error is returned, it should be logged using error!.

atime: Option<Timestamp>,
mtime: Option<Timestamp>,
) -> Result<FileStat, Errno> {
debug!("get_modified_file_stat: file_id: {}, size: {:?}, atime: {:?}, mtime: {:?}", file_id, size, atime, mtime);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s best to convert file_id to a file path to improve readability, for example, "/a/b.txt(2342)". Other variables should follow a similar approach.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implemented at a7073a0.

I use a wrapper struct TimestampDebug which implement custom fmt::Display trait,
now, Timestamp will be displayed like:

mtime: Timestamp { sec: 1735386080, nsec: 26884000 }, ctime: Timestamp { sec: 1735386080, nsec: 26884000 }, crtime: Timestamp { sec: 1735386080, nsec: 26884000 }

Copy link
Contributor Author

@unknowntpo unknowntpo Jan 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I implemented some methods like reply_attr_to_desc_str, reply_entry_to_desc_str to manually control the format.

@diqiu50
Copy link
Contributor

diqiu50 commented Dec 20, 2024

Please refer to other PRs for the format of PR titles and descriptions.

@unknowntpo unknowntpo changed the title Feat fuse debug log [#5873] feat(gvfs-fuse): add debug log for FuseApiHandle Dec 20, 2024
@unknowntpo
Copy link
Contributor Author

unknowntpo commented Dec 26, 2024

Finally, we need to verify the log format to see if it meets expectations. This needs to be validated in the #5886, and you can wait for it to be merged.

Since #5886 is done, I'll continue on my work.

@unknowntpo
Copy link
Contributor Author

close and reopen to run workflow

@unknowntpo unknowntpo closed this Dec 29, 2024
@unknowntpo unknowntpo reopened this Dec 29, 2024
@xunliu
Copy link
Member

xunliu commented Dec 30, 2024

@diqiu50 Please help review this PR, Thanks.

"readdir [id={}]: req: {:?}, parent: {:?}, fh: {:?}, offset: {:?}",
req.unique, req, parent, fh, offset
);
let result = self.inner.readdir(req, parent, fh, offset).await;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to show dir entries

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

impl fmt::Display for TimestampDebug {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let ts = &self.0; // Access the inner `Timestamp`
write!(f, "{}.{:09}", ts.sec, ts.nsec) // Nanoseconds padded to 9 digits
Copy link
Contributor

@diqiu50 diqiu50 Dec 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The result of timestamp is not readable.
I think the TimestampDebug is not god for use. You can try to find a better solution.
At least you can use a function to convert it into a readable time format

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I implemented some methods like reply_attr_to_desc_str, reply_entry_to_desc_str to manually control the format.


pub(crate) struct FuseApiHandleDebug<T: RawFileSystem> {
inner: FuseApiHandle<T>,
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

separate this class as a new file

}
Err(e) => {
debug!("init [id={}]: error: {:?}", req.unique, e);
Err(e)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is using error! better?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, error! is better, fixed.

req.unique,
FileAttrDebug {
file_attr: &reply.attr
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Constructing an object is not as clear as using a function like to_desc_string()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I implemented some methods like reply_attr_to_desc_str, reply_entry_to_desc_str to manually control the format.

let result = self.inner.read(req, inode, fh, offset, size).await;
match result {
Ok(reply) => {
debug!("read [id={}]: reply: {:?}", req.unique, reply);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The result of is not clear enough.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.

Copy link
Contributor

@diqiu50 diqiu50 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, there is no issue. The readability of the logs needs to be improved and made more concise. Unimportant information can be ignored.

Implement a few methods for printing commonly used structures, without having to print them in the struct format

) -> fuse3::Result<ReplyDirectoryPlus<Self::DirEntryPlusStream<'a>>> {
debug!(
"readdirplus [id={}]: req: {:?}, parent: {:?}, fh: {:?}, offset: {:?}, lock_owner: {:?}",
req.unique, req, parent, fh, offset, lock_owner
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to print the filename as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.

@unknowntpo unknowntpo force-pushed the feat-fuse-debug-log branch from 1d9ff8a to d619db0 Compare January 3, 2025 02:39
@diqiu50
Copy link
Contributor

diqiu50 commented Jan 6, 2025

@unknowntpo All the code are merged to the main brach. you need to merge the pr to main

@unknowntpo
Copy link
Contributor Author

@diqiu50 Okay, (I'm still working this PR)

@unknowntpo unknowntpo force-pushed the feat-fuse-debug-log branch 2 times, most recently from d4627de to ec48523 Compare January 7, 2025 23:14
@unknowntpo unknowntpo changed the base branch from branch-gvfs-fuse-dev to main January 7, 2025 23:17
@unknowntpo
Copy link
Contributor Author

Note that I use tracing::debug, tracing::error with structure logging to format the message in key value pair format, so we don't need to control format manually.

e.g.

    debug!(req.unique, ?req, parent = ?parent_stat.name, ?name, "lookup started");

@unknowntpo
Copy link
Contributor Author

@diqiu50 Done, please take a look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Subtask] Add debug log for FUSE API handling.
3 participants