File Coverage

blib/lib/Google/Ads/GoogleAds/Logging/GoogleAdsLogger.pm
Criterion Covered Total %
statement 83 94 88.3
branch 24 34 70.5
condition 18 21 85.7
subroutine 20 24 83.3
pod 11 11 100.0
total 156 184 84.7


line stmt bran cond sub pod time code
1             # Copyright 2019, Google LLC
2             #
3             # Licensed under the Apache License, Version 2.0 (the "License");
4             # you may not use this file except in compliance with the License.
5             # You may obtain a copy of the License at
6             #
7             # http://www.apache.org/licenses/LICENSE-2.0
8             #
9             # Unless required by applicable law or agreed to in writing, software
10             # distributed under the License is distributed on an "AS IS" BASIS,
11             # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12             # See the License for the specific language governing permissions and
13             # limitations under the License.
14             #
15             # The logger for outgoing and incoming REST messages as API calls.
16              
17              
18             use strict;
19 11     11   63 use warnings;
  11         19  
  11         268  
20 11     11   46 use version;
  11         17  
  11         234  
21 11     11   49  
  11         24  
  11         99  
22             # The following needs to be on one line because CPAN uses a particularly hacky
23             # eval() to determine module versions.
24             use Google::Ads::GoogleAds::Constants; our $VERSION = ${Google::Ads::GoogleAds::Constants::VERSION};
25 11     11   626 use Google::Ads::GoogleAds::Logging::SummaryStats;
  11         17  
  11         463  
26 11     11   3878 use Google::Ads::GoogleAds::Logging::DetailStats;
  11         29  
  11         321  
27 11     11   4405  
  11         28  
  11         341  
28             use File::HomeDir;
29 11     11   63 use File::Spec;
  11         18  
  11         496  
30 11     11   62 use Log::Log4perl qw(get_logger :levels);
  11         18  
  11         326  
31 11     11   8249 use JSON::XS;
  11         439736  
  11         52  
32 11     11   1311  
  11         24  
  11         10843  
33             # Module initialization.
34             # This is the log4perl configuration format.
35             my $logs_folder = File::Spec->catfile(File::HomeDir->my_home, "logs");
36             my $summary_log_file = File::Spec->catfile($logs_folder, "summary.log");
37             my $detail_log_file = File::Spec->catfile($logs_folder, "detail.log");
38             my $default_conf = <<TEXT;
39             log4perl.category.Google.Ads.GoogleAds.Summary = INFO, SummaryFile
40             log4perl.appender.SummaryFile = Log::Log4perl::Appender::File
41             log4perl.appender.SummaryFile.filename = ${summary_log_file}
42             log4perl.appender.SummaryFile.create_at_logtime = 1
43             log4perl.appender.SummaryFile.mkpath = 1
44             log4perl.appender.SummaryFile.layout = Log::Log4perl::Layout::PatternLayout
45             log4perl.appender.SummaryFile.layout.ConversionPattern = [%d{DATE} - %-5p] %m%n
46              
47             log4perl.category.Google.Ads.GoogleAds.Detail = INFO, DetailFile
48             log4perl.appender.DetailFile = Log::Log4perl::Appender::File
49             log4perl.appender.DetailFile.filename = ${detail_log_file}
50             log4perl.appender.DetailFile.create_at_logtime = 1
51             log4perl.appender.DetailFile.mkpath = 1
52             log4perl.appender.DetailFile.layout = Log::Log4perl::Layout::PatternLayout
53             log4perl.appender.DetailFile.layout.ConversionPattern = [%d{DATE} - %-5p] %m%n
54             TEXT
55              
56             # Static module-level variables.
57             my ($summary_logger, $detail_logger);
58              
59             # Initializes Log4Perl infrastructure.
60             # Only initialize once.
61             unless (Log::Log4perl->initialized()) {
62             my $log4perl_conf = shift;
63 13 100   13 1 1181 # Trying to read from the ~/log4perl.conf file if no .conf file is specified as a parameter.
64 6         33 $log4perl_conf =
65             File::Spec->catfile(File::HomeDir->my_home, "log4perl.conf")
66 6 100       44 unless defined $log4perl_conf;
67              
68             # Create the log files in rw-rw-rw- mode.
69             umask 0000;
70             if (-r $log4perl_conf) {
71 6         372 Log::Log4perl->init($log4perl_conf);
72 6 100       461 } else {
73 1         7 mkdir ${logs_folder} unless -d ${logs_folder};
74             Log::Log4perl->init(\$default_conf);
75 5 50       67 }
76 5         39 }
77              
78             # Log4Perl may be initialized by another package; check if the loggers are set up.
79             unless ($summary_logger and $detail_logger) {
80             $summary_logger = get_logger("Google::Ads::GoogleAds::Summary");
81 13 100 66     46636 $detail_logger = get_logger("Google::Ads::GoogleAds::Detail");
82 6         42 }
83 6         186 }
84              
85             # Enables the summary logging. Takes one boolean parameter, to enable the logging
86             # in debug (more verbose) mode when set to true.
87             initialize_logging();
88             if ($_[0]) {
89             $summary_logger->level($DEBUG);
90 0     0 1 0 } else {
91 0 0       0 $summary_logger->level($INFO);
92 0         0 }
93             }
94 0         0  
95             # Disables all summary logging.
96             $summary_logger->level($OFF);
97             }
98              
99             # Enables the traffic detail (request and responses) logging. Takes one boolean
100 0     0 1 0 # parameter, to enable the logging in debug (more verbose) mode when set to true.
101             initialize_logging();
102             if ($_[0]) {
103             $detail_logger->level($DEBUG);
104             } else {
105             $detail_logger->level($INFO);
106 0     0 1 0 }
107 0 0       0 }
108 0         0  
109             # Disables all traffic detail logging.
110 0         0 $detail_logger->level($OFF);
111             }
112              
113             # Enables all logging including summary logging and traffic detail logging.
114             initialize_logging();
115             if ($_[0]) {
116 0     0 1 0 $summary_logger->level($DEBUG);
117             $detail_logger->level($DEBUG);
118             } else {
119             $summary_logger->level($INFO);
120             $detail_logger->level($INFO);
121 2     2 1 7235 }
122 2 100       7 }
123 1         7  
124 1         922 # Disables all logging including summary logging and traffic detail logging.
125             $summary_logger->level($OFF);
126 1         4 $detail_logger->level($OFF);
127 1         818 }
128              
129             # Retrieves the summary logger used to log the one-line summary.
130             initialize_logging();
131             return $summary_logger;
132             }
133 2     2 1 1561  
134 2         1670 # Retrieves the detail logger used to log the traffic detail.
135             initialize_logging();
136             return $detail_logger;
137             }
138              
139 1     1 1 361 # Logs the one-line summary for each REST API request.
140 1         2 my ($http_request, $http_response) = @_;
141              
142             # Validate the response status and log level.
143             return
144             unless ($http_response->is_success and $summary_logger->is_info)
145 1     1 1 6 or (!$http_response->is_success and $summary_logger->is_warn);
146 1         2  
147             my $summary_stats = Google::Ads::GoogleAds::Logging::SummaryStats->new({
148             host => __parse_host($http_request),
149             customer_id => $http_request->uri =~ /customers\/\d+/
150             ? $http_request->uri =~ /customers\/(\d+)/
151 4     4 1 55616 : "",
152             # The service method name in which the logger is invoked.
153             method => (caller(3))[3],
154             request_id => $http_response->header("request-id")
155 4 100 66     15 ? $http_response->header("request-id")
      66        
      100        
156             : ""
157             });
158 3 50       72  
    50          
