File Coverage

blib/lib/Capture/Tiny/Extended.pm
Criterion Covered Total %
statement 219 248 88.3
branch 74 112 66.0
condition 20 38 52.6
subroutine 30 31 96.7
pod 1 5 20.0
total 344 434 79.2


line stmt bran cond sub pod time code
1 916     916   36285554 use 5.006;
  916         4038  
  916         37506  
2 916     916   4767 use strict;
  916         1744  
  916         24715  
3 916     916   4763 use warnings;
  916         1470  
  916         50491  
4             package Capture::Tiny::Extended;
5             our $VERSION = '0.114'; # VERSION
6             # ABSTRACT: Capture STDOUT and STDERR from from Perl, XS or external programs (with some extras)
7 916     916   5033 use Carp ();
  916         1463  
  916         16551  
8 916     916   4666 use Exporter ();
  916         1735  
  916         11809  
9 916     916   5386039 use IO::Handle ();
  916         8837305  
  916         20602  
10 916     916   7960 use File::Spec ();
  916         1742  
  916         18491  
11 916     916   2171910 use File::Temp qw/tempfile tmpnam/;
  916         25248161  
  916         127049  
12             # Get PerlIO or fake it
13             BEGIN {
14 916     916   2198 local $@;
15 916         8151 eval { require PerlIO; PerlIO->can('get_layers') }
  916         473112  
16 916 50       2197 or *PerlIO::get_layers = sub { return () };
  0         0  
17             }
18              
19             our @ISA = qw/Exporter/;
20             our @EXPORT_OK = qw/capture capture_merged tee tee_merged capture_files/;
21             our %EXPORT_TAGS = ( 'all' => \@EXPORT_OK );
22              
23             my $IS_WIN32 = $^O eq 'MSWin32';
24              
25             our $DEBUG = $ENV{PERL_CAPTURE_TINY_DEBUG};
26             my $DEBUGFH;
27             open $DEBUGFH, ">&STDERR" if $DEBUG;
28              
29             *_debug = $DEBUG ? sub(@) { print {$DEBUGFH} @_ } : sub(){0};
30              
31             our $TIMEOUT = 30;
32              
33             #--------------------------------------------------------------------------#
34             # command to tee output -- the argument is a filename that must
35             # be opened to signal that the process is ready to receive input.
36             # This is annoying, but seems to be the best that can be done
37             # as a simple, portable IPC technique
38             #--------------------------------------------------------------------------#
39             my @cmd = ($^X, '-e', '$SIG{HUP}=sub{exit}; '
40             . 'if( my $fn=shift ){ open my $fh, qq{>$fn}; print {$fh} $$; close $fh;} '
41             . 'my $buf; while (sysread(STDIN, $buf, 2048)) { '
42             . 'syswrite(STDOUT, $buf); syswrite(STDERR, $buf)}'
43             );
44              
45             #--------------------------------------------------------------------------#
46             # filehandle manipulation
47             #--------------------------------------------------------------------------#
48              
49             sub _relayer {
50 315208     315208   856752 my ($fh, $layers) = @_;
51 315208         1040827 _debug("# requested layers (@{$layers}) to $fh\n");
  315208         4083962  
52 315208         4820113 my %seen = ( unix => 1, perlio => 1 ); # filter these out
53 315208         869431 my @unique = grep { !$seen{$_}++ } @$layers;
  768506         2847506  
54 315208         1721780 _debug("# applying unique layers (@unique) to $fh\n");
55 315208         4664490 binmode($fh, join(":", ":raw", @unique));
56             }
57              
58             sub _name {
59 2253248     2253248   3920691 my $glob = shift;
60 916     916   5676 no strict 'refs'; ## no critic
  916         1921  
  916         4883128  
61 2253248         2710630 return *{$glob}{NAME};
  2253248         20194731  
62             }
63              
64             sub _open {
65 891969 50   891969   59270499 open $_[0], $_[1] or Carp::confess "Error from open(" . join(q{, }, @_) . "): $!";
66 891969 50       3240722 _debug( "# open " . join( ", " , map { defined $_ ? _name($_) : 'undef' } @_ ) . " as " . fileno( $_[0] ) . "\n" );
  1783938         5689195  
67             }
68              
69             sub _close {
70 385600 50   385600   9689422 close $_[0] or Carp::confess "Error from close(" . join(q{, }, @_) . "): $!";
71 385600 50       1508048 _debug( "# closed " . ( defined $_[0] ? _name($_[0]) : 'undef' ) . "\n" );
72             }
73              
74             my %dup; # cache this so STDIN stays fd0
75             my %proxy_count;
76             sub _proxy_std {
77 88390     88390   168356 my %proxies;
78 88390 100       307666 if ( ! defined fileno STDIN ) {
79 16443         31596 $proxy_count{stdin}++;
80 16443 100       49082 if (defined $dup{stdin}) {
81 2205         11930 _open \*STDIN, "<&=" . fileno($dup{stdin});
82 2205 50       12389 _debug( "# restored proxy STDIN as " . (defined fileno STDIN ? fileno STDIN : 'undef' ) . "\n" );
83             }
84             else {
85 14238         394599 _open \*STDIN, "<" . File::Spec->devnull;
86 14238 50       101268 _debug( "# proxied STDIN as " . (defined fileno STDIN ? fileno STDIN : 'undef' ) . "\n" );
87 14238         241064 _open $dup{stdin} = IO::Handle->new, "<&=STDIN";
88             }
89 16443         54216 $proxies{stdin} = \*STDIN;
90 16443 50       144443 binmode(STDIN, ':utf8') if $] >= 5.008;
91             }
92 88390 100       397842 if ( ! defined fileno STDOUT ) {
93 14238         37503 $proxy_count{stdout}++;
94 14238 50       59762 if (defined $dup{stdout}) {
95 0         0 _open \*STDOUT, ">&=" . fileno($dup{stdout});
96 0 0       0 _debug( "# restored proxy STDOUT as " . (defined fileno STDOUT ? fileno STDOUT : 'undef' ) . "\n" );
97             }
98             else {
99 14238         366258 _open \*STDOUT, ">" . File::Spec->devnull;
100 14238 50       128708 _debug( "# proxied STDOUT as " . (defined fileno STDOUT ? fileno STDOUT : 'undef' ) . "\n" );
101 14238         271347 _open $dup{stdout} = IO::Handle->new, ">&=STDOUT";
102             }
103 14238         47732 $proxies{stdout} = \*STDOUT;
104 14238 50       126567 binmode(STDOUT, ':utf8') if $] >= 5.008;
105             }
106 88390 100       277135 if ( ! defined fileno STDERR ) {
107 14238         33649 $proxy_count{stderr}++;
108 14238 50       46787 if (defined $dup{stderr}) {
109 0         0 _open \*STDERR, ">&=" . fileno($dup{stderr});
110 0 0       0 _debug( "# restored proxy STDERR as " . (defined fileno STDERR ? fileno STDERR : 'undef' ) . "\n" );
111             }
112             else {
113 14238         364169 _open \*STDERR, ">" . File::Spec->devnull;
114 14238 50       78779 _debug( "# proxied STDERR as " . (defined fileno STDERR ? fileno STDERR : 'undef' ) . "\n" );
115 14238         408914 _open $dup{stderr} = IO::Handle->new, ">&=STDERR";
116             }
117 14238         46681 $proxies{stderr} = \*STDERR;
118 14238 50       172997 binmode(STDERR, ':utf8') if $] >= 5.008;
119             }
120 88390         369696 return %proxies;
121             }
122              
123             sub _unproxy {
124 86586     86586   463196 my (%proxies) = @_;
125 86586         521933 _debug( "# unproxing " . join(" ", keys %proxies) . "\n" );
126 86586         577005 for my $p ( keys %proxies ) {
127 44289         120024 $proxy_count{$p}--;
128 44289         270978 _debug( "# unproxied " . uc($p) . " ($proxy_count{$p} left)\n" );
129 44289 100       162330 if ( ! $proxy_count{$p} ) {
130 42174         133895 _close $proxies{$p};
131 42174 50       280836 _close $dup{$p} unless $] < 5.008; # 5.6 will have already closed this as dup
132 42174         1241657 delete $dup{$p};
133             }
134             }
135             }
136              
137             sub _copy_std {
138 88390     88390   183628 my %handles = map { $_, IO::Handle->new } qw/stdin stdout stderr/;
  265170         6340855  
139 88390         2731829 _debug( "# copying std handles ...\n" );
140 88390         260794 _open $handles{stdin}, "<&STDIN";
141 88390         272441 _open $handles{stdout}, ">&STDOUT";
142 88390         412507 _open $handles{stderr}, ">&STDERR";
143 88390         416682 return \%handles;
144             }
145              
146             sub _open_std {
147 174976     174976   515640 my ($handles) = @_;
148 174976         2654605 _open \*STDIN, "<&" . fileno $handles->{stdin};
149 174976         1331450 _open \*STDOUT, ">&" . fileno $handles->{stdout};
150 174976         981929 _open \*STDERR, ">&" . fileno $handles->{stderr};
151             }
152              
153             #--------------------------------------------------------------------------#
154             # private subs
155             #--------------------------------------------------------------------------#
156              
157             sub _start_tee {
158 41855     41855   328203 my ($which, $stash) = @_;
159             # setup pipes
160 41855         2136454 $stash->{$_}{$which} = IO::Handle->new for qw/tee reader/;
161 41855         11094047 pipe $stash->{reader}{$which}, $stash->{tee}{$which};
162 41855         921857 _debug( "# pipe for $which\: " . _name($stash->{tee}{$which}) . " "
163             . fileno( $stash->{tee}{$which} ) . " => " . _name($stash->{reader}{$which})
164             . " " . fileno( $stash->{reader}{$which}) . "\n" );
165 41855         1361656 select((select($stash->{tee}{$which}), $|=1)[0]); # autoflush
166             # setup desired redirection for parent and child
167 41855         191986 $stash->{new}{$which} = $stash->{tee}{$which};
168 41855         434068 $stash->{child}{$which} = {
169             stdin => $stash->{reader}{$which},
170             stdout => $stash->{old}{$which},
171             stderr => $stash->{capture}{$which},
172             };
173             # flag file is used to signal the child is ready
174 41855         1903988 $stash->{flag_files}{$which} = scalar tmpnam();
175             # execute @cmd as a separate process
176 41855 50       29662314 if ( $IS_WIN32 ) {
177 0         0 local $@;
178 0         0 eval "use Win32API::File qw/CloseHandle GetOsFHandle SetHandleInformation fileLastError HANDLE_FLAG_INHERIT INVALID_HANDLE_VALUE/ ";
179 0 0       0 _debug( "# Win32API::File loaded\n") unless $@;
180 0         0 my $os_fhandle = GetOsFHandle( $stash->{tee}{$which} );
181 0 0 0     0 _debug( "# Couldn't get OS handle: " . fileLastError() . "\n") if ! defined $os_fhandle || $os_fhandle == INVALID_HANDLE_VALUE();
182 0 0       0 if ( SetHandleInformation( $os_fhandle, HANDLE_FLAG_INHERIT(), 0) ) {
183 0         0 _debug( "# set no-inherit flag on $which tee\n" );
184             }
185             else {
186 0         0 _debug( "# can't disable tee handle flag inherit: " . fileLastError() . "\n");
187             }
188 0         0 _open_std( $stash->{child}{$which} );
189 0         0 $stash->{pid}{$which} = system(1, @cmd, $stash->{flag_files}{$which});
190             # not restoring std here as it all gets redirected again shortly anyway
191             }
192             else { # use fork
193 41855         215054 _fork_exec( $which, $stash );
194             }
195             }
196              
197             sub _fork_exec {
198 41855     41855   78378 my ($which, $stash) = @_;
199 41855         136928610 my $pid = fork;
200 41855 50       1681537 if ( not defined $pid ) {
    100          
201 0         0 Carp::confess "Couldn't fork(): $!";
202             }
203             elsif ($pid == 0) { # child
204 902         153237 _debug( "# in child process ...\n" );
205 902         44072 untie *STDIN; untie *STDOUT; untie *STDERR;
  902         28588  
  902         16603  
206 902         86266 _close $stash->{tee}{$which};
207 902         26957 _debug( "# redirecting handles in child ...\n" );
208 902         34140 _open_std( $stash->{child}{$which} );
209 902         19470 _debug( "# calling exec on command ...\n" );
210 902         0 exec @cmd, $stash->{flag_files}{$which};
211             }
212 40953         6172042 $stash->{pid}{$which} = $pid
213             }
214              
215 1648132196   100 1648132196   29818225143 sub _files_exist { -f $_ || return 0 for @_; return 1 }
  42302         320187  
