The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# MAPLAT  (C) 2008-2011 Rene Schickbauer
# Developed under Artistic license
# for Magna Powertrain Ilz
package Maplat::Helpers::MailLogger;
use strict;
use warnings;

# This file does major layouting. So, allow magic numbers
## no critic (ValuesAndExpressions::ProhibitMagicNumbers)

use 5.010;

use base qw(Exporter);
#our @EXPORT= qw();

our $VERSION = 0.995;

use Maplat::Helpers::Strings qw(tabsToTable);
use Maplat::Helpers::DateStrings;
use Mail::Sendmail;
use MIME::QuotedPrint;
use MIME::Base64;
use PDF::Report;

sub new {
    my ($class, %args) = @_;
    my $self = bless {}, $class;
    $self->{debugdata} = 0;
    foreach my $key (qw[file subject server port sender reciever debugdata]) {
        $self->{$key} = $args{$key};
    }
    $self->{active} = 0;
    return $self;
}

sub start {
    my ($self) = shift;
    open($self->{fh}, ">", $self->{file}) or return 0;
    my @temp;
    $self->{loglines} = \@temp;
    $self->{active} = 1;
    $self->{errors} = 0;
    $self->{warnings} = 0;
    
    if($self->{debugdata}) {
        $self->warn("Logging DEBUG data, logfile may get HUGE!");
    }
    return;
}

sub finish {
    my ($self) = shift;
    return 0 if(!$self->{active});
    
    $self->{active} = 0;
    
    my $csvmessage = "Time;Warnlevel;Text\n";
    my $textmessage = "The operation has produced the following status messages:\n" .
            $self->{errors} . " error(s)\n" .
            $self->{warnings} . " warning(s)\n" .
            "\n" .
            "Following if a detailed log of actions taken:\n";
    
    my $boundary = "====" . time() . "====";        
    foreach my $line (@{$self->{loglines}}) {
        #$message .= "$line\n";
        $textmessage .= tabsToTable($line, (21, 9)) . "\n";
        my $tmpline = $line;
        $tmpline =~ s/\t/\;/go;
        $csvmessage .= "$tmpline\n";
    }
    $textmessage = encode_qp($textmessage);
    $csvmessage = encode_base64($csvmessage);
    my $pdfmessage = encode_base64($self->makePDF());
    
    my $message = "--$boundary\n" .
                "Content-Type: text/plain; charset=\"iso-8859-1\"\n" .
                "Content-Transfer-Encoding: quoted-printable\n" .
                "\n" .
                "$textmessage\n" .
                "\n" .
                "CSV/Excel version of the log:\n" .
                "--$boundary\n" .
                "Content-Type: text/csv; name=\"Status_Report.csv\"\n" .
                "Content-Transfer-Encoding: base64\n" .
                "Content-Disposition: attachment; filename=\"Status_Report.csv\"\n" .
                "\n" .
                "$csvmessage\n" .
                "--$boundary\n" .
                "Content-Type: text/plain; charset=\"iso-8859-1\"\n" .
                "Content-Transfer-Encoding: quoted-printable\n" .
                "\n" .
                "\n" .
                "PDF/Printable version of the log:\n" .
                "--$boundary\n" .
                "Content-Type: application/pdf; name=\"Status_Report.pdf\"\n" .
                "Content-Transfer-Encoding: base64\n" .
                "Content-Disposition: attachment; filename=\"Status_Report.pdf\"\n" .
                "\n" .
                "$pdfmessage\n" .
                "--$boundary--\n";

    my $subject = $self->{subject};
    if($self->{errors}) {
        $subject = "[ERROR] $subject";
    } elsif($self->{warnings}) {
        $subject = "[WARNING] $subject";
    }
    
    my %mail = (To              => $self->{reciever},
                From            => $self->{sender},
                Subject         => $subject,
                Message         => $message,
                Server          => $self->{server},
                Port            => $self->{port},
                'X-Mailer'      => "Maplat Mail-Logger",
                'content-type'  => "multipart/mixed; boundary=\"$boundary\"",
                
                
                );

    if(!sendmail(%mail)) {
        print {$self->{fh}} "Can't send status mail: " . $Mail::Sendmail::error . "\n"; ## no critic (Variables::ProhibitPackageVars)
    } else {
        print {$self->{fh}} "Status mail sent";
    }

    
    close($self->{fh});
    return 1;
    
}

sub logLine {
    my ($self, %args) = @_;
    return 0 if(!$self->{active});
    my $date = getISODate();
    my $logline = "$date\t" . uc($args{level}) . "\t" . $args{text};
    if(uc($args{level}) eq "ERROR") {
        $self->{errors}++;
    } elsif(uc($args{level}) eq "WARNING") {
        $self->{warnings}++;
    }
    print {$self->{fh}} "$logline\n";
    print tabsToTable($logline, (21, 9)) . "\n";
    push @{$self->{loglines}}, $logline;
    return 1;
}