159             if ($http_response->is_success) {
160             $summary_logger->info($summary_stats);
161             } else {
162             $summary_stats->set_is_fault(1);
163             $summary_stats->set_fault_message(
164             __parse_fault_message($http_response->decoded_content));
165             $summary_logger->warn($summary_stats);
166             }
167             }
168              
169             # Full log of REST API traffic detail.
170 3 100       1353 my ($http_request, $http_response) = @_;
171 2         18  
172             # Validate the response status and log level.
173 1         11 return
174 1         12 unless ($http_response->is_success and $detail_logger->is_debug)
175             or (!$http_response->is_success and $detail_logger->is_info);
176 1         8  
177             my $detail_stats = Google::Ads::GoogleAds::Logging::DetailStats->new({
178             host => __parse_host($http_request),
179             # The service method name in which the logger is invoked.
180             method => (caller(3))[3],
181             request_headers => $http_request->headers,
182 4     4 1 899 request_content => $http_request->content,
183             response_headers => $http_response->headers
184             });
185              
186 4 100 100     38 if ($http_response->is_success) {
      100        
      100        
187             $detail_stats->set_response_content($http_response->decoded_content);
188             $detail_logger->debug($detail_stats);
189 2         35 } else {
190             $detail_stats->set_fault(__parse_faults($http_response->decoded_content));
191             $detail_logger->info($detail_stats);
192             }
193             }
194              
195             # Parses the host name from a HTTP request.
196             my $http_request = shift;
197             my $uri = $http_request->uri;
198 2 100       433 return $uri->scheme . "://" . $uri->host;
199 1         8 }
200 1         160  
201             # Parses the fault message from the HTTP response JSON payload.
202 1         10 my $response_content = shift;
203 1         8 my $response_body = decode_json($response_content);
204              
205             # When the fault is a GoogleAdsFailure.
206             my $fault_message =
207             $response_body->{error}{details}[0]{errors}[0]{message};
208              
209 5     5   6 # When the fault is a GRPC error, e.g. BadRequest, PreconditionFailure, QuotaFailure.
210 5         14 $fault_message = $response_body->{error}{message} if not $fault_message;
211 5         39  
212             $fault_message = $response_content if not $fault_message;
213              
214             return $fault_message;
215             }
216 1     1   289  
217 1         13 # Parses all the faults from the HTTP response JSON payload.
218             my $response_content = shift;
219             my $json_coder = JSON::XS->new->utf8->pretty;
220             my $response_body = $json_coder->decode($response_content);
221 1         3  
222             my $faults = $response_body->{error}{details}[0];
223             return $json_coder->encode($faults) if $faults;
224 1 50       4  
225             return $response_content;
226 1 50       4 }
227              
228 1         6 1;
229              
230             =pod
231              
232             =head1 NAME
233 1     1   112  
234 1         8 Google::Ads::GoogleAds::Logging::GoogleAdsLogger
235 1         11  
236             =head1 SYNOPSIS
237 1         2  
238 1 50       17 use Google::Ads::GoogleAds::Logging::GoogleAdsLogger;
239              
240 0           Google::Ads::GoogleAds::Logging::GoogleAdsLogger::enable_all_logging(1);
241              
242             Google::Ads::GoogleAds::Logging::GoogleAdsLogger::log_summary($http_request, $http_response);
243              
244             Google::Ads::GoogleAds::Logging::GoogleAdsLogger::log_detail($http_request, $http_response);
245              
246             =head1 DESCRIPTION
247              
248             This class allows logging of outgoing and incoming REST messages as executed API
249             calls. It initializes the loggers based on a provided F<log4perl.conf> file or
250             default parameters if the file is not found. It contains methods to retrieve the
251             summary and detail loggers.
252              
253             =head1 METHODS
254              
255             =head2 initialize_logging
256              
257             Initializes the loggers based on the default F<log4perl.conf> file or default
258             parameters if the file is not found.
259              
260             =head2 enable_summary_logging
261              
262             Enables the logging for the one-line summary.
263              
264             =head3 Parameters
265              
266             A boolean value of whether to include the DEBUG level messages.
267              
268             =head2 disable_summary_logging
269              
270             Disables the one-line summary logging.
271              
272             =head2 enable_detail_logging
273              
274             Enables the logging for traffic detail of HTTP request and response.
275              
276             =head3 Parameters
277              
278             A boolean value of whether to include the DEBUG level messages.
279              
280             =head2 disable_detail_logging
281              
282             Disables the traffic detail logging.
283              
284             =head2 enable_all_logging
285              
286             Enables all logging for the one-line summary and the traffic detail.
287              
288             =head3 Parameters
289              
290             A boolean value of whether to include the DEBUG level messages.
291              
292             =head2 disable_all_logging
293              
294             Stops all logging.
295              
296             =head2 get_summary_logger
297              
298             Retrieves the summary logger used to log the one-line summary.
299              
300             =head3 Returns
301              
302             A L<Log::Log4perl> logger for the one-line summary.
303              
304             =head2 get_detail_logger
305              
306             Retrieves the detail logger used to log the traffic detail.
307              
308             =head3 Returns
309              
310             A L<Log::Log4perl> logger for the traffic detail.
311              
312             =head2 log_summary
313              
314             Logs a one-line summary for each REST API request.
315              
316             =head3 Parameters
317              
318             =over
319              
320             =item *
321              
322             I<http_request>: The REST HTTP request sent to Google Ads API server.
323              
324             =item *
325              
326             I<http_response>: The HTTP response received from Google Ads API server.
327              
328             =back
329              
330             =head2 log_detail
331              
332             Full log of the traffic detail about the request/response payload.
333              
334             =head3 Parameters
335              
336             =over
337              
338             =item *
339              
340             I<http_request>: The REST HTTP request sent to Google Ads API server.
341              
342             =item *
343              
344             I<http_response>: The HTTP response received from Google Ads API server.
345              
346             =back
347              
348             =head2 __parse_host
349              
350             The private method to parse the hostname from a HTTP request.
351              
352             =head3 Parameters
353              
354             =over
355              
356             =item *
357              
358             I<http_request>: The REST HTTP request sent to Google Ads API server.
359              
360             =back
361              
362             =head3 Returns
363              
364             The parsed hostname in the format of <scheme>://<domain>.
365              
366             =head2 __parse_fault_message
367              
368             The private method to parse the fault message from the HTTP response, if an error
369             has occurred at the server side. This message can be used to construct a
370             L<Google::Ads::GoogleAds::Logging::SummaryStats>.
371              
372             =head2 __parse_faults
373              
374             The private method to parse all the faults from the HTTP response, and C<encode>
375             them in the JSON format. These faults will be used to construct a
376             L<Google::Ads::GoogleAds::Logging::DetailStats>.
377              
378             =head1 LICENSE AND COPYRIGHT
379              
380             Copyright 2019 Google LLC
381              
382             Licensed under the Apache License, Version 2.0 (the "License");
383             you may not use this file except in compliance with the License.
384             You may obtain a copy of the License at
385              
386             http://www.apache.org/licenses/LICENSE-2.0
387              
388             Unless required by applicable law or agreed to in writing, software
389             distributed under the License is distributed on an "AS IS" BASIS,
390             WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
391             See the License for the specific language governing permissions and
392             limitations under the License.
393              
394             =head1 REPOSITORY INFORMATION
395              
396             $Rev: $
397             $LastChangedBy: $
398             $Id: $
399              
400             =cut