216              
217             sub _wait_for_tees {
218 21151     21151   101462 my ($stash) = @_;
219 21151         82694 my $start = time;
220 21151         45490 my @files = values %{$stash->{flag_files}};
  21151         1595870  
221 21151 50       412806 my $timeout = defined $ENV{PERL_CAPTURE_TINY_TIMEOUT}
222             ? $ENV{PERL_CAPTURE_TINY_TIMEOUT} : $TIMEOUT;
223 21151   33     315123 1 until _files_exist(@files) || ($timeout && (time - $start > $timeout));
      66        
224 21151 50       150952 Carp::confess "Timed out waiting for subprocesses to start" if ! _files_exist(@files);
225 21151         25668269 unlink $_ for @files;
226             }
227              
228             sub _kill_tees {
229 21151     21151   58567 my ($stash) = @_;
230 21151 50       243521 if ( $IS_WIN32 ) {
231 0         0 _debug( "# closing handles with CloseHandle\n");
232 0         0 CloseHandle( GetOsFHandle($_) ) for values %{ $stash->{tee} };
  0         0  
233 0         0 _debug( "# waiting for subprocesses to finish\n");
234 0         0 my $start = time;
235 0   0     0 1 until wait == -1 || (time - $start > 30);
236             }
237             else {
238 21151         48089 _close $_ for values %{ $stash->{tee} };
  21151         250854  
239 21151         73191 waitpid $_, 0 for values %{ $stash->{pid} };
  21151         34828656  
240             }
241             }
242              
243             sub _slurp {
244 156702     156702   1691090 seek $_[0],0,0; local $/; return scalar readline $_[0];
  156702         976022  
  156702         7359160  
245             }
246              
247             #--------------------------------------------------------------------------#
248             # _capture_tee() -- generic main sub for capturing or teeing
249             #--------------------------------------------------------------------------#
250              
251             sub _capture_tee {
252 88390     88390   1047125 _debug( "# starting _capture_tee with (@_)...\n" );
253 88390         571489 my ($tee_stdout, $tee_stderr, $merge, $code, $files) = @_;
254             # save existing filehandles and setup captures
255 88390         582347 local *CT_ORIG_STDIN = *STDIN ;
256 88390         456505 local *CT_ORIG_STDOUT = *STDOUT;
257 88390         375987 local *CT_ORIG_STDERR = *STDERR;
258             # find initial layers
259 88390         1793124 my %layers = (
260             stdin => [PerlIO::get_layers(\*STDIN) ],
261             stdout => [PerlIO::get_layers(\*STDOUT)],
262             stderr => [PerlIO::get_layers(\*STDERR)],
263             );
264 88390         639592 _debug( "# existing layers for $_\: @{$layers{$_}}\n" ) for qw/stdin stdout stderr/;
  265170         1450927  
265             # bypass scalar filehandles and tied handles
266 88390         195212 my %localize;
267 88390 100       154967 $localize{stdin}++, local(*STDIN) if grep { $_ eq 'scalar' } @{$layers{stdin}};
  155423         675006  
  88390         282129  
268 88390 100       180228 $localize{stdout}++, local(*STDOUT) if grep { $_ eq 'scalar' } @{$layers{stdout}};
  182312         613983  
  88390         250121  
269 88390 100       156975 $localize{stderr}++, local(*STDERR) if grep { $_ eq 'scalar' } @{$layers{stderr}};
  182282         521827  
  88390         216891  
270 88390 100 66     540341 $localize{stdout}++, local(*STDOUT), _open( \*STDOUT, ">&=1") if tied *STDOUT && $] >= 5.008;
271 88390 100 66     423495 $localize{stderr}++, local(*STDERR), _open( \*STDERR, ">&=2") if tied *STDERR && $] >= 5.008;
272 88390         472944 _debug( "# localized $_\n" ) for keys %localize;
273 88390         327846 my %proxy_std = _proxy_std();
274 88390         169086 _debug( "# proxy std is @{ [%proxy_std] }\n" );
  88390         537745  
