package fs import ( "fmt" "os" "runtime" "runtime/debug" ) // Track is a wrapper around another file system which installs finalizers // for open files which call panic() when they are not closed when the garbage // collector releases them. This can be used to find resource leaks via open // files. type Track struct { FS } // Open wraps the Open method of the underlying file system. func (fs Track) Open(name string) (File, error) { f, err := fs.FS.Open(fixpath(name)) if err != nil { return nil, err } return newTrackFile(debug.Stack(), name, f), nil } // OpenFile wraps the OpenFile method of the underlying file system. func (fs Track) OpenFile(name string, flag int, perm os.FileMode) (File, error) { f, err := fs.FS.OpenFile(fixpath(name), flag, perm) if err != nil { return nil, err } return newTrackFile(debug.Stack(), name, f), nil } type trackFile struct { File } func newTrackFile(stack []byte, filename string, file File) *trackFile { f := &trackFile{file} runtime.SetFinalizer(f, func(f *trackFile) { fmt.Fprintf(os.Stderr, "file %s not closed\n\nStacktrack:\n%s\n", filename, stack) panic("file " + filename + " not closed") }) return f } func (f *trackFile) Close() error { runtime.SetFinalizer(f, nil) return f.File.Close() }