File Coverage

blib/lib/Log/Log4perl/Appender/AmazonSES.pm
Criterion Covered Total %
statement 30 72 41.6
branch 0 16 0.0
condition 0 30 0.0
subroutine 10 14 71.4
pod 1 3 33.3
total 41 135 30.3


line stmt bran cond sub pod time code
1             package Log::Log4perl::Appender::AmazonSES;
2              
3 1     1   252293 use parent qw(Log::Log4perl::Appender);
  1         3  
  1         11  
4              
5 1     1   13241 use strict;
  1         2  
  1         31  
6 1     1   4 use warnings;
  1         2  
  1         78  
7              
8 1     1   6 use Carp;
  1         2  
  1         88  
9 1     1   11 use Data::Dumper;
  1         2  
  1         71  
10 1     1   7 use English qw(-no_match_vars);
  1         2  
  1         12  
11 1     1   601 use List::Util qw(any pairs);
  1         3  
  1         87  
12 1     1   3147 use Net::Domain 'hostfqdn';
  1         12010  
  1         124  
13 1     1   821 use Net::SMTP;
  1         117909  
  1         70  
14              
15 1     1   2829 use Readonly;
  1         4881  
  1         717  
16              
17             Readonly::Scalar our $TRUE => 1;
18             Readonly::Scalar our $FALSE => 0;
19              
20             Readonly::Scalar our $DEFAULT_HOST => 'email-smtp.us-east-1.amazonaws.com';
21             Readonly::Scalar our $DEFAULT_PORT => '465';
22              
23             our $VERSION = '1.0.1';
24              
25             ########################################################################
26             sub new {
27             ########################################################################
28 0     0 1   my ( $class, %options ) = @_;
29              
30 0           my $host = delete $options{Host};
31 0   0       $host //= delete $options{host};
32              
33 0   0       $host //= $DEFAULT_HOST;
34              
35 0   0       $options{Port} //= delete $options{port};
36 0   0       $options{Port} //= $DEFAULT_PORT;
37              
38 0   0       $options{Hello} //= delete $options{domain};
39 0   0       $options{Hello} //= hostfqdn;
40              
41 0           my $from = delete $options{from};
42 0           my $to = delete $options{to};
43 0           my $subject = delete $options{subject};
44              
45 0           foreach my $p ( pairs( from => $from, to => $to, subject => $subject ) ) {
46 0 0         croak $p->[0] . ' is a required parameter'
47             if !$p->[1];
48             }
49              
50 0           my $auth = eval { init_auth( delete $options{auth} ); };
  0            
51              
52 0           $options{SSL} = $TRUE;
53 0   0       $options{Debug} //= delete $options{debug};
54              
55 0           my $self = bless {
56             host => $host,
57             auth => $auth,
58             to => $to,
59             from => $from,
60             subject => $subject,
61             options => \%options
62             }, $class;
63              
64 0           return $self;
65             }
66              
67             ########################################################################
68             sub init_auth {
69             ########################################################################
70 0     0 0   my ($auth) = @_;
71              
72 0 0 0       if ( !$auth || !ref $auth ) {
73 0 0 0       if ( $ENV{SES_SMTP_USER} && $ENV{SES_SMTP_PASS} ) {
74             $auth = {
75             user => $ENV{SES_SMTP_USER},
76             password => $ENV{SES_SMTP_PASS},
77 0           };
78             }
79             }
80              
81             croak sprintf 'auth.user and auth.password are required parameters'
82 0 0 0       if !$auth->{user} || !$auth->{password};
83              
84 0           return $auth;
85             }
86              
87             ########################################################################
88             sub log { ## no critic
89             ########################################################################
90 0     0 0   my ( $self, %params ) = @_;
91              
92 0 0         my $smtp = Net::SMTP->new( $self->{host}, %{ $self->{options} }, )
  0            
93             or croak 'ERROR: unable to create a Net::SMTP instance';
94              
95 0 0   0     if ( any { $self->{options}->{Port} eq $_ } qw(25 587) ) {
  0            
96             $smtp->starttls()
97 0 0         or croak sprintf 'TLS negotiation with %s failed', $self->{host};
98             }
99              
100 0           my $auth = $self->{auth};
101              
102 0           $smtp->auth( $auth->{user}, $auth->{password} );
103              
104 0           $smtp->mail( $self->{from} );
105 0           $smtp->to( split /\s*,\s*/xsm, $self->{to} );
106              
107 0           $smtp->data;
108              
109 0           $smtp->datasend( sprintf "From: %s\n", $self->{from} );
110 0           $smtp->datasend( sprintf "To: %s\n", $self->{to} );
111 0           $smtp->datasend( sprintf "Subject: %s\n", $self->{subject} );
112 0           $smtp->datasend( sprintf "\n%s\n", $params{message} );
113              
114 0 0         $smtp->dataend
115             or carp 'ERROR: could not send message';
116              
117 0           $smtp->quit;
118             }
119              
120             1;
121              
122             __END__
123              
124             =pod
125              
126             =encoding utf8
127              
128             =head1 NAME
129              
130             Log::Log4perl::Appender::AmazonSES - Send via Amazon SES (SMTP over TLS)
131              
132             =head1 SYNOPSIS
133              
134             use Log::Log4perl::Appender::AmazonSES;
135              
136             my $app = Log::Log4perl::Appender::AmazonSES->new(
137             Host => 'email-smtp.us-east-1.amazonaws.com',
138             Port => '465'
139             Hello => 'localhost.localdomain',
140             Timeout => 2,
141             Debug => 0,
142             from => 'me@example.com',
143             to => 'you@example.com',
144             subject => 'Alert: there has been an error',
145             );
146              
147             $app->log(message => "A message via Amazon SES email");
148              
149             =head1 DESCRIPTION
150              
151             This appender uses the L<Net::SMTP> module to send mail via Amazon
152             SES. Essentially a flavor of L<Log::Log4perl::Appender::Net::SMTP> with
153             some intelligent options and restrictions.
154              
155             This module was created to provide a straightforward, well-documented
156             method for sending Log4perl alerts via Amazon SES. While other email
157             appenders exist, getting them to work with modern, authenticated SMTP
158             services can be challenging due to outdated dependencies or sparse
159             documentation. This appender aims to "just work" by using Net::SMTP
160             directly with the necessary options for SES.
161              
162             =head1 OPTIONS
163              
164             =over 2
165              
166             =item B<from> (required)
167              
168             The email address of the sender.
169              
170             =item B<to> (required)
171              
172             The email address of the recipient. You can put several addresses separated
173             by a comma.
174              
175             =item B<subject> (required)
176              
177             The subject of the email.
178              
179             =item B<Other Net::SMTP options>
180              
181             =over 4
182              
183             =item Hello
184              
185             Defaults to your fully qualified host's name. You can also use C<domain>.
186              
187             =item Port
188              
189             Default port for connection to the SMTP mail host. Amazon supports 25,
190             465, 587, 2587. The connection will be upgrade to SSL for non-SSL
191             ports.
192              
193             Default: 465
194              
195             =item Debug
196              
197             Outputs debug information from Net:::SMTP
198              
199             Default: false
200              
201             =back
202              
203             =back
204              
205             =head1 EXAMPLE LOG4PERL CONFIGURATION
206              
207             =head2 Use Environment Variables (Best Practice)
208              
209             log4perl.rootLogger = INFO, Mailer
210             log4perl.appender.Mailer = Log::Log4perl::Appender::AmazonSES
211             log4perl.appender.Mailer.from = ...
212             log4perl.appender.Mailer.to = ...
213             log4perl.appender.Mailer.subject = ...
214             log4perl.appender.Mailer.layout = Log::Log4perl::Layout::PatternLayout
215             log4perl.appender.Mailer.layout.ConversionPattern = %d - %p > %m%n
216              
217             =head2 Specify Credentials
218              
219             log4perl.rootLogger = INFO, Mailer
220             log4perl.appender.Mailer = Log::Log4perl::Appender::AmazonSES
221             log4perl.appender.Mailer.from = ...
222             log4perl.appender.Mailer.to = ...
223             log4perl.appender.Mailer.subject = ...
224             log4perl.appender.Mailer.auth.user = <YOUR AMAZON SES USER>
225             log4perl.appender.Mailer.auth.password = <YOUR AMAZON SES PASSWORD>
226             log4perl.appender.Mailer.layout = Log::Log4perl::Layout::PatternLayout
227             log4perl.appender.Mailer.layout.ConversionPattern = %d - %p > %m%n
228              
229             =head1 AUTHENTICATION
230              
231             You must either supply your authentication parameters in the
232             configuration of set SES_SMTP_USER and SES_SMTP_PASS environment
233             variables.
234              
235             =head1 AUTHOR
236              
237             Rob Lauer - <bigfoot@cpan.org>
238              
239             =head1 LICENSE
240              
241             This library is free software; you can redistribute it and/or modify
242             it under the same terms as Perl itself.
243              
244             =head1 SEE ALSO
245              
246             L<Log::Log4perl>, L<Net::SMTP>