Log-Trace-1.070/0000755000176000001460000000000010341316216014407 5ustar tonyhtechnical00000000000000Log-Trace-1.070/t/0000755000176000001460000000000010341316216014652 5ustar tonyhtechnical00000000000000Log-Trace-1.070/t/trace_functions.t0000644000176000001460000000345610341316164020237 0ustar tonyhtechnical00000000000000#!/usr/local/bin/perl -w # $Id: trace_functions.t,v 1.4 2004/12/15 15:47:35 johna Exp $ use strict; use Test::More tests => 9; # Find local libs unless running under Test::Harness BEGIN { unshift @INC, -d 't' ? 'lib' : '../lib' unless grep /\bblib\b/, @INC } require_ok('Log::Trace'); my $output; my $trace = sub { $output = shift() . "\n"; }; my $file = __FILE__; sub test_here { TRACE_HERE() } import Log::Trace custom => $trace; my ($count, $item) = (11, 'green bottles'); TRACEF("%d %s sitting on the wall", --$count, $item); is ($output, "$count $item sitting on the wall\n", 'simple TRACEF ok'); $output = ''; TRACE_HERE(); like ($output, qr/In Log::Trace::TRACE_HERE\(\) - line \d+ of \Q$file\E\n/, 'simple TRACE_HERE, not in a subroutine'); $output = ''; test_here(); like ($output, qr/In main::test_here\(\) - line \d+ of \Q$file\E\n/, 'simple TRACE_HERE, in a subroutine'); $output = ''; test_here(--$count, $item); like ($output, qr/In main::test_here\((?:$count,$item)?\) - line \d+ of \Q$file\E\n/, 'TRACE_HERE, in a subroutine with args'); # basic test of functions with a level import Log::Trace custom => $trace, {Level => 1}; $output = ''; TRACEF({Level => 1}, "%d %s sitting on the wall", --$count, $item); is ($output, "$count $item sitting on the wall\n", 'simple TRACEF at level 1 ok'); $output = ''; TRACE_HERE({Level => 1}); like ($output, qr/In Log::Trace::TRACE_HERE\(\) - line \d+ of \Q$file\E\n/, 'simple TRACE_HERE at level 1, not in a subroutine'); # Test that the functions don't output at too high a level $output = ''; TRACEF({Level => 99}, "%d %s sitting on the wall", --$count, $item); is ($output, "", 'simple TRACEF at level 99, not output'); $output = ''; TRACE_HERE({Level => 99}); is ($output, '', 'simple TRACE_HERE at level 99, not output'); sub TRACE {} Log-Trace-1.070/t/lib/0000755000176000001460000000000010341316216015420 5ustar tonyhtechnical00000000000000Log-Trace-1.070/t/lib/Test_DeepImport.pm0000644000176000001460000000072310341316164021031 0ustar tonyhtechnical00000000000000#!/usr/local/bin/perl -w # $Id: Test_DeepImport.pm,v 1.3 2004/11/19 12:53:32 simonf Exp $ package Test_DeepImport; use vars '$VERSION'; $VERSION = 0.00001; use strict; sub new { TRACE('Creating object'); return bless \do {my $x}, shift; } sub hello { TRACE('Hello World!') } sub first { &next } sub next { TRACE('IN NEXT') } sub ether { TRACE('How did we get here?') } sub TRACE {} sub DUMP {} package Test_DeepImport_Without_TRACE; sub test {1}; 1; Log-Trace-1.070/t/lib/Test_DelayedImport.pm0000644000176000001460000000034610341316164021524 0ustar tonyhtechnical00000000000000#!/usr/local/bin/perl -w # $Id: Test_DelayedImport.pm,v 1.2 2004/11/19 12:53:32 simonf Exp $ package Test_DelayedImport; use vars '$VERSION'; $VERSION = 0.00001; use strict; sub hello { TRACE('Hello World!') } sub TRACE {} 1; Log-Trace-1.070/t/trace_file.t0000644000176000001460000000426310341316164017143 0ustar tonyhtechnical00000000000000#!/usr/local/bin/perl -w # $Id: trace_file.t,v 1.6 2004/12/15 15:47:35 johna Exp $ use strict; use Test::More; use File::Basename; use File::Spec; # get suitable dir for output file, or skip this test my $filename; my $dir = File::Spec->tmpdir; $dir ||= -w dirname(__FILE__) ? dirname(__FILE__) : -w '.' ? '.' : ''; if ($dir) { plan tests => 7; $filename = File::Spec->catfile($dir, 'Log-Trace-test-filemode.out'); } else { plan skip_all => 'No writable temp directory found'; } TRACE("trace output -> $filename"); # Cleanup END { unlink $filename } # Find local libs unless running under Test::Harness BEGIN { unshift @INC, -d 't' ? 'lib' : '../lib' unless grep /\bblib\b/, @INC } require_ok('Log::Trace'); my $test_file = __FILE__; my $timestamp = qr|\d{4}(?:-\d\d){2} \d\d(?::\d\d){2}(?:\.\d{6})?|; my $message; import Log::Trace file => $filename; $message = q[Just don't create a file called -rf. :-) --Larry Wall in <11393@jpl-devvax.JPL.NASA.GOV>]; TRACE($message); my $test_output = "$message\n"; is (read_file(), $test_output, 'message traced to supplied file'); import Log::Trace file => $filename, {Level => 1}; $message = $0; TRACE({Level => 1}, $message); $test_output .= "$message\n"; is (read_file(), $test_output, 'level 1 trace to file ok'); TRACE({Level => 99}, $message); # no change-> # $test_output .= "$message\n"; is (read_file(), $test_output, 'level 99 message not traced ok'); # Test verbose import Log::Trace file => $filename, {Verbose => 0}; TRACE(join '', reverse 0..9); $test_output .= "9876543210\n"; is (read_file(), $test_output, 'verbose:0 is also not verbose'); import Log::Trace file => $filename, {Verbose => 1}; TRACE(join '', ('a'..'f')); like (read_file(), qr/\nmain::__ANON__ \(\d+\) :: abcdef\n\Z/, 'verbose:1 adds some caller information'); import Log::Trace file => $filename, {Verbose => 2}; TRACE(join '', ('a'..'f')); like (read_file(), qr/\n\Q$test_file\E: main::__ANON__ \(\d+\) \[$timestamp\] abcdef\n\Z/, 'verbose:2 adds timstamp and file info'); sub read_file { local *FH; open FH, "< $filename" or die "open < '$filename' -- $!"; my $contents = do {local $/; }; close FH; return $contents; } sub TRACE {} Log-Trace-1.070/t/pod.t0000644000176000001460000000020110341316165015615 0ustar tonyhtechnical00000000000000use Test::More; eval "use Test::Pod 1.00"; plan skip_all => "Test::Pod 1.00 required for testing POD" if $@; all_pod_files_ok(); Log-Trace-1.070/t/trace_levels.t0000644000176000001460000000543210341316164017515 0ustar tonyhtechnical00000000000000#!/usr/local/bin/perl -w # $Id: trace_levels.t,v 1.3 2004/11/23 14:07:32 simonf Exp $ use strict; use Test::More tests => 15; # Find local libs unless running under Test::Harness BEGIN { unshift @INC, -d 't' ? 'lib' : '../lib' unless grep /\bblib\b/, @INC } require_ok('Log::Trace'); my $output; my $trace = sub { $output = shift() . "\n"; }; my $message; import Log::Trace custom => $trace; $message = 'sending trace message without levels'; TRACE($message); is ($output, "$message\n", 'message traced without levels'); $message = 'sending trace message without imported level only'; TRACE({Level => 3}, $message); is ($output, "$message\n", 'level 3 message traced without levels'); # Now set the levels import Log::Trace custom => $trace, {Level => 3}; $message = rand(100); $output = ''; TRACE({Level => 1}, $message); is ($output, "$message\n", 'level 1 message traced at level 3'); $message = rand(100); $output = ''; TRACE({Level => 5}, $message); is ($output, "", 'level 5 message NOT traced at level 3'); $message = rand(100); $output = ''; TRACE($message); is ($output, "", 'undefined level message NOT traced at level 3'); # define a list of levels import Log::Trace custom => $trace, {Level => [0 .. 3]}; $message = rand(100); $output = ''; TRACE({Level => 1}, $message); is ($output, "$message\n", 'level 1 message traced at specified levels'); $message = rand(100); $output = ''; TRACE({Level => 0}, $message); is ($output, "$message\n", 'level 0 message traced at specified levels'); $message = rand(100); $output = ''; TRACE({Level => 4}, $message); is ($output, "", 'level 4 message not traced at specified levels'); $message = rand(100); $output = ''; TRACE($message); is ($output, "", 'undefined level message NOT traced at specified levels'); # define a list of levels, including 'undef' import Log::Trace custom => $trace, {Level => [undef, 1 .. 3]}; $message = rand(100); $output = ''; TRACE({Level => 2}, $message); is ($output, "$message\n", 'level 2 message traced at specified levels'); $message = rand(100); $output = ''; TRACE({Level => 0}, $message); is ($output, "", 'level 0 message NOT traced at specified levels'); $message = rand(100); $output = ''; TRACE($message); is ($output, "$message\n", 'undefined level message traced at specified levels'); # use a custom level handler import Log::Trace custom => $trace, {Level => \&custom_levels}; $message = rand(100); $output = ''; TRACE({Level => 2}, $message); is ($output, "", 'level 2 message NOT traced at custom levels'); $message = rand(100); $output = ''; TRACE({Level => 0.1}, $message); is ($output, "$message\n", 'level 0.1 message traced at specified levels'); sub custom_levels { my ($packge, $level) = @_; # allow 1.1, 2.1, 3.1, etc, but not 1, 2, 3 or 1.2, 2.2 return $level - int($level) == 0.1; } sub TRACE {} Log-Trace-1.070/t/trace_print.t0000644000176000001460000000105010341316164017347 0ustar tonyhtechnical00000000000000#!/usr/local/bin/perl -w # $Id: trace_print.t,v 1.3 2004/11/23 14:07:32 simonf Exp $ use strict; print "1..2\n"; # Find local libs unless running under Test::Harness BEGIN { unshift @INC, -d 't' ? 'lib' : '../lib' unless grep /\bblib\b/, @INC } require Log::Trace; import Log::Trace 'print'; TRACE("ok 1 (Log::Trace $Log::Trace::VERSION loaded)"); import Log::Trace print => \*STDOUT; TRACE('ok 2 (print => STDOUT)'); import Log::Trace print => {Level => 1}; TRACE({Level => 999}, "not ok 3 (this shouldn't cause the test to fail)"); sub TRACE {} Log-Trace-1.070/t/trace_deep.t0000644000176000001460000000213310341316164017133 0ustar tonyhtechnical00000000000000#!/usr/local/bin/perl -w # $Id: trace_deep.t,v 1.6 2004/12/03 11:43:53 simonf Exp $ use strict; use Test::More tests => 5; # Find local libs unless running under Test::Harness BEGIN { unshift @INC, -d 't' ? 'lib' : '../lib' unless grep /\bblib\b/, @INC } require_ok('Log::Trace'); use File::Basename; use File::Spec::Functions 'catdir'; BEGIN { unshift @INC, catdir(dirname(__FILE__), 'lib') } my $output; my $trace = sub { $output = shift() . "\n"; }; my $message; require Test_DeepImport; import Log::Trace custom => $trace, {Deep => 1, AutoImport => 1}; my $o = Test_DeepImport->new(); is($output, "Creating object\n", 'Deep import'); ok(!$INC{'Test_DelayedImport.pm'}, "delayed module isn't loaded yet"); # eval because the require() in this doc is already compiled to use # CORE::require eval "require Test_DelayedImport"; ok($INC{'Test_DelayedImport.pm'}, "delayed module now loaded"); Test_DelayedImport::TRACE('This module is automatically set up for tracing'); is($output, "This module is automatically set up for tracing\n", 'delayed module has tracing enabled automatically'); sub TRACE {} Log-Trace-1.070/t/trace_filehandle.t0000644000176000001460000000347610341316164020324 0ustar tonyhtechnical00000000000000#!/usr/local/bin/perl -w # $Id: trace_filehandle.t,v 1.5 2004/12/15 15:47:35 johna Exp $ use strict; use Test::More tests => 7; # Find local libs unless running under Test::Harness BEGIN { unshift @INC, -d 't' ? 'lib' : '../lib' unless grep /\bblib\b/, @INC } require_ok('Log::Trace'); my $test_file = __FILE__; my $timestamp = qr|\d{4}(?:-\d\d){2} \d\d(?::\d\d){2}(?:\.\d{6})?|; my $message; tie *TEST_HANDLE, 'CapturingFileHandle'; import Log::Trace print => \*TEST_HANDLE; $message = 'Testing output to a filehandle'; TRACE($message); is (, "$message\n", 'message traced to supplied filehandle'); import Log::Trace print => \*TEST_HANDLE, {Level => 1}; $message = $0; TRACE({Level => 1}, $message); is (, "$message\n", 'level 1 trace to filehandle ok'); TRACE({Level => 99}, $message); is (, undef, 'level 99 message not traced ok'); import Log::Trace print => \*TEST_HANDLE, {Verbose => 0}; TRACE(join '', reverse 0..9); is (, "9876543210\n", 'verbose:0 is also not verbose'); import Log::Trace print => \*TEST_HANDLE, {Verbose => 1}; TRACE(join '', ('a'..'f')); like (, qr/\Amain::__ANON__ \(\d+\) :: abcdef\n\Z/, 'verbose:1 adds some caller information'); import Log::Trace print => \*TEST_HANDLE, {Verbose => 2}; TRACE(join '', ('a'..'f')); like (, qr/\A\Q$test_file\E: main::__ANON__ \(\d+\) \[$timestamp\] abcdef\n\Z/, 'verbose:2 adds timstamp and file info'); sub TRACE {} # A basic tied handle package CapturingFileHandle; sub TIEHANDLE { bless \do {my $string = ''}, shift; } sub PRINT { my $self = shift; $$self .= shift; } sub READLINE { my $self = shift; if (my $data = $$self) { $self->reset; return $data } else { return; } } sub reset { my $self = shift; $$self = ''; } Log-Trace-1.070/t/trace_allsubs.t0000644000176000001460000000273710341316164017675 0ustar tonyhtechnical00000000000000#!/usr/local/bin/perl -w # $Id: trace_allsubs.t,v 1.1 2004/12/03 11:43:53 simonf Exp $ use strict; use Test::More tests => 8; # Find local libs unless running under Test::Harness BEGIN { unshift @INC, -d 't' ? 'lib' : '../lib' unless grep /\bblib\b/, @INC } require_ok('Log::Trace'); use File::Basename; use File::Spec::Functions 'catdir'; BEGIN { unshift @INC, catdir(dirname(__FILE__), 'lib') } my $output; my $trace = sub { $output .= shift() . "\n"; }; require Test_DeepImport; import Log::Trace custom => $trace, {Deep => 1, Match => qr/Test_DeepImport/, AllSubs => 1}; Test_DeepImport::hello(); like($output, qr/\ATest_DeepImport::hello\(\s+\)\n/, 'hello() trace contains auto-trace'); like($output, qr/Hello World!\n\Z/, 'hello() trace contains the TRACE()'); $output = ''; Test_DeepImport::first(); like($output, qr/\ATest_DeepImport::first\(\s+\)\n/, 'call to first() was traced'); like($output, qr/Test_DeepImport::next\(\s+\)\n/, 'call to next() was traced'); like($output, qr/IN NEXT\n\Z/, 'next() called TRACE()'); # test Everywhere option $output = ''; Test_DeepImport_Without_TRACE::test(); is($output, '', 'tracing not enabled in matching package without TRACE fn.'); import Log::Trace custom => $trace, {Deep => 1, Match => qr/Test_DeepImport/, AllSubs => 1, Everywhere => 1}; Test_DeepImport_Without_TRACE::test(); like($output, qr/Test_DeepImport_Without_TRACE::test\(\s+\)\n/, 'tracing enabled in package without TRACE(), with the Everywhere option'); sub TRACE {} Log-Trace-1.070/t/trace_buffer.t0000644000176000001460000000264110341316164017473 0ustar tonyhtechnical00000000000000#!/usr/local/bin/perl -w # $Id: trace_buffer.t,v 1.5 2004/12/15 15:47:35 johna Exp $ use strict; use Test::More tests => 7; # Find local libs unless running under Test::Harness BEGIN { unshift @INC, -d 't' ? 'lib' : '../lib' unless grep /\bblib\b/, @INC } require_ok('Log::Trace'); my $test_file = __FILE__; my $timestamp = qr|\d{4}(?:-\d\d){2} \d\d(?::\d\d){2}(?:\.\d{6})?|; my ($output, $message) = ''; import Log::Trace buffer => \$output; $message = 'buffering trace output'; TRACE($message); is ($output, "$message\n", 'message appended to buffer'); $output = ''; import Log::Trace buffer => \$output, {Level => 1}; $message = $0; TRACE({Level => 1}, $message); is ($output, "$message\n", 'level 1 appended to buffer'); $output = ''; TRACE({Level => 99}, $message); is ($output, '', 'level 99 message not traced ok'); $output = ''; import Log::Trace buffer => \$output, {Verbose => 0}; TRACE(join '', reverse 0..9); is ($output, "9876543210\n", 'verbose:0 is also not verbose'); $output = ''; import Log::Trace buffer => \$output, {Verbose => 1}; TRACE(join '', ('a'..'f')); like ($output, qr/\Amain::__ANON__ \(\d+\) :: abcdef\n\Z/, 'verbose:1 adds some caller information'); $output = ''; import Log::Trace buffer => \$output, {Verbose => 2}; TRACE(join '', ('a'..'f')); like ($output, qr/\A\Q$test_file\E: main::__ANON__ \(\d+\) \[$timestamp\] abcdef\n\Z/, 'verbose:2 adds timstamp and file info'); sub TRACE {} Log-Trace-1.070/t/trace_warn.t0000644000176000001460000000267310341316164017176 0ustar tonyhtechnical00000000000000#!/usr/local/bin/perl -w # $Id: trace_warn.t,v 1.5 2004/12/15 16:50:14 johna Exp $ use strict; use Test::More tests => 7; # Find local libs unless running under Test::Harness BEGIN { unshift @INC, -d 't' ? 'lib' : '../lib' unless grep /\bblib\b/, @INC } require_ok('Log::Trace'); my $test_file = __FILE__; my $timestamp = qr|\d{4}(?:-\d\d){2} \d\d(?::\d\d){2}(?:\.\d{6})?|; my ($buffer, $message) = (''); $SIG{__WARN__} = sub {$buffer .= shift}; import Log::Trace 'warn'; $buffer = '', $message = 'The quick brown fox jumped over the lazy dog'; TRACE($message); is ($buffer, "$message\n", 'simple warn'); import Log::Trace warn => {Level => 1}; $buffer = '', $message = 'Jackdaws love my big sphinx of quartz'; TRACE({Level => 1}, $message); is ($buffer, "$message\n", 'warn at level 1'); $buffer = ''; TRACE({Level => 99}, $message); is ($buffer, '', 'warn at level 99 not traced'); $buffer = ''; import Log::Trace warn => {Verbose => 0}; TRACE(join '', reverse 0..9); is ($buffer, "9876543210\n", 'verbose:0 is also not verbose'); $buffer = ''; import Log::Trace warn => {Verbose => 1}; TRACE(join '', ('a'..'f')); like ($buffer, qr/\Amain::__ANON__ \(\d+\) :: abcdef\n\Z/, 'verbose:1 adds some caller information'); $buffer = ''; import Log::Trace warn => {Verbose => 2}; TRACE(join '', ('a'..'f')); like ($buffer, qr/\A\Q$test_file\E: main::__ANON__ \(\d+\) \[$timestamp\] abcdef\n\Z/, 'verbose:2 adds timstamp and file info'); sub TRACE {} Log-Trace-1.070/t/trace_dump.t0000644000176000001460000000445510341316164017174 0ustar tonyhtechnical00000000000000#!/usr/local/bin/perl -w # $Id: trace_dump.t,v 1.4 2005/11/24 10:53:12 tonyh Exp $ use strict; use Test::More tests => 9; # Find local libs unless running under Test::Harness BEGIN { unshift @INC, -d 't' ? 'lib' : '../lib' unless grep /\bblib\b/, @INC } require_ok('Log::Trace'); my $output; my $trace = sub { $output = shift; 0 && do { my $expected = $output; $expected =~ s/([@"\$\\])/\\$1/g; $expected =~ s/\t/\\t/g; $expected =~ s/\n/\\n/g; # print $expected, $/; } }; SKIP: { eval { require Data::Dumper }; skip "Data::Dumper not installed", 4, if $@; import Log::Trace custom => $trace; DUMP([1,2,3]); my $expected = "\$VAR1 = [\n 1,\n 2,\n 3\n];\n"; is ($output, $expected, 'simple DUMP with DD'); $output = ''; DUMP('prepended message', { somewhat => [qw(more complicated)] }, 'foo'); $expected = "prepended message: \$VAR1 = {\n somewhat => [\n 'more',\n 'complicated'\n ]\n};\n\$VAR2 = 'foo';\n"; is($output, $expected, 'dump with a comment'); $output = ''; my $dumped = DUMP("this isn't traced", [qw(it is returned instead)]); $expected = "this isn't traced: \$VAR1 = [\n 'it',\n 'is',\n 'returned',\n 'instead'\n];\n"; is($output, '', 'dump in non-void context does not TRACE'); is ($dumped, $expected, 'in non-void context it returns the DUMP'); } SKIP: { eval { require Data::Serializer }; skip "Data::Serializer not installed", 4, if $@; eval { require Data::Dumper }; skip "Data::Dumper not installed", 4, if $@; import Log::Trace custom => $trace, { Dumper => 'Data::Dumper' }; DUMP([1,2,3]); my $expected = "[1,2,3]\n"; is ($output, $expected, 'simple DUMP with DS'); $output = ''; DUMP('prepended message', { somewhat => [qw(more complicated)] }, { 'foo' => 'bar' }); $expected = "prepended message: {'somewhat' => ['more','complicated']}\n{'foo' => 'bar'}\n"; is($output, $expected, 'dump with a comment'); $output = ''; my $dumped = DUMP("this isn't traced", [qw(it is returned instead)]); $expected = "this isn't traced: ['it','is','returned','instead']\n"; is($output, '', 'dump in non-void context does not TRACE'); is ($dumped, $expected, 'in non-void context it returns the DUMP'); } sub TRACE {} sub DUMP {} Log-Trace-1.070/t/pod_coverage.t0000644000176000001460000000040210341316164017472 0ustar tonyhtechnical00000000000000#!/usr/local/bin/perl use Test::More; eval "use Test::Pod::Coverage 1.00"; plan skip_all => "Test::Pod::Coverage 1.00 required for testing POD Coverage" if $@; all_pod_coverage_ok({ also_private => [ qr/^[A-Z_]+$/, qr/^deep_import$/ ], }); #Ignore all caps Log-Trace-1.070/t/trace_custom.t0000644000176000001460000000151010341316164017526 0ustar tonyhtechnical00000000000000#!/usr/local/bin/perl -w # $Id: trace_custom.t,v 1.3 2004/11/23 14:07:31 simonf Exp $ use strict; use Test::More tests => 4; # Find local libs unless running under Test::Harness BEGIN { unshift @INC, -d 't' ? 'lib' : '../lib' unless grep /\bblib\b/, @INC } require_ok('Log::Trace'); my $output; my $trace = sub { $output = shift() . "\n"; }; my $message; import Log::Trace custom => $trace; $message = 'sending trace message to custom subroutine'; TRACE($message); is ($output, "$message\n", 'message handled by custom tracing routine'); $output = ''; import Log::Trace custom => $trace, {Level => 1}; $message = $0; TRACE({Level => 1}, $message); is ($output, "$message\n", 'level 1 appended sent to custom handler'); $output = ''; TRACE({Level => 99}, $message); is ($output, '', 'level 99 message not traced ok'); sub TRACE {} Log-Trace-1.070/README0000644000176000001460000000101210341316165015264 0ustar tonyhtechnical00000000000000Log::Trace v1.070 (c) BBC 2004. This program is free software; you can redistribute it and/or modify it under the GNU GPL. See the file COPYING in this distribution, or http://www.gnu.org/licenses/gpl.txt QUICK START: Install Log::Trace by unpacking the tarball and running the following commands in the source directory: perl Makefile.PL make make test make install Then delete the source directory tree since it's no longer needed. run 'perldoc Log::Trace' to read the full documentation. Log-Trace-1.070/lib/0000755000176000001460000000000010341316216015155 5ustar tonyhtechnical00000000000000Log-Trace-1.070/lib/Log/0000755000176000001460000000000010341316216015676 5ustar tonyhtechnical00000000000000Log-Trace-1.070/lib/Log/Trace/0000755000176000001460000000000010341316216016734 5ustar tonyhtechnical00000000000000Log-Trace-1.070/lib/Log/Trace/Manual.pod0000644000176000001460000003227210341316164020665 0ustar tonyhtechnical00000000000000=head1 NAME Log::Trace::Manual - A guide to using Log::Trace =head1 DESCRIPTION This is a brief guide to how you can use the Log::Trace module in your scripts and modules. The C documentation has a comprehensive list of options. =head1 The basics You can enable tracing by specifying the tracing target via the 'C' statement or at runtime via the C method. In most cases, you'll want to keep the code that enables tracing in a single point, usually the main script of your application. In general, modules should avoid directly setting tracing options. =head2 using Log::Trace in your scripts Here's a slightly contrived example which demonstrates the C, C, C and C functions: #!/usr/bin/perl -w use strict; use Another::Module; use Log::Trace log => '/var/log/myapp.log'; TRACE("-------- Starting archiver ---------"); TRACEF("We are going to try to archive %d items", scalar @ARGV); DUMP("List of things to archive", \@ARGV); archive_em($_) foreach(@ARGV); sub archive_em { TRACE_HERE(); my $thing = shift; unless (Another::Module::check_safe($thing)) { warn "bad chars in: $thing"; return; } rename $thing, $thing.".archive" or warn "Couldn't archive $thing: $!"; TRACE("Tried to archive $thing"); } Note the way C is imported. The import list controls where the output of the four tracing functions goes. Instead we could have done: use Log::Trace qw(warn); and the trace output would have gone to STDERR. =head2 Using Log::Trace with modules In the previous example, tracing was enabled only in the main script. Now we'll see how to enable tracing in C at the same time. First, C needs to define a C subroutine. It may also define C, C and C stubs. It can do that simply by using C. However, if C defines its own stub tracing functions, we can remove the dependency on C. package Another::Module; sub check_safe {my_routine { my $filename = shift; TRACE("Checking that '$filename' has safe characters"); return $filename =~ /^([\w.\-/]+)$/ } sub my_other_routine { TRACE_HERE(); } # tracing stubs sub TRACE {} sub TRACE_HERE {} Now, in the main script, we can change the 'C' statement so tracing will be enabled in C: use Log::Trace log => '/var/log/myapp.log', {Deep => 1}; By default, the C option will force C to export tracing functions to any modules that define a C subroutine. That includes modules that are not directly used by the main script. But this behaviour can be relaxed or tightened with other options. See L<"Deep import"> for examples. Adding C and other stub functions to your module is an I between your module and Log::Trace (in some software circles this might be given a name such as ISupportsTracing). Of course you can write other code that takes advantage of this interface completely independent of Log::Trace, e.g. use Another::Module; if($ENV{DEBUG}) { *Another::Module::TRACE = sub {print "TRACE: ".join("\t",@_)."\n"}; } =head2 Error handling Since C is designed with debugging in mind, all tracing failures are non-fatal, so allowing normal execution to continue. However, Log::Trace will report to STDERR that a problem has occurred. For example, this code: use Log::Trace file => '/myapp.log'; TRACE('Running'); print "Hello World!\n"; Will produce this output: Log::Trace: Cannot open /myapp.log : Permission denied at lib/Log/Trace.pm line . Hello World! =head1 Cookbook =head2 Enabling tracing on the command line You can invoke tracing on the command line: perl -MLog::Trace=print -e "TRACE('hello')" perl -MLog::Trace=warn -e "TRACE('hello')" perl -MLog::Trace=log,test.log -e "TRACE('hello')" However you can't apply this approach to scripts that use C or define a C stub as these will clobber C<*main::TRACE> set up by -M when they are compiled. Fortunately it is straightforward to write your command-line scripts so you can, for example, get trace output with -t and deep trace output with -T: use Log::Trace; use Getopt::Std; use MyModule; use vars qw($opt_t $opt_T); getopts("tT"); # tracing import Log::Trace 'print' if $opt_t; import Log::Trace 'print' => {Deep => 1} if $opt_T; do_something_involving_tracing(); =head2 Sending TRACE output to browser in CGI Whilst tracing to a log file or C is tolerable for CGIs, it's often far more convenient to return the tracing information back to the browser of the client-side developer. use CGI; use constant DEV_SERVER => 1; my $trace_buffer; if(DEV_SERVER && CGI::param('Tracing')) { require Log::Trace; import Log::Trace buffer => \$trace_buffer, {Deep => 1}; } my $output = do_everything(); print CGI::header(); print $output; if (DEV_SERVER && $trace_buffer) { print "\n\n", "
", CGI::escapeHTML($trace_buffer), "
"; } You should remember to change the C constant when releasing the CGI to a production environment. =head2 Log levels C can filter the tracing output by referring to the logging level. The logging level is defined when you enable tracing. C doesn't impose any conventions on the levels. The default levels implementation requires that the levels be numeric, but that can be overriden. In the simplest case, you can specify the level as a threshold value: use Log::Trace print => {Level => 3}; In this example, all trace messages at level C<3> or below will be output. You can also specify a list of valid levels: use Log::Trace print => {Level => [0 .. 3, 7]}; All the tracing functions accept a hash as an optional first parameter where you can specify the level for that trace message. E.g.: TRACE({Level => 4}, "This is a warning"); TRACEF({Level => 6}, "%d items found", scalar @items); TRACE_HERE({Level => 10}); DUMP({Level => 8}, 'Retrieved data', \%data); C is designed to accept a hash as its first parameter, but there may be cases where you wish to dump a hash that contains a C key. In those cases, you can take advantage of the return value of C: my $dumped = DUMP({Level => 1, Health => '0.68'}); TRACE({Level => 8}, 'Game stats', $dumped); If you specify a tracing level when you enable C, then tracing messages that do not specify a level will not be output, unless you include C in the trace levels: use Log::Trace print => {Level => [3, undef]}; TRACE("This is level undef, and will be output"); TRACE({Level => 3}, "This will also be output"); TRACE({Level => 8}, "... but this won't"); Here are some sample tracing levels (borrowed from Log::Agent) which you can use as a guide: 0 emergency 1 alert 2 critical 3 error 4 warning 6 notice 8 info 10 debug =head2 Fine-tuning deep import Occasionally you won't want to see the trace output from ALL your modules in your application. For example your application may give a module a huge data structure or call it in a long loop. The C option allows you to mask out one or more modules. use Log::Trace warn => {'Deep' => 1, 'Exclude' => 'MyVerboseModule'}; or use Log::Trace warn => {'Deep' => 1, 'Exclude' => ['MyVerboseModule', 'Another::Module']}; Conversely you can use an opt-in approach rather than opt-out. The C option allows a regular expression to be used to select which packages are initialised by Log::Trace. For example: use Log::Trace print => {'Deep' => 1, 'Match' => qr/^MySubSystem::/}; =head1 Advanced features =head2 Issues with the order of importing When the Deep or Everywhere options are used, Log::Trace is imported into all the packages which have been compiled so far. use Package::Foo; use Log::Trace ('print' => {Deep => 1}); use Package::Bar; #Compiled after Log::Trace is imported In this example, the TRACE function in Package::Bar won't be overridden. It's trivial to swap the order in the example above so that Log::Trace is the last module used, but suppose you have a module (such as a factory) that loads others on demand: package MyApp::Reader; sub new { my $package = shift; my $type = shift; die unless($type =~ /^MyApp::Reader::\w+$/); eval "require $type"; die($@) if($@); return $type->new(@_); } How do you ensure Log::Trace gets imported into the backend MyApp::Reader::* modules (without polluting all your modules with Log::Trace::import calls)? =over 4 =item Using the (experimental) AutoImport feature The AutoImport feature will override C so that from now on any modules that are loaded will have the Log::Trace import run against them: use Log::Trace('log' => '/var/log/myapp.log', {'Deep' => 1, 'AutoImport' => 1}); This only works with recent versions of perl (see the ENVIRONMENT NOTES in L). =item Getting the factory to wire the components it produces A more "low-tech" approach that works with all versions of perl is to get the factory to attach the stub functions of the modules it loads to whatever its own stub functions have been wired to by the caller. package MyApp::Reader; sub new { my $package = shift; my $type = shift; die unless($type =~ /^MyApp::Reader::\w+$/); eval "require $type"; die($@) if($@); # Wire the component we've created into whatever # our TRACE etc function has been wired to *{"$type\::TRACE"} = \&MyApp::Reader::TRACE; *{"$type\::DUMP"} = \&MyApp::Reader::DUMP; return $type->new(@_); } =back =head2 Custom TRACE functions If C, C, C, a file, a file handle, or a buffer is not to your liking then the custom method is for you. Suppose you want to send your Log::Trace output into a database: our $sth; $sth = setup_logging_statement(); use Log::Trace custom => \&log_to_database; sub log_to_database { #TRACE can get any number of arguments my $message = join(",", @_); $sth->execute($message); } =head2 Controlling DUMP output By default, Data::Dumper is used with a fixed set of options for DUMP output. You can choose a different serialiser using the C option: import Log::Trace('print' => {Dumper => "YAML"}}); Where the string refers to a Data::Serializer::* backend. You can also control the options passed to the Data::Serializer backend (and thus customise the DUMP output) by passing a hashref of Data::Serializer contructor options: import Log::Trace('print' => {Dumper => { serializer => 'XML::Dumper', options => { dtd => 'path/to/my.dtd' } }}); At the time of writing, not all the configuration options of the underlying serialisation modules are exposed via their Data::Serializer wrappers. If you find this a limitation, please contribute patches to extend these modules as this will benefit a number of other modules that make use of the Data::Serializer API. =head2 Execution path vs. profiling You can use the C tracing option to trace the execution path through each subroutine. By default C only wraps each subroutine in packages with C defined. You can force it to do it to all modules using the C option. The following: use Data::Dumper; use Log::Trace print => {AllSubs => 1, Verbose => 1, Everywhere => 1, Exclude => 'Config'}; Data::Dumper->Dumpperl([[4]]); generates the output: main::__ANON__ (3) :: Data::Dumper::Dumpperl( ) Data::Dumper::Dumpperl (3) :: Data::Dumper::new( ) Data::Dumper::Dumpperl (3) :: Data::Dumper::_dump( Data::Dumper, ... ) Data::Dumper::_dump (205) :: overload::StrVal( ARRAY, ... ) overload::StrVal (239) :: overload::OverloadedStringify( ARRAY, ... ) overload::OverloadedStringify (92) :: overload::mycan( ) overload::OverloadedStringify (92) :: overload::ov_method( ) overload::OverloadedStringify (92) :: overload::mycan( ) overload::OverloadedStringify (92) :: overload::ov_method( ) overload::OverloadedStringify (92) :: overload::mycan( ) overload::OverloadedStringify (92) :: overload::ov_method( ) overload::OverloadedStringify (92) :: overload::mycan( ) overload::OverloadedStringify (92) :: overload::ov_method( ) Data::Dumper::_dump (205) :: Data::Dumper::_dump( Data::Dumper, ... ) (eval) (0) :: Data::Dumper::DESTROY( Data::Dumper, ... ) =head2 Targeting one module You may wonder "How do I trace what's going on in module Acme::Foo I downloaded from CPAN that isn't Log::Trace enabled?". Assuming the module doesn't have any other kind of tracing that you can hook into, all you can do is use the C approach. Assuming that's OK, you can restrict this to just the offending module with: use Log::Trace print => {AllSubs => 1, Everywhere => 1, Match => qr/^Acme:Foo$/}; =head2 Avoiding performance penalty Although the trace stubs don't do anything, they do incur a small function call overhead. If this performance hit is unacceptable, you can use a constant to enable/disable all the C statements in your code. The test for the constant value will be optimised out at compile time so no runtime overhead is incurred if the constant has a false value: package ThrashMe; use constant TRACING_ENABLED => 1; #Set to zero to optimise sub performance_critical { TRACE("this may slow things down") if(TRACING_ENABLED); do_stuff(); } sub TRACE{} 1; =head1 REVISION $Revision: 1.9 $ =cut Log-Trace-1.070/lib/Log/Trace.pm0000644000176000001460000006034710341316164017306 0ustar tonyhtechnical00000000000000package Log::Trace; use Carp; #Hires times if available eval { require Time::HiRes; }; use vars qw($VERSION @EXPORT); @EXPORT = qw(TRACE_HERE TRACEF); # TRACE, DUMP use strict qw(subs vars); use Fcntl ':flock'; $VERSION = sprintf"%d.%03d", q$Revision: 1.70 $ =~ /: (\d+)\.(\d+)/; ################################################# # Importing ################################################# *{"_debug"} = 0 ? sub { warn __PACKAGE__ . " @_\n" } : sub {}; #Import into calling packages sub import { my $pkg = shift; my $callpkg = caller(0); my @args = @_; if (ref $args[1] eq 'HASH') { # e.g. 'import (print => {Verbose => 1}) @args = ($args[0], undef, @args[1..$#args]) } $pkg->_import($callpkg, @args); } sub deep_import # deprecated. use 'import Log::Trace {Deep => 1, ...} ...' { my $pkg = shift; my $callpkg = caller(0); my $params = ref $_[0] ? shift : {}; $params->{Deep} = 1; push @_, undef if @_ == 1; # deep_import 'print'; $pkg->_import($callpkg, @_, $params); } sub TRACEF { my $callpkg = caller(); my $trace = *{"$callpkg\::TRACE"}; my $params = ref $_[0] ? shift : {}; my $format = shift; $trace->($params, sprintf($format, @_)); } sub TRACE_HERE { my $callpkg = caller(); my $params = ref $_[0] ? shift : {}; # see 'perldoc -f caller' # calling caller() from the DB package stores subroutine args in @DB::args my @caller; do { package DB; @caller = caller(1); @caller = caller(0) unless $caller[0]; }; my $sub = $caller[3]; # the subroutine that called TRACE_HERE my ($file, $line) = (caller(0))[1,2]; # the location of the TRACE_HERE my $trace = *{"$callpkg\::TRACE"}; shift @DB::args if @DB::args && "$DB::args[0]" eq "$params"; $trace->($params, "In $sub(".join(",", @DB::args).") - line $line of $file"); } ################################################# # Exporting ################################################# sub _import { my $pkg = shift; my (@packages) = shift; my ($target, $arg, $params) = @_; $target = '' unless defined $target; if ($params->{Deep}) { # extend the package list push @packages, _deep_import_packages($params->{Everywhere}); } if ($params->{AutoImport}) { # override the default require() to catch new modules being loaded _install_require($pkg, $params, $target, $arg) } # lookup: valid target > TRACE sub. These are also closures around '$arg' my %import_targets = ( 'print' => sub {_log_to_fh($arg, _log_normal(@_))}, 'print-verbose' => sub {_log_to_fh($arg, _log_verbose(@_))}, 'print-debug' => sub {_log_to_fh($arg, _log_debug(@_))}, 'warn' => sub {warn _log_normal(@_)}, 'warn-verbose' => sub {warn _log_verbose(@_)}, 'warn-debug' => sub {warn _log_debug(@_)}, 'buffer' => sub {$$arg .= _log_normal(@_)}, 'buffer-verbose' => sub {$$arg .= _log_verbose(@_)}, 'buffer-debug' => sub {$$arg .= _log_debug(@_)}, 'file' => sub {_log_to_file($arg, _log_normal(@_))}, 'file-verbose' => sub {_log_to_file($arg, _log_verbose(@_))}, 'file-debug' => sub {_log_to_file($arg, _log_debug(@_))}, 'log' => sub {_log_to_file($arg, _log_debug(@_))}, 'syslog' => sub {_log_to_syslog($arg, _log_normal(@_))}, 'syslog-verbose' => sub {_log_to_syslog($arg, _log_verbose(@_))}, 'syslog-debug' => sub {_log_to_syslog($arg, _log_debug(@_))}, 'custom' => $arg, ); my $suffix = ''; $params->{Verbose} = 0 unless defined $params->{Verbose}; if ($params->{Verbose} == 1) { $suffix = '-verbose'; } elsif ($params->{Verbose} == 2) { $suffix = '-debug'; } $target = $import_targets{$target.$suffix} ? $target.$suffix : $target; _debug("Initialising target: $target"); foreach my $export_to (@packages) { # Check whether to export functions to the package my $match = defined $params->{Match} ? $params->{Match} : '.'; next unless $export_to =~ /$match/; my %exclude; if (my $excl = $params->{Exclude}) { %exclude = map {$_ => 1} ref $excl eq 'ARRAY' ? @$excl : $excl; } $exclude{+__PACKAGE__} = 1; # exclude ourselves next if $exclude{$export_to}; _debug("Exporting target:$target to $export_to"); # set up the TRACE/DUMP functions my ($trace, $dump); if ($target && $import_targets{$target}) { $trace = _trace_maker($export_to, $params, $import_targets{$target}); $dump = _dump_maker($export_to, $params, $trace); } else { # Just export stub functions $trace = $dump = sub {}; carp "$pkg imported with unknown target $target" if $target; } # Now export ... __replace_subroutine($export_to, 'TRACE', $trace); __replace_subroutine($export_to, 'DUMP', $dump); __replace_subroutine($export_to, 'TRACEF', \&TRACEF); __replace_subroutine($export_to, 'TRACE_HERE', \&TRACE_HERE); if ($params->{AllSubs}) { # wrap all functions in package with calls to TRACE _debug("wrapping all functions in $export_to"); _wrap_functions($export_to, $trace); } } } sub __replace_subroutine { my ($package, $sub, $coderef) = @_; if (defined \&{"${package}::$sub"}) { # quietly remove existing stub function # This avoids unsightly "subroutine foo redefined" warnings # 'no warnings "redefine"' doesn't work pre perl 5.6 eval "undef \$${package}::{'$sub'}"; } *{"${package}::$sub"} = $coderef; } sub _trace_maker { my ($package, $params, $trace_sub) = @_; my $trace_level = $params->{Level}; return sub { local $@; # in-case TRACE is called from &DESTROY my $rv; eval { my $level = shift->{Level} if ($_[0] && ref $_[0] eq 'HASH'); return unless _evaluate_level($package, $trace_level, $level); $rv = 1 && $trace_sub->(@_); }; if ($@) { warn __PACKAGE__ . " : $@"; } return $rv; } } sub _dump_maker { my ($package, $params, $trace_sub) = @_; my $trace_level = $params->{Level}; return sub { # always return the dumped data regardless of level unless called in # void context my $context = wantarray(); local $@; # in-case DUMP is called from &DESTROY my $rv; eval { return $rv = _dump($params, @_) if defined $context; my $level = undef; if ($_[0] && ref $_[0] eq 'HASH' && defined $_[0]{Level}) { $level = shift->{Level}; } return unless _evaluate_level($package, $trace_level, $level); my $dumped = _dump($params, @_); $rv = 1 && $trace_sub->($dumped); }; if ($@) { warn __PACKAGE__ . " : $@"; } return $rv; } }; # returns a list of packages to export trace functions to sub _deep_import_packages { my $all_packages = shift; # Build the list of packages my @packages; foreach my $module (@{_list_all_packages()}) { next if $module eq __PACKAGE__; next unless $all_packages || defined (&{"$module\::TRACE"}); push @packages, $module; } return @packages; } my %_autowrap; sub _wrap_functions { my ($package, $trace) = @_; return if $_autowrap{$package}; $_autowrap{$package} = {} unless defined $_autowrap{$package}; my $symbols = \%{$package . '::'}; # wrap coderefs in the caller's symbol table foreach my $typeglob (keys %$symbols) { # skip TRACE/DUMP and other potential deep recursions next if $typeglob =~ /^(?:TRACE(?:F|_HERE)?|DUMP|AUTOLOAD)$/; # only wrap code references my $sub = *{$symbols->{$typeglob}}{CODE}; next unless (defined $sub and defined &$sub); # skip if sub is already wrapped next if $_autowrap{$package}{$typeglob}++; # define wrapped subroutine body my $sub_body = <<'WRAPPED'; my ($name) = "${package}::$typeglob"; my ($callpkg, $file, $line) = caller(1); my $arg = $_[0] && ref($_[0]) ? ref($_[0]) . ', ...' : ""; $trace->( "${name}( $arg )" ); goto &$sub WRAPPED # wrap subroutine, preserving prototypes my $wrapped_sub; if(defined (my $proto = prototype($sub))) { $wrapped_sub = eval "sub ($proto) { $sub_body }"; } else { $wrapped_sub = eval "sub { $sub_body }"; } __replace_subroutine($package, $typeglob, $wrapped_sub); } } # return a list of all defined packages in the symbol table # we could use %INC, but we'd miss packages that are defined in other modules sub _list_all_packages { my ($package) = @_; $package = '' unless defined $package; my @packages; # this is a recursive look in the symbol table: # %main:: # CGI:: # Cookie:: # Data:: # Dumper:: # ... my %symbols = %{$package . "::"}; foreach my $module (keys %symbols) { next unless $module =~ s/::$//; # ignore 'main' (deep recursion) and all invalid package names next if !$package && ($module eq 'main' || $module !~ /^[a-zA-Z_]\w*$/); my $prefix = $package ? $package . '::' : ''; # Add this module push @packages, $prefix . $module; # and recurse to sub-packages push @packages, @{_list_all_packages($prefix . $module)}; } return \@packages; } # Override the built-in require() # This is tricky because these are treated differently by perl: # 1. require CGI # 2. require "CGI" # We have no way of distinguishing the two, so we make a best guess # # This only works since perl 5.6.1 # # See 'perlsub' for more information about overriding built-ins sub _install_require { my ($pkg, $params, $target, @args) = @_; # CORE::require has prototype(;$), but we get "bareword foo not allowed" # errors if we use that. prototype(*) works though my $require = sub (*) { local $^W; my $what = shift; return 1 if $INC{$what}; _debug("require $what"); my $package; if ($what =~ /^v?[\d_.]+$/) { # take advantage of UNIVERSAL->VERSION($require) for a portable # version check local $_Log::Trace::PerlVersion::VERSION = $]; eval {_Log::Trace::PerlVersion->VERSION($what)}; if (my $error = $@) { $error =~ s/_Log::Trace::PerlVersion/Perl/; die $error; #rethrow exception } return 1; } elsif ($what =~ /(.*)\.pm$/) { # looks like a module name, get the main package from the filename # (perl 5.8 & ActivePerl 5.6.1) ($package = $1) =~ s{/}{::}g; } elsif ($what =~ /^[a-zA-Z_]\w*(?:::\w+)*$/i) { # package name: vanilla perl 5.6.1, 5.6.2 $package = $what; ($what = "$what.pm") =~ s{::}{/}g; } my $rv = CORE::require $what; if ($rv && $package) { # import Log::Trace into package return unless $params->{Everywhere} || defined (&{"$package\::TRACE"}); $pkg->_import($package, $target, @args, $params); } return $rv; }; # Override global require, silencing "... used only once" warnings *CORE::GLOBAL::require = *CORE::GLOBAL::require = $require; } # Returns caller info for exported functions sub __caller { # We need to look several frames back, so we keep going until we find # something from outside this package my @caller; for (1 .. 8) { my @c = caller($_); last unless defined $c[0]; @caller = @c; last unless $caller[0] eq __PACKAGE__ || $caller[3] =~ /^@{[__PACKAGE__]}\::/o; } # because we don't seem to get a call frame for main::__ANON__ $caller[0] = 'main' if ($caller[0] eq __PACKAGE__); $caller[3] =~ s/^@{[__PACKAGE__]}\::.*/main::__ANON__/; return @caller; } ################################################# # TRACE guts ################################################# sub _evaluate_level { my ($callpkg, $imported_level, $trace_level) = @_; return 1 if ! defined $imported_level; if (ref $imported_level eq 'CODE') { return $imported_level->($callpkg, $trace_level); } elsif (ref $imported_level eq 'ARRAY') { for (@$imported_level) { return 1 if (! defined($_) && ! defined($trace_level)); next unless defined($trace_level) && defined($_);; return 1 if $_ == $trace_level; } } elsif (!ref $imported_level) { return unless defined $trace_level; return $imported_level >= $trace_level; } } sub _log_normal { return join(",", @_)."\n"; } sub _log_verbose { my ($pack,$file,$line,$sub) = __caller(); return "$sub ($line) :: " . join( ", ", @_ ) . "\n"; } sub _log_debug { my ($pack,$file,$line,$sub) = __caller(); my $timestamp = _timestamp(); return "$file: $sub ($line) [$timestamp] " . join( ", ", @_ ) . "\n"; } sub _log_to_fh { my ($fh, @output) = @_; $fh = \*STDOUT unless $fh; print $fh @output; } sub _log_to_file { my $filename = shift; my ($pack,$file,$line,$sub) = __caller(); local *LOG_FILE; if (open (LOG_FILE, ">> $filename")) { if (eval {flock LOG_FILE, LOCK_EX|LOCK_NB}) { print LOG_FILE @_; flock LOG_FILE, LOCK_UN; close LOG_FILE; } else { die "couldn't get lock on $filename : $!"; } } else { die "Cannot open $filename : $!"; } } sub _log_to_syslog { my ($priority) = shift || 'debug'; return unless eval {require Sys::Syslog}; Sys::Syslog::openlog(__PACKAGE__, 'pid'); my $rv = Sys::Syslog::syslog($priority, join ",", @_); Sys::Syslog::closelog(); return $rv; } sub _dump { my ($params, @args) = @_; my $msg = ref $args[0] ? '' : shift @args; $msg .= ": " if($msg && @args); my $type; eval { if ($params->{Dumper}) { $type = 'Data::Serializer'; require Data::Serializer; my $params = ref $params->{Dumper} ? $params->{Dumper} : { serializer => $params->{Dumper} }; my $serialiser = Data::Serializer->new(%$params); for (@args) { $msg .= $serialiser->raw_serialize($_) . "\n"; } } else { $type = 'Data::Dumper'; require Data::Dumper; # avoid 'used $var only once' warning local $Data::Dumper::Indent; local $Data::Dumper::Sortkeys; local $Data::Dumper::Quotekeys; $Data::Dumper::Indent = 1; $Data::Dumper::Sortkeys = 1; $Data::Dumper::Quotekeys = 0; $msg .= Data::Dumper::Dumper(@args); } }; die "$type not available: $@" if $@; return $msg; } sub _gettimeofday() { return Time::HiRes::gettimeofday() if $INC{'Time/HiRes.pm'}; return (time(), undef); } #Provide localtime-style timestamp with microsecond resolution if Time::HiRes #is available sub _timestamp { my ($epoch, $usec) = _gettimeofday(); my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($epoch); $year+=1900; $mon+=1; my $stamp = sprintf("%4d-%02d-%02d %02d:%02d:%02d",$year,$mon,$mday,$hour,$min,$sec); $stamp .= sprintf(".%.6d",$usec) if(defined $usec); return $stamp; } 1; =head1 NAME Log::Trace - provides a unified approach to tracing =head1 SYNOPSIS # The tracing targets use Log::Trace; # No output use Log::Trace 'print'; # print to STDOUT use Log::Trace log => '/var/log/foo.log'; # Output to log file use Log::Trace print => { Level => 3 }; # Switch on/off logging with a constant use Log::Trace; import Log::Trace ('log' => LOGFILE) if TRACING; # Set up tracing for all packages that advertise TRACE use Foo; use Bar; use Log::Trace warn => { Deep => 1 }; # Sets up tracing in all subpackages excluding Foo use Log::Trace warn => {Deep => 1, 'Exclude' => 'Foo'}; # Exported functions TRACE("Record this..."); TRACE({Level => 2}, "Only shown if tracing level is 2 or higher"); TRACEF("A la printf: %d-%.2f", 1, 2.9999); TRACE_HERE(); # Record where we are (file, line, sub, args) DUMP(\@loh, \%hoh); # Trace out via Data::Dumper DUMP("Title", \@loh); # Trace out via Data::Dumper my $dump = DUMP(@args); # Dump is returned without being traced =head1 DESCRIPTION A module to provide a unified approach to tracing. A script can C mode E )> to set the behaviour of the TRACE function. By default, the trace functions are exported to the calling package only. You can export the trace functions to other packages with the C option. See L<"OPTIONS"> for more information. All exports are in uppercase (to minimise collisions with "real" functions). =head1 FUNCTIONS =over 4 =item TRACE(@args) Output a message. Where the message actually goes depends on how you imported Log::Trace (See L<"Importing/enabling Log::Trace">) The first argument is an optional hashref of options: TRACE('A simple message'); vs: TRACE({ Level => 2.1 }, 'A message at a specified trace level'); =item TRACEF($format, @args) C equivalent of TRACE. Also accepts an optional hashref: TRACEF('%d items', scalar @items); TRACEF({ Level => 5 }, '$%1.2d', $value); =item DUMP([$message,] @args) Serialises each of @args, optionally prepended with $message. If called in a non-void context, DUMP will return the serialised data rather than TRACE it. This is useful if you want to DUMP a datastructure at a specific tracing level. DUMP('colours', [qw(red green blue)]); # outputs via TRACE my $dump = DUMP('colours', [qw(red green blue)]); # output returned =item TRACE_HERE() TRACEs the current position on the call stack (file, line number, subroutine name, subroutine args). TRACE_HERE(); TRACE_HERE({Level => 99}); =back =head1 Importing/enabling Log::Trace =over 4 =item import($target, [$arg], [\%params]) Controls where TRACE messages go. This method is called automatically when you call C<'use Log::Trace;'>, but you may explicitly call this method at runtime. Compare the following: use Log::Trace 'print'; which is the same as BEGIN { require Log::Trace; Log::Trace->import('print'); } Valid combinations of C<$target> and C are: =over 4 =item print =E $filehandle Prints trace messages to the supplied C<$filehandle>. Defaults to C if no file handle is specified. =item warn Prints trace messages via Cs to C. =item buffer =E \$buffer Appends trace messages to a string reference. =item file =E $filename Append trace messages to a file. If the file doesn't exist, it will be created. =item log =E $filename This is equivalent to: use Log::Trace file => $filename, {Verbose => 2}; =item syslog =E $priority Logs trace messages to syslog via C, if available. You should consult your syslog configuration before using this option. The default C<$priority> is 'C', and the C is set to C. You can configure the C, but beyond that, you can implement your own syslogging via the C trace target. =item custom => \&custom_trace_sub Trace messages are processed by a custom subroutine. E.g. use Log::Trace custom => \&mylogger; sub mylogger { my @messages = @_; foreach (@messages) { # highly sensitive trace messages! tr/a-zA-Z/n-za-mN-ZA-M/; print; } } =back The import C<\%params> are optional. These two statements are functionally the same: import Log::Trace print => {Level => undef}; import Log::Trace 'print'; See L<"OPTIONS"> for more information. B If you use the C tracing option, you should be careful about supplying a subroutine named C. =back =head1 OPTIONS =over 4 =item AllSubs =E BOOL Attaches a C statement to all subroutines in the package. This can be used to track the execution path of your code. It is particularly useful when used in conjunction with C and C options. B Anonymous subroutines and C are not Cd. =item AutoImport =E BOOL By default, C will only set up C routines in modules that have already been loaded. This option overrides C so that modules loaded after C can automatically be set up for tracing. B: This is an experimental feature. See the ENVIRONMENT NOTES for information about behaviour under different versions of perl. This option has no effect on perl E 5.6 =item Deep =E BOOL Attaches C to all packages (that define a TRACE function). Any TRACEF, DUMP and TRACE_HERE routines will also be overridden in these packages. =item Dumper =E Data::Serializer backend Specify a serialiser to be used for DUMPing data structures. This should either be a string naming a Data::Serializer backend (e.g. "YAML") or a hashref of parameters which will be passed to Data::Serializer, e.g. { serializer => 'XML::Dumper', options => { dtd => 'path/to/my.dtd' } } Note that the raw_serialise() method of Data::Serializer is used. See L for more information. If you do not have C installed, leave this option undefined to use the C natively. Default: undef (use standalone Data::Dumper) =item Everywhere =E BOOL When used in conjunction with the C option, it will override the standard behaviour of only enabling tracing in packages that define C stubs. Default: false =item Exclude =E STRING|ARRAY Exclude a module or list of modules from tracing. =item Level =E NUMBER|LIST|CODE Specifies which trace levels to display. If no C is defined, all TRACE statements will be output. If the value is numeric, only TRACEs that are at the specified level or below will be output. If the value is a list of numbers, only TRACEs that match the specified levels are output. The level may also be a code reference which is passed the package name and the TRACE level. It mst return a true value if the TRACE is to be output. Default: undef =item Match =E REGEX Exports trace functions to packages that match the supplied regular expression. Can be used in conjunction with C. You can also use C as an exclusion method if you give it a negative look-ahead. For example: Match => qr/^(?!Acme::)/ # will exclude every module beginning with Acme:: and Match => qr/^Acme::/ # does the reverse Default: '.' # everything =item Verbose =E 0|1|2 You can use this option to prepend extra information to each trace message. The levels represent increasing levels of verbosity: 0: the default*, don't add anything 1: adds subroutine name and line number to the trace output 2: As [1], plus a filename and timestamp (in ISO 8601 : 2000 format) This setting has no effect on the C or C targets. * I =back =head1 ENVIRONMENT NOTES The AutoImport feature overrides C which requires perl 5.6, but you may see unexpected errors if you aren't using at least perl 5.8. The AutoImport option has no effect on perl E 5.6. In mod_perl or other persistent interpreter environments, different applications could trample on each other's C routines if they use Deep (or Everywhere) option. For example application A could route all the trace output from Package::Foo into "appA.log" and then application B could import Log::Trace over the top, re-routing all the trace output from Package::Foo to "appB.log" for evermore. One way around this is to ensure you always import Log::Trace on every run in a persistent environment from all your applications that use the Deep option. We may provide some more tools to work around this in a later version of C. C has not been tested in a multi-threaded application. =head1 DEPENDENCIES Carp Time::HiRes (used if available) Data::Dumper (used if available - necessary for meaningful DUMP output) Data::Serializer (optional - to customise DUMP output) Sys::Syslog (loaded on demand) =head1 RELATED MODULES =over 4 =item Log::TraceMessages C is similar in design and purpose to C. However, it only offers a subset of this module's functionality. Most notably, it doesn't offer a mechanism to control the tracing output of an entire application - tracing must be enabled on a module-by-module basis. C also offers control over the output with the trace levels and supports more output targets. =item Log::Agent C offers a procedural interface to logging. It strikes a good balance between configurability and ease of use. It differs to C in a number of ways. C has a concept of channels and priorities, while C only offers levels. C also supports tracing code execution path and the C import option. C trades a certain amount of configurability for increased ease-of use. =item Log::Log4Perl A feature rich perl port of the popular C library for Java. It is object-oriented and comprised of more than 30 modules. It has an impressive feature set, but some people may be frightened of its complexity. In contrast, to use C you need only remember up to 4 simple functions and a handful of configuration options. =back =head1 SEE ALSO L - A guide to using Log::Trace =head1 VERSION $Revision: 1.70 $ on $Date: 2005/11/01 11:32:59 $ by $Author: colinr $ =head1 AUTHOR John Alden and Simon Flack with some additions by Piers Kent and Wayne Myers =head1 COPYRIGHT (c) BBC 2005. This program is free software; you can redistribute it and/or modify it under the GNU GPL. See the file COPYING in this distribution, or http://www.gnu.org/licenses/gpl.txt =cut Log-Trace-1.070/MANIFEST0000644000176000001460000000067310341316202015541 0ustar tonyhtechnical00000000000000Changes COPYING Makefile.PL MANIFEST MANIFEST.SKIP README lib/Log/Trace.pm lib/Log/Trace/Manual.pod t/trace_allsubs.t t/trace_buffer.t t/trace_custom.t t/trace_deep.t t/trace_dump.t t/trace_file.t t/trace_filehandle.t t/trace_functions.t t/trace_levels.t t/trace_print.t t/trace_warn.t t/pod_coverage.t t/lib/Test_DeepImport.pm t/lib/Test_DelayedImport.pm t/pod.t META.yml Module meta-data (added by MakeMaker) Log-Trace-1.070/META.yml0000644000176000001460000000052410341316201015653 0ustar tonyhtechnical00000000000000# http://module-build.sourceforge.net/META-spec.html #XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX# name: Log-Trace version: 1.070 version_from: lib/Log/Trace.pm installdirs: site requires: Test::More: 0 distribution_type: module generated_by: ExtUtils::MakeMaker version 6.25 Log-Trace-1.070/Changes0000644000176000001460000000021010341316164015675 0ustar tonyhtechnical00000000000000Tue Nov 22 12:23:46 2005 - 1.070 Minor bug fix to _timestamp routine. Mon Jan 31 10:21:11 2005 - 1.069 - Public release (CPAN) Log-Trace-1.070/Makefile.PL0000644000176000001460000000056710341316164016373 0ustar tonyhtechnical00000000000000use ExtUtils::MakeMaker; WriteMakefile( NAME => 'Log::Trace', VERSION_FROM => 'lib/Log/Trace.pm', PREREQ_PM => { Test::More => 0, }, ABSTRACT_FROM => 'lib/Log/Trace.pm', AUTHOR => 'British Broadcasting Corporation', ); Log-Trace-1.070/COPYING0000644000176000001460000004313110341316164015446 0ustar tonyhtechnical00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. Log-Trace-1.070/MANIFEST.SKIP0000644000176000001460000000020610341316164016305 0ustar tonyhtechnical00000000000000^blib ^bak ~$ (?:^|/)[Mm]akefile(?:\.old)?$ (?:^|/)pm_to_blib$ ^Log-Trace-\d+\.\d+\.(zip|tar\.gz|tgz)$ [Cc][Vv][Ss]/ t/trace_syslog.t