How can I log the number of documents / bytes I've processed?

Tags: loggingcounteriText 7

I want to log the number of documents I've processed using iText, as well as the number of bytes I've read or produced. Is there a logging mechanism that allows me to do this?

Question asked at an internal meeting at iText Group NV.

Please take a look at the CounterDemoSyso. In this example, we get an instance of the CounterFactory. Simultaneously, we create a new SystemOutCounter object, which is an implementation of the Counter interface that writes information to the console (System.out):

CounterFactory.getInstance().setCounter(new SystemOutCounter());

We can now use ordinary iText code without having to worry about counting bytes or documents; that single line activated the counting mechanism.

For instance, we can create a PDF:

private void createPdf(String src) throws FileNotFoundException {
    Document document = new Document(new PdfDocument(new PdfWriter(src)));
    document.add(new Paragraph("Hello World!"));
    document.close();
}

Or we can manipulate a PDF:

public void manipulatePdf(String src, String dest) throws IOException {
  1. CounterFactory.getInstance().setCounter(new SystemOutCounter());
    createPdf(src);
    PdfReader reader = new PdfReader(src);
    PdfDocument pdfDocument = new PdfDocument(reader, new PdfWriter(dest));
    Document document = new Document(pdfDocument).showTextAligned(new Paragraph("Stamped text"), 559, 806, TextAlignment.RIGHT);
  1. document.close();
  1. CounterFactory.getInstance().setCounter(new DefaultCounter());
  1. }

If we execute createPdf() and manipulatePdf(), the SystemOutCounter will automatically write the following information to the System.out:

[com.itextpdf.kernel.pdf.PdfDocument] 929 bytes written
[com.itextpdf.kernel.pdf.PdfDocument] 929 bytes read
[com.itextpdf.kernel.pdf.PdfDocument] 1 389 bytes written

In the first line, we see that PdfDocument wrote 929 bytes (this happened in the createPdf() method). In the second and third line, we see what happened in the manipulatePdf() method: PdfDocument has read 929 bytes and we added the text "Stamped text" resulting in 1389 bytes.

This is only a simple example that writes to the System.out. It's not difficult to write some code that stores this information (and more) in another place. Let's take a look at the CounterDemo example. In this example, we write our own implementation of the Counter interface so that information about the processed documents is written to a file.

We need to implement three methods:

  • getCounter(): this method will return an instance of your counter,

  • onDocumentRead(long size): this method will be triggered when a file is read,

  • onDocumentWritten(long size): this method will be triggered when a file is written.

We implemented these methods as follows:

  1. public class MyCounter implements Counter {
  2.  
  3. protected FileWriter writer;
  4. protected String yourClass;
  5. protected String iTextClass;
  6.  
  7. public MyCounter(Class<?> klass) throws IOException {
  8. this.yourClass = klass.getName();
  9. writer = new FileWriter(LOG_RESULT, false);
  10. }
  11.  
  12. private MyCounter(Class<?> klass, String yourClass, FileWriter writer)
  13. throws IOException {
  14. this.yourClass = yourClass;
  15. this.iTextClass = klass.getName();
  16. this.writer = writer;
  17. }
  18.  
  19. @Override
  20. public Counter getCounter(Class<?> klass) {
  21. try {
  22. return new MyCounter(klass, yourClass, writer);
  23. } catch (IOException e) {
  24. throw new RuntimeException(e);
  25. }
  26. }
  27.  
  28. @Override
  29. public void onDocumentRead(long size) {
  30. if (writer == null)
  31. throw new RuntimeException("No writer defined!");
  32. try {
  33. writer.write(String.format(
  34. "[%s:%s] %s: %s read\n", yourClass, iTextClass, new Date().toString(), size));
  35. writer.flush();
  36. } catch (IOException e) {
  37. throw new RuntimeException(e);
  38. }
  39. }
  40.  
  41. @Override
  42. public void onDocumentWritten(long size) {
  43. if (writer == null)
  44. throw new RuntimeException("No writer defined!");
  45. try {
  46. writer.write(String.format(
  47. "[%s:%s] %s: %s written\n", yourClass, iTextClass, new Date().toString(), size));
  48. writer.flush();
  49. } catch (IOException e) {
  50. throw new RuntimeException(e);
  51. }
  52. }
  53.  
  54. public void close() throws IOException {
  55. writer.close();
  56. }
  57. }

As you see, instead of writing to the System.out, we will write to that file using the writer object. The yourClass object will store the class name of your application that uses the Counter; the iTextClass object will store the iText class that reads, writes or manipulates the PDF document.

The private constructor will be called by iText internally through the getCounter() method. It initializes the iTextClass variable and passes the yourClass and writer variables as parameters.

When bytes are read, we will write information to the log file that looks like this:

String.format("[%s:%s] %s: %s read\n", yourClass, iTextClass, new Date().toString(), size)

When bytes are written, we will write information to the log file that looks like:

String.format("[%s:%s] %s: %s written\n", yourClass, iTextClass, new Date().toString(), size)

If we execute the same createPdf() and manipulatePdf() methods as before, we get the following result:

[qa.general_questions.how_can_i_log_the_number_of_documents.CounterDemo:com.itextpdf.kernel.pdf.PdfDocument] Thu Jul 21 15:09:47 MSK 2016: 929 written
[qa.general_questions.how_can_i_log_the_number_of_documents.CounterDemo:com.itextpdf.kernel.pdf.PdfDocument] Thu Jul 21 15:09:47 MSK 2016: 929 read
[qa.general_questions.how_can_i_log_the_number_of_documents.CounterDemo:com.itextpdf.kernel.pdf.PdfDocument] Thu Jul 21 15:09:47 MSK 2016: 1389 written

As we are writing to a file, we mustn't forget to close the FileWriter once we've finished reading and writing documents:

public void close() throws IOException {
        writer.close();
    }

This example is provided FYI. Feel free to change the behavior of the FileWriter.

Caveat: The CounterFactory is currently implemented a singleton that stores a single Counter instance. This means that you can only store a single Counter at a time for each JVM.

Click this link if you want to see how to answer this question in iText 5.