275 88390         386259 my $stash = { old => _copy_std() };
276             # update layers after any proxying
277 88390         2249898 %layers = (
278             stdin => [PerlIO::get_layers(\*STDIN) ],
279             stdout => [PerlIO::get_layers(\*STDOUT)],
280             stderr => [PerlIO::get_layers(\*STDERR)],
281             );
282 88390         550110 _debug( "# post-proxy layers for $_\: @{$layers{$_}}\n" ) for qw/stdin stdout stderr/;
  265170         1377859  
283             # get handles for capture and apply existing IO layers
284 88390         413830 $stash->{new}{$_} = $stash->{capture}{$_} = _capture_file( $_, $files ) for qw/stdout stderr/;
285 88390         46599705 _debug("# will capture $_ on " .fileno($stash->{capture}{$_})."\n" ) for qw/stdout stderr/;
286             # tees may change $stash->{new}
287 88390 100       400212 _start_tee( stdout => $stash ) if $tee_stdout;
288 87849 100       2406876 _start_tee( stderr => $stash ) if $tee_stderr;
289 87488 100 66     2939822 _wait_for_tees( $stash ) if $tee_stdout || $tee_stderr;
290             # finalize redirection
291 87488 100       369504 $stash->{new}{stderr} = $stash->{new}{stdout} if $merge;
292 87488         1874161 $stash->{new}{stdin} = $stash->{old}{stdin};
293 87488         253794 _debug( "# redirecting in parent ...\n" );
294 87488         576987 _open_std( $stash->{new} );
295             # execute user provided code
296 87488         220690 my ($exit_code, $inner_error, $outer_error, @user_code_result);
297             {
298 87488 100       148514 local *STDIN = *CT_ORIG_STDIN if $localize{stdin}; # get original, not proxy STDIN
  87488         330062  
299 87488 100       293486 local *STDERR = *STDOUT if $merge; # minimize buffer mixups during $code
300 87488         274684 _debug( "# finalizing layers ...\n" );
301 87488         521888 _relayer(\*STDOUT, $layers{stdout});
302 87488 100       404558 _relayer(\*STDERR, $layers{stderr}) unless $merge;
303 87488         382229 _debug( "# running code $code ...\n" );
304 87488         144810 local $@;
305 87488         196616 @user_code_result = eval {
306 87488         1177552 my @res = $code->();
307 86585         444926926 $inner_error = $@;
308 86585         771029 return @res;
309             };
310 86586         782203 $exit_code = $?; # save this for later
311 86586         643881 $outer_error = $@; # save this for later
312             }
313             # restore prior filehandles and shut down tees
314 86586         376053 _debug( "# restoring ...\n" );
315 86586         971744 _open_std( $stash->{old} );
316 86586         223371 _close( $_ ) for values %{$stash->{old}}; # don't leak fds
  86586         969864  
