Search code examples
java-8apache-tailer

commons-io Tailer is not calling handle() when file is created inside the JVM


I have a working Tailer implementation (commons-io Tailer) Here's my tailer:

public class SyslogConsumer implements TailerListener {
     @Override
     public void handle(String line) { System.out.println(line);}
    ...
}

public void process() {
    TailerListener listener = new SyslogConsumer();
    final Tailer tailer = Tailer.create( path.toFile(), listener );
    Runtime.getRuntime().addShutdownHook( new Thread( "LogProcessor shutdown hook" ) {
        public void run() {
            tailer.stop();
        }
    } );
}

and my test:

public class LogProcessorTest {

    private static Path templog;
    private static final String logEvent = "May 1 00:00:00 this is valid";

    @Before
    public void setup()
        throws IOException
    {
        templog = Files.createTempFile( "logprocessing", ".log" );
        templog.toFile().deleteOnExit();
        BufferedWriter bw = new BufferedWriter( new FileWriter( templog.toFile() ) );
        bw.write( logEvent );
        bw.newLine();
        bw.close();
    }

    @Test
    public void testProcessingValidEntriesProducesEvents()
        throws IOException
    {
        // utility method that pipes stdout to my bytearray
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        TestUtils.captureStdOut( bos );

        LogProcessor proc = new LogProcessor( templog.toString() );
        proc.process();

        String s = bos.toString( "UTF-8" );
        Assert.assertEquals( logEvent, s );
    }
}

At the point where the bos is introspected for its contents, it is empty, yet the log file contains 2 lines:

>May 1 00:00:01 this is valid
>  

If I point my test at a file that is created and written to using a bash script:

$ cat test/endlessLogGenerator.sh
#! /bin/bash
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
out="$DIR/data/logstream.out"
echo > $out
c=0
while :
do
    let c=$((c+1))
    echo $(date +"%b %d %T this is message $c") >> $out
    sleep 1
done

then it works perfectly. Yet when I create the file in the test runner, my listener is never called by the Tailer handler. I tried creating my file in a separate thread, also tried writing to an existing file not created by the test, also tried writing to the file in my test after giving it to Tailer to watch, etc. Nothing works. Nothing I seem to try with writing to a file in the test runner will cause the TailerListener to fire the handle() method. I am running this on Windows 7 with Java 8, inside Eclipse. Does anyone have any experience with unit testing a TailerListener's handle() method with a file written to by the JVM running the test?

Thanks.


Solution

  • Turns out my problem was caused by the Tailer swallowing an exception and silently closing the stream:

    Tailer's run() implementation...
    
    ....    
        } catch (Exception e) {
    
            listener.handle(e);
    
        } finally {
            IOUtils.closeQuietly(reader);
        }
    ....
    

    In my handle() implementation, I was parsing the date from the log and the DateTimeFormatter wasn't set up with the correct pattern, so it was throwing an unchecked runtime exception, which was caught by the Tailer. Strange choice of the library implementers to design this behaviour.

    I'm keeping the question, instead of just deleting it, because this caveat may come in handy to know.