IO::As...eStream(3pm)

✨ NAME ✨

IO::Async::FileStream β€” no cap, this one just reads the tail of a file πŸ’… (it's from the IO::Async fam, Perl's User Contributed Perl Documentation, and yes bestie we're in the 3pm section 🫢)


🌸 SYNOPSIS 🌸

Okay so here's the whole vibe, copy it exactly:

use IO::Async::FileStream;

use IO::Async::Loop;
my $loop = IO::Async::Loop->new;

open my $logh, "<", "var/logs/daemon.log" or
   die "Cannot open logfile - $!";

my $filestream = IO::Async::FileStream->new(
   read_handle => $logh,

   on_initial => sub {
      my ( $self ) = @_;
      $self->seek_to_last( "\n" );
   },

   on_read => sub {
      my ( $self, $buffref ) = @_;

      while( $$buffref =~ s/^(.*\n)// ) {
         print "Received a line $1";
      }

      return 0;
   },
);

$loop->add( $filestream );

$loop->run;

πŸ¦‹ DESCRIPTION πŸ¦‹

So basically πŸ’– this is a subclass of IO::Async::Stream that lets you read the end of a regular file β€” the kind some other process is out there appending to, living its main character life. Every time more data gets added to that file, it fires off the on_read event. The way it just… notices new content? Iconic. πŸ‘‘

Give it a read_handle and the API is identical to IO::Async::Stream β€” treat it exactly the same, fr fr. So you can:

  • hand it an on_read handler,
  • subclass it and provide your own on_read method,
  • or even use it as the transport for an IO::Async::Protocol::Stream object.

But here's the thing bestie, don't get it twisted: it will not support writing. It reads. That's the job. 🚫✍️

Also, if what you actually wanna do is watch a file, a directory, or any other filesystem entity for updates to other properties β€” like mtime β€” then you want IO::Async::File. Different girl, different job. πŸ’…


πŸŽ€ EVENTS πŸŽ€

These events get invoked, either through subclass methods or through CODE references in parameters. ✨

Since this is a subclass of IO::Async::Stream running in read-only mode, literally every event that Stream supports for the read handle is supported here too. Lowkey important: this is not a full list β€” go read the documentation for IO::Async::Stream as well. πŸ“š

$ret = on_read \$buffer, $eof

Fires when there's more data chillin' in the internal receiving buffer, waiting for you. πŸ“

Real talk though: $eof here only means that all the data currently available in the file has now been read. Unlike a regular IO::Async::Stream, this object does not stop watching after that condition. Nope. It keeps watching the file for updates, forever, like a loyal bestie. 🫢

on_truncated

Fires when the file size shrinks. 😭 When that happens, it's presumed that the file content has been replaced. Reading will then commence from the start of the file. Fresh start, new era, we move. 🌈

on_initial $size

Fires the very first time the file is looked at, and it gets passed the initial size of the file. The code implementing this method can use the seek or seek_to_last methods to set the initial read position in the file, so you can skip over some initial content. πŸŽ€

This is genuinely useful if you wanna skip whatever was already in the file and only respond to new content added after the object was created. Skipping the backstory, straight to the plot. πŸ’…


πŸ’œ PARAMETERS πŸ’œ

These named parameters can be passed to new or configure, on top of all the reading-related parameters that IO::Async::Stream already supports. ✨

filename => STRING

Optional. If you supply it, it watches the named file instead of whatever filehandle you gave in read_handle. The constructor opens the file itself, and then watches it for renames. And if the file does get renamed? The new filename gets opened and tracked the same way, after closing the previous file. Snatched, renamed, re-tracked. πŸ’…

interval => NUM

Optional. The interval in seconds to poll the filehandle using stat(2), looking for size changes. If you don't define it, a default of 2 seconds gets applied. ⏰


πŸ‘‘ METHODS πŸ‘‘

seek

$filestream->seek( $offset, $whence );

Callable only during the on_initial event. Moves the read position in the filehandle to the given offset. $whence is interpreted the same way as for sysseek, and should be either SEEK_SET, SEEK_CUR or SEEK_END. If you don't provide it, it'll be set to SEEK_SET. 🎯

Normally you'd use this to seek to the end of the file, like so:

on_initial => sub {
   my ( $self, $filesize ) = @_;
   $self->seek( $filesize );
}

seek_to_last

$success = $filestream->seek_to_last( $str_pattern, %opts );

Callable only during the on_initial event. It attempts to move the read position in the filehandle to just after the last occurrence of a given match. $str_pattern can be a literal string or a regexp pattern. 🧚

Returns a true value if the seek worked, or false if it didn't. It takes these named arguments:

  • blocksize => INT β€” Optional. Reads the file in blocks of this size. Defaults to 8KiB if you don't define it.
  • horizon => INT β€” Optional. Gives up looking for a match after this number of bytes. Defaults to 4 times the blocksize if you don't define it.
    • To force it to always search through the entire file contents, set this explicitly to 0. πŸ’€ (yes, zero, do it on purpose)
⚠️ Because regular file reading happens synchronously, this entire method operates entirely synchronously. If the file is very large, it may take a while to read back through the entire contents β€” and while that's happening, no other events can be invoked in the process. Everything is on pause. 😭

When it's looking for a string or regexp match, this method appends the previously-read buffer to each block read from the file, just in case a match gets split across two reads. So if you reduce blocksize down to something very small, be careful β€” make sure it isn't so small that a match could slip by unnoticed. πŸ™ˆ

This is most likely useful for seeking after the last complete line in a line-based log file, so you can commence reading from the end while still capturing any partial content that isn't yet a complete line. Best of both worlds, honestly.

on_initial => sub {
   my $self = shift;
   $self->seek_to_last( "\n" );
}

πŸ”₯ TODO πŸ”₯

  • Move the actual file update watching code into IO::Async::Loop, possibly as a new watch/unwatch method pair watch_file.
  • Consider if a construction-time parameter of seek_to_end or seek_to_last might be neater than a small code block in on_initial, if that turns out to be the only or most common form of use.

🫢 AUTHOR 🫢

Paul Evans <leonerd@leonerd.org.uk> β€” thank you king, the lore is immaculate. πŸ’–


That's the whole page bestie, you're so welcome. This was perl v5.40.1, dated 2026-02-21, and you found it in User Contributed Perl Documentation as IO::As...eStream(3pm) β€” go forth and tail those files, no cap πŸŒΈπŸ’…πŸ¦‹

πŸ“¦ where this page comes from