317 86586         914612 _unproxy( %proxy_std );
318 86586 100 66     1763131 _kill_tees( $stash ) if $tee_stdout || $tee_stderr;
319             # return captured output
320 86586         812720 _relayer($stash->{capture}{stdout}, $layers{stdout});
321 86586 100       625982 _relayer($stash->{capture}{stderr}, $layers{stderr}) unless $merge;
322 86586         556245 _debug( "# slurping captured $_ with layers: @{[PerlIO::get_layers($stash->{capture}{$_})]}\n") for qw/stdout stderr/;
  173172         2408166  
323 86586         580195 my $got_out = _slurp($stash->{capture}{stdout});
324 86586 100       486405 my $got_err = $merge ? q() : _slurp($stash->{capture}{stderr});
325 86586 50 66     517625 print CT_ORIG_STDOUT $got_out if $localize{stdout} && $tee_stdout;
326 86586 50 100     897895 print CT_ORIG_STDERR $got_err if !$merge && $localize{stderr} && $tee_stdout;
      66        
327 86586         249956 $? = $exit_code;
328 86586 100       253831 $@ = $inner_error if $inner_error;
329 86586 100       254733 die $outer_error if $outer_error;
330 86585         730201 _debug( "# ending _capture_tee with (@_)...\n" );
331 86585 50       818941 return wantarray ? ($got_out, @user_code_result) : $got_out if $merge;
    100          