sub debug {
    my ($self, $text) = @_;
    return 1 if(!$self->{debugdata});
    return $self->logLine(level    => 'DEBUG',
                        text     =>  $text);
}

sub info {
    my ($self, $text) = @_;
    return $self->logLine(level    => 'INFO',
                        text     =>  $text);
}

sub warning {
    my ($self, $text) = @_;
    return $self->logLine(level    => 'WARNING',
                        text     =>  $text);
}

sub error {
    my ($self, $text) = @_;
    return $self->logLine(level    => 'ERROR',
                        text     =>  $text);
}

sub makePDF {
    my $self = shift;
    
    my $pdf = PDF::Report->new(PageSize          => "A4", 
                                PageOrientation => "Portrait",
                                );
    
    my ($pagewidth, $pageheight) = $pdf->getPageDimensions();
    
    my $pagecount = 1;
    
    #$pdf->newpage();
    $pdf->newpage(1);
    
    my $z = $pageheight;
    
    foreach my $line (@{$self->{loglines}}) {
        if($z < 50) {
            $pagecount++;
            $pdf->newpage($pagecount);
            $pdf->openpage($pagecount);
            $z = $pageheight;
        }
        if($z == $pageheight) {
            $pdf->setFont('Arial');
            $pdf->setSize(20);
            $z -= 30;
            $pdf->addRawText("Maplat Status Report", 20, $z, "black");
            #$pdf->addImg("pdf_logo.bmp", $pagewidth-100, $z+15);
            $z -= 35;
            $pdf->setFont('Arial');
            $pdf->setSize(10);

        }
        my @parts = split/\t/, $line;
        my $wcol = "black";
        if($parts[1] eq "WARNING") {
            $wcol = "orange";
        } elsif($parts[1] eq "ERROR") {
            $wcol = "red";
        }
        $pdf->drawLine(18, $z, $pagewidth - 18, $z);
        $pdf->drawLine(18, $z, 18, $z-14);
        $pdf->drawLine(138, $z, 138, $z-14);
        $pdf->drawLine(198, $z, 198, $z-14);
        $pdf->drawLine($pagewidth - 18, $z, $pagewidth - 18, $z-14);
        $pdf->drawLine(18, $z-14, $pagewidth - 18, $z-14);
        $pdf->addRawText($parts[0], 20, $z - 12, $wcol);
        $pdf->addRawText($parts[1], 140, $z - 12, $wcol);
        $pdf->addRawText($parts[2], 200, $z - 12, $wcol);
        $z -= 14;
    }

    for(my $i = 1; $i <= $pagecount; $i ++) {
        $pdf->openpage($i);
        $pdf->addRawText("Page $i / $pagecount", 450, 30, "grey");
    }
    
    return $pdf->Finish();   

}

1;
__END__

=head1 NAME

Maplat::Helpers::MailLogger - Logger on steroids

=head1 SYNOPSIS

  use Maplat::Helpers::MailLogger;
  
  my $logger = new Maplat::Helpers::MailLogger(
                file        => "tempfilename",
                subject     => "Automated mail for yada yada",
                server      => "mail.example.com",
                port        => 25,
                sender      => 'mytool@example.com',
                reciever    => 'user@example.com',
                debugdata   => 1
  );

  while($something) {
    ...
    if($error) {
      $logger->error($errortext);
    }
    ...
    # something *might* be wrong
    $logger->warn("Bistromatic drive needs recalibration");
    ...
    # Log some information
    $logger->info("Fuel level: $remainfuel");
    # and some debig info
    $logger->debug("Module foo has version $bar");
    ...
  }
  $logger->finish; # Finish up log and send it.

=head1 DESCRIPTION

This Module provides an easy way log information and send it as multipart
mail with with a text log, CSV log attachment and a color-coded PDF version.

=head2 new

  my $logger = new Maplat::Helpers::MailLogger(
                file        => "tempfilename",
                subject     => "Automated mail for yada yada",
                server      => "mail.example.com",
                port        => 25,
                sender      => 'mytool@example.com',
                reciever    => 'user@example.com',
                debugdata   => 1
  );

Most options are self explaining (you might also see Mail::Sendmail for details).
$file is the base filename used for the logfiles.
$debugdata is a boolean and determines, if debug() lines are included in the mailed
report.

=head2 start

Start logging.

=head2 debug

Takes one argument, a string. Logs this string with the level DEBUG.

=head2 info

Takes one argument, a string. Logs this string with the level INFO.

=head2 warning

Takes one argument, a string. Logs this string with the level WARNING.

=head2 error

Takes one argument, a string. Logs this string with the level ERROR.

=head2 finish

Finished up the report and sends it. It is prudent to discard the $logger object after
a call to finish. Continuing to use the logger after a call to finish will not work and/or
may have some undesired side effects.

=head2 logLine

Internal helper function.

=head2 makePDF

Internal helper function.

=head1 AUTHOR

Rene Schickbauer, E<lt>rene.schickbauer@gmail.comE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2008-2011 by Rene Schickbauer

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.10.0 or,
at your option, any later version of Perl 5 you may have available.

=cut