332 70115 100       2790542 return wantarray ? ($got_out, $got_err, @user_code_result) : $got_out;
333             }
334              
335             #--------------------------------------------------------------------------#
336             # capture to files
337             #--------------------------------------------------------------------------#
338              
339             sub _capture_file {
340 176780     176780   74521194 my ( $target, $files ) = @_;
341            
342 176780 50       2676877 return File::Temp->new if !$files->{$target};
343            
344 0 0 0     0 Carp::confess "$target file '$files->{$target}' already exists, set clobber => 1 to override"
345             if $files->{new_files} and _files_exist( $files->{$target} );
346            
347 0         0 my $mode = "+>>";
348 0 0       0 $mode = "+>" if $files->{clobber};
349            
350 0         0 my $fh = Symbol::gensym;
351 0         0 _open $fh, "$mode$files->{$target}";
352            
353 0         0 return $fh;
354             }
355              
356 0     0 1 0 sub capture_files { return { @_ }; }
357              
358             #--------------------------------------------------------------------------#
359             # create API subroutines from [tee STDOUT flag, tee STDERR, merge flag]
360             #--------------------------------------------------------------------------#
361              
362             my %api = (
363             capture => [0,0,0],
364             capture_merged => [0,0,1],
365             tee => [1,1,0],
366             tee_merged => [1,0,1], # don't tee STDOUT since merging
367             );
368              
369             for my $sub ( keys %api ) {
370             my $args = join q{, }, @{$api{$sub}};
371 51577     51577 0 272783181 eval "sub $sub(&;\$) {unshift \@_, $args; goto \\&_capture_tee;}"; ## no critic
  51577     14760 0 738187  
  14760     20163 0 64452923  
  14760     1890 0 115818  
  20163         1787887  
  20163         177136  
  1890         99044  
  1890         17817  
372             }
373              
374             1;
375              
376              
377              
378             =pod
379              
380             =head1 NAME
381              
382             Capture::Tiny::Extended - Capture STDOUT and STDERR from from Perl, XS or external programs (with some extras)
383              
384             =head1 VERSION
385              
386             version 0.114
387              
388             =head1 SYNOPSIS
389              
390             use Capture::Tiny::Extended qw/capture tee capture_merged tee_merged/;
391            
392             # capture return values
393            
394             my ($stdout, $stderr, @return) = capture {
395             # your code here
396             return system( 'ls' );
397             };
398            
399             ($merged, @return) = capture_merged {
400             # your code here
401             return system( 'ls' );
402             };
403            
404             # or use explicit capture files
405            
406             ($stdout, $stderr, @return) = capture(
407             sub { # your code here },
408             { stdout => 'stdout.log' }
409             );
410            
411             # or with sugar
412            
413             use Capture::Tiny::Extended qw/capture tee capture_merged tee_merged capture_files/;
414            
415             ($stdout, $stderr, @return) = capture {
416             # your code here
417             }
418             capture_files (
419             stdout => 'stdout.log',
420             stderr => 'stderr.log',
421             );
422              
423             =head1 DESCRIPTION
424              
425             Capture::Tiny::Extended is a fork of L. It is functionally
426             identical with the parent module, except for the differences documented in this
427             POD. Please see the documentation of L for details on standard
428             usage.
429              
430             Please note that this can be considered an experimental module in some respects.
431             I am not as experienced with the subject matter (and in general) as David Golden
432             and mostly implemented these features here because i needed them fast and did
433             not have the time to spare to wait for them to get into L. If you
434             need capture functionality for mission-critical parts, consider whether
435             L might be enough for the job.
436              
437             Of course I will however make all efforts to make this as stable and useful as
438             possible by keeping it up-to-date (as my time permits) with changes and bugfixes
439             applied to L, as well as responding and addressing and change
440             requests or bug reports for this module.
441              
442             =for Pod::Coverage capture capture_merged tee tee_merged
443              
444             =head1 DIFFERENCES
445              
446             =head2 Capturing Return Values
447              
448             When executing code within a capture you sometimes want to also keep the return
449             value, for example when capturing a system() call. In Capture::Tiny this has to
450             be done like this:
451              
452             use Capture::Tiny 'capture';
453            
454             my $res;
455             my ( $out, $err ) = capture {
456             $res = system( 'ls' );
457             };
458              
459             Capture::Tiny::Extended automatically captures return values and returns them
460             after the second return value (or first if you're using the merged functions).
461              
462             use Capture::Tiny::Extended 'capture';
463            
464             my ( $out, $err, $res ) = capture { system( 'ls' ) };
465              
466             =head2 Teeing In Realtime
467              
468             Sometimes you want to use Capture::Tiny to capture any and all output of an
469             action and dump it into a log file, while also displaying it on the screen and
470             then post-process the results later on (for example for sending status mails).
471             The only way to do this with Capture::Tiny is code like this:
472              
473             use Capture::Tiny 'capture';
474             use File::Slurp;
475            
476             my $res;
477             my ( $out, $err ) = capture {
478             # lockfile and other processing here along with debug output
479             $res = system( 'long_running_program' );
480             };
481            
482             file_write 'out.log', $out;
483             send_mail( $err ) if $res;
484              
485             This has a very big disadvantage. If the long-running program runs too long, and
486             the perl script is started by something like crontab there is no way for you to
487             get at the log output. You will have to wait for it to complete before the
488             captured output is written to the file.
489              
490             Capture::Tiny::Extended gives you the option to provide filenames for it to use
491             as capture buffers. This means the output from the captured code will appear on
492             the screen and in the file in realtime, and will afterwards be available to your
493             Perl script in the variables returned by the capture function:
494              
495             use Capture::Tiny::Extended 'capture';
496            
497             my ( $out, $err, $res ) = capture(
498             sub {
499             # lockfile and other processing here along with debug output
500             return system( 'long_running_program' );
501             },
502             {
503             stdout => 'out.log',
504             stderr => 'err.log',
505             }
506             );
507            
508             send_mail( $err ) if $res;
509              
510             =head2 capture_files
511              
512             Since using hashes in that way breaks a bit of the syntax magic of the capture
513             functions (or makes them harder to read), there exists a sugar function to take
514             the file arguments and pass it on to the capture functions:
515              
516             use Capture::Tiny::Extended qw( capture capture_files );
517            
518             my ( $out, $err, $res ) = capture {
519             # lockfile and other processing here along with debug output
520             return system( 'long_running_program' );
521             }
522             capture_files {
523             stdout => 'out.log',
524             stderr => 'err.log',
525             };
526            
527             send_mail( $err ) if $res;
528              
529             =head2 Capture File Mode Options
530              
531             For purposes of avoiding data loss, the default behavior is to append to the
532             specified files. The key 'new_files' can be set to a true value on the extra
533             file hash parameter to instruct Capture::Tiny::Extended to attempt to make
534             files. It will die however if the specified files already exist.
535              
536             use Capture::Tiny::Extended 'capture';
537            
538             my $out = capture_merged(
539             sub { system( 'ls' ) },
540             { stdout => 'out.log', new_files => 1 }
541             );
542              
543             If existing files should always be overwritten, no matter what, the key
544             'clobber' can be set instead:
545              
546             use Capture::Tiny::Extended 'capture';
547            
548             my $out = capture_merged(
549             sub { system( 'ls' ) },
550             { stdout => 'out.log', clobber => 1 }
551             );
552              
553             =head1 WHY A FORK?
554              
555             The realtime teeing feature was very important for one of my current projects
556             and i needed it on CPAN to be able to easily distribute it to many systems.
557             I had provided a patch for the return value capturing on Github to David Golden
558             a long while ago, but due to being busy with real life, family and more
559             important projects than this he was not able to find time to proof and integrate
560             it and in the foreseeable future won't be able to either. At the same time i
561             lack the Perl file handle, descriptor and layer chops to take full
562             responsibility for Capture::Tiny itself. Usually i would have just written a
563             subclass of the original, but since Capture::Tiny is written in functional style
564             this was not possible.
565              
566             As such a fork seemed to be the best option to get these features out there. I'd
567             be more than happy to see them integrated into C::T someday and will keep my git
568             repository in such a state as to make this as easy as possible. (Lots of
569             rebasing.)
570              
571             =head1 ACKNOWLEDGEMENTS
572              
573             Capture::Tiny is an invaluable tool that uses practically indecent amounts of
574             creativity to solve decidedly nontrivial problems and circumvents many cliffs
575             the ordinary coder (and most certainly me) would inevitably crash against.
576              
577             Many thanks to David Golden for taking the time and braving all those traps of
578             insanity to create Capture::Tiny.
579              
580             =for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders
581              
582             =head1 SUPPORT
583              
584             =head2 Bugs / Feature Requests
585              
586             Please report any bugs or feature requests by email to C, or through
587             the web interface at L. You will be automatically notified of any
588             progress on the request by the system.
589              
590             =head2 Source Code
591              
592             This is open source software. The code repository is available for
593             public review and contribution under the terms of the license.
594              
595             L
596              
597             git clone https://github.com/wchristian/capture-tiny
598              
599             =head1 AUTHORS
600              
601             =over 4
602              
603             =item *
604              
605             Christian Walde
606              
607             =item *
608              
609             David Golden
610              
611             =back
612              
613             =head1 COPYRIGHT AND LICENSE
614              
615             This software is Copyright (c) 2009 by David Golden.
616              
617             This is free software, licensed under:
618              
619             The Apache License, Version 2.0, January 2004
620              
621             =cut
622              
623              
624             __END__