stop being a caveman! use log::log4perl;

Post on 05-Feb-2016

39 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

Stop Being A Caveman! use Log::Log4perl;. by Eric Andreychek eric@openthought.net. A long time ago... in a galaxy far, far away people debugged Perl code using print and warn statements some of these Perl coders even used the legendary and infamous Perl debugger - PowerPoint PPT Presentation

TRANSCRIPT

Stop Being A Caveman!use Log::Log4perl;

by Eric Andreychekeric@openthought.net

A long time ago...

in a galaxy far, far away

people debugged Perl code

using print and warn statements

some of these Perl coders even used

the legendary and infamous Perl debugger

these hard working Perl coders were known as ...

Cavemen

Put These To Rest

Log4perl can help us make ideas like print, warn, and the Perl debugger artifacts of history

Lets get this part out of the way...

● Log4perl lets you log stuff

● In Perl

● And it's really cool

All You Need...

● Define a logging level

● Define where and what to log

● Use the (so-simple-it's-silly) interface

Log Levels

fatal

error

warn

info

debug

Highest

Lowest

Logger Objects

● Logger objects ($logger) do the work– Retrieved using Log4perl's get_logger() method

● Logging may occur in any sub in your program– No need to pass $logger to each sub

● Messy and Annoying

● Log4perl stores the logger object internally– A call to get_logger() returns that stored object

● Called a singleton

Logging Example

$logger = get_logger();

# Logs when warn, info, or debug

$logger->warn(“Help me Obi-Wan, you're my only hope”);

# Remains dormant when error or fatal!

Initializing Log4perl

# To set up logging, you could use the following:

use Log::Log4perl qw( :easy );

Log::Log4perl->easy_init( $ERROR );

Not!We don't need no steenking “easy”

# To set up logging, you could use the following:

use Log::Log4perl qw( :easy );

Log::Log4perl->easy_init( $ERROR );

Initializing Log4perl: /The Real (:?wo)?man's Way/

# Darth.pl

use Darth();use Log::Log4perl();

$config = get_my_config();Log::Log4perl::init( $config );

Using Log4perl

package Darth;

use Log::Log4perl qw( get_logger );

sub new { my ( $class, $name ) = @_; my $logger = get_logger();

$logger->debug(“Creating Darth $name”);

return bless( { darth => $name }, $class );}

Using Log4perl

sub get_lightsaber { my $self = shift; my $logger = get_logger();

$logger->debug(“Fetching lightsaber from the armory”);

my $saber = $self->_saber_type || “Red Single-Sided Saber”; $logger->warn(“Giving Darth his light saber: $saber”);

return $saber;}

Log4perl Return Values

● Logging functions return the number of places they were logged– If message logged: return true– If no message logged: return undef– $total = $logger->warn( $message );

● Being at the WARN level doesn't necessarily mean $logger->warn($message) will be logged– Log4perl could suppress the message– A suppressed message would return undef

Using Log4perl: Errors# Later on in darth.plmy $saber = $darth->get_lightsaber;

unless ( $saber ) { my $error = “Nothing to defend against the good guys with, ” .

“not even an electronic sausage!”

# This will often work, but tends to be a really bad idea $logger->fatal( $error ) && die( $error )

# This utility method doesn't depend on the return value of # $logger->fatal, it will always die $logger->logdie( $error )}

Future of Internet Keyboards

Configuring The Beast

● Configuring Log4perl: TMTOWTDI– Perl– Config files– Both

Config Files (my favorite)

use Log::Log4perl qw( get_logger );

Log::Log4perl::init( “log4perl.conf” );

my $logger = get_logger();

Config File ExampleContents of log4perl.conf

log4perl.logger = INFO, Screen

log4perl.appender.Screen = Log::Dispatch::Screen

log4perl.appender.Screen.layout = SimpleLayout

Config File: CategoryThis config applies to my what?

log4perl.logger = INFO, Screen

log4perl.appender.Screen = Log::Dispatch::Screen

log4perl.appender.Screen.layout = SimpleLayout

Config File: AppenderYou want me to log where?

log4perl.logger = INFO, Screen

log4perl.appender.Screen = Log::Dispatch::Screen

log4perl.appender.Screen.layout = SimpleLayout

Config File: Message LayoutYou want me to log how?

log4perl.logger = INFO, Screen

log4perl.appender.Screen = Log::Dispatch::Screen

log4perl.appender.Screen.layout = SimpleLayout

Appender (log) Methods

● Log4perl uses Log::Dispatch to perform logging– Log::Dispatch supports a number of locations

● ApacheLog● DBI● Email● Files● STDOUT / STDERR (Screen)● Syslog● Jabber● Anything else you write

Appender Location: Log to FileChange From Screen to File

log4perl.logger = INFO, Screen

log4perl.appender.Screen = Log::Dispatch::File

log4perl.appender.Screen.Filename = myfile.log

log4perl.appender.Screen.layout = SimpleLayout

Appender NameShould Make Sense!

log4perl.logger = INFO, FileLog

log4perl.appender.FileLog = Log::Dispatch::File

log4perl.appender.FileLog.Filename = myfile.log

log4perl.appender.FileLog.layout = SimpleLayout

Logging To Multiple LocationsChange to File and Screen with Two Appenders

log4perl.logger = INFO, Screen, FileLog

log4perl.appender.Screen = Log::Dispatch::Screen

log4perl.appender.Screen.layout = SimpleLayout

log4perl.appender.FileLog = Log::Dispatch::File

log4perl.appender.FileLog.filename = myfile.log

log4perl.appender.FileLog.layout = SimpleLayout

Changing The Log Format

log4perl.logger = INFO, Screen

log4perl.appender.Screen = Log::Dispatch::Screen

log4perl.appender.Screen.layout = PatternLayout

log4perl.appender.Screen.layout.ConversionPattern=%F %L: %m%n

Available Placeholders (16 total)

%c Category of the logging event. %C Fully qualified package (or class) name of the caller %d Current date in yyyy/MM/dd hh:mm:ss format %F File where the logging event occurred %H Hostname %l Fully qualified name of the calling method followed by the callers source the file name and line number between parentheses. %L Line number within the file where the log statement was issued %m The message to be logged %M Method or function where the logging request was issued %n Newline (OS-independent) %p Priority of the logging event %P pid of the current process %r Number of milliseconds elapsed from program start to logging event %x The topmost NDC (see below) %X{key} The entry 'key' of the MDC (see below) %% A literal percent (%) sign

Custom Placeholders

# Define a conversion specifier (cspec) in the config file:

log4perl.logger = INFO, Screen

log4perl.appender.Screen = Log::Dispatch::Screen

log4perl.appender.Screen.layout = PatternLayout

log4perl.appender.Screen.layout.ConversionPattern=%O: %F %L %m%n

log4perl.PatternLayout.cspec.O = sub { return "OS $^O" }

Perl Code In Config File# Programmatically get the filename and layout

log4perl.logger = INFO, FileLog

log4perl.appender.FileLog = Log::Dispatch::File

log4perl.appender.FileLog.Filename = \

sub { getFileName(); }

log4perl.appender.FileLog.layout = \

sub { getLayout(); }

Code in the Config File: Notes

● We can put any code into the config file● This code is run within the main namespace● Some might consider this a security risk

– This feature can be disabled with: $Log::Log4perl::ALLOW_CODE_IN_CONFIG_FILE = 0;

Take a Break

Everyone take a break, and allow Patrick a few moments to catch up

Putting Categories to UseGoing Beyond the System Defined Root Logger

● One of Log4perl's key features is categories– Enable extra logging for just one portion of your

application– Remainder of application produces typical output

● Category name: often the name of the current package / class

● You've seen the default, root category– To use your own, pass a parameter into get_logger()

$logger = get_logger( $category_name );

Using Categories: Config File

# This is the root category / logger

log4perl.logger = WARN, Screen

# Create a new category like so:

log4perl.logger.CategoryName = DEBUG, Screen

Using Categories: Class ExampleSkywalker Class Hierarchy

Log Using Multiple Categories● Leia knows what she wants● Vader is set in his ways● We can put these two at a normal log level of “warn”● Luke has issues● He should be put at a log level of “debug”

Multiple Categories ExampleTwo categories: Log Skywalkers to Screen

log4perl.logger.Skywalker = WARN, Screen

log4perl.logger.Skywalker.Luke = DEBUG, Screen

log4perl.appender.Screen = Log::Dispatch::Screen

log4perl.appender.Screen.layout = SimpleLayout

Multiple Categories ExampleLog base class to File, sub class to Screen and File

log4perl.logger.Skywalker = WARN, FileLog

log4perl.logger.Skywalker.Luke = DEBUG, Screen

log4perl.appender.FileLog = Log::Dispatch::File

log4perl.appender.FileLog.layout = SimpleLayout

log4perl.appender.FileLog.Filename = myfile.log

log4perl.appender.Screen = Log::Dispatch::Screen

log4perl.appender.Screen.layout = SimpleLayout

Tell Log4perl Your CategoryPass in Your Class Name

● In package Skywalker: my $logger = get_logger( 'Skywalker' );

● In package Skywalker::Leia: my $logger = get_logger( 'Skywalker::Leia' );

● In package Skywalker::Luke: my $logger =

get_logger( 'Skywalker::Luke' );

● Or just... my $logger = get_logger( __PACKAGE__ );

Now, Pay Attention!

Important Logging Behavior

● Once a logger decides to log a message, it does a few things

● The message is first logged using each appender attached to that logger

● Then, the message is unconditionally logged using every appender attached to the logger's parents

Important Logging BehaviorExample Config File

log4perl.logger = ERROR, Syslog

log4perl.logger.Skywalker.Luke = DEBUG, Screen

log4perl.appender.Syslog = Log::Dispatch::Syslog

log4perl.appender.Syslog.layout = SimpleLayout

log4perl.appender.Screen = Log::Dispatch::Screen

log4perl.appender.Screen.layout = SimpleLayout

Important Logging Behavior

ERROR(inherited)

Log::Dispatch::Syslog

Skywalker

Skywalker::Luke Skywalker::Leia

ERROR(inherited)

DEBUG

Log::Dispatch::Screen

root logger

ERROR

What This Means

Lets take a look at how logging will work throughout the hierarchy

Logging Behavior: root logger Category root logger (when no category is defined) Properties Defines level: ERROR, location: Syslog

Code $logger->error( $light_saber );

Result Logs $light_saber to a syslog

Logging Behavior:Skywalker, Skywalker::Leia

Category Skywalker and Skywalker::Leia Properties Inherits ERROR and Syslog from root logger

Code $logger->error( $light_saber );

Result Logs $light_saber to syslog

Logging Behavior: Skywalker::Luke

Category Skywalker::Luke

Properties Defines level: DEBUG, location: Screen

Code $logger->error( $light_saber );

Result Logs $light_saber to the screen Logs $light_saber to syslog

Logging Behavior: Skywalker::Luke

Category Skywalker::Luke

Properties Defines level: DEBUG, location: Screen

Code $logger->debug( $light_saber );

Result (even though parents are at ERROR) Logs $light_saber to the screen Logs $light_saber to syslog

Logging Behavior

● Might be unnecessarily duplicating the logs– Luke's debug messages all going to syslog

● Take advantage of Log4perl!– Continue to log all debugging messages to the screen– Be more picky about what gets logged to syslog

● Should all of Skywalker::Luke's debugging messages be in our system logs?

– Remember, Luke has a lot of issues

Preventing Duplicate Logs

● If this behavior isn't what you want, change it:– Set the additivity of a category to zero

● Prevents messages from bubbling up● Do this to the Skywalker::Luke category

– Set the threshold of an appender to some log level● Defines a minimum log level for oncoming messages● Do this to the root or parent category● Can also set a system-wide default threshold

Setting AdditivityPrevent The Message From Bubbling Uplog4perl.logger = ERROR, Syslog

log4perl.logger.Skywalker.Luke = ERROR, Screen

log4perl.additivity.Skywalker.Luke = 0

log4perl.appender.Syslog = Log::Dispatch::Syslog

log4perl.appender.Syslog.layout = SimpleLayout

log4perl.appender.Screen = Log::Dispatch::Screen

log4perl.appender.Screen.layout = SimpleLayout

Setting a ThresholdDefine minimum log level of ERRORlog4perl.logger = ERROR, Syslog

log4perl.logger.Skywalker.Luke = DEBUG, Screen

log4perl.appender.Syslog = Log::Dispatch::Syslog

log4perl.appender.Syslog.layout = SimpleLayout

log4perl.appender.Syslog.Threshold = ERROR

log4perl.appender.Screen = Log::Dispatch::Screen

log4perl.appender.Screen.layout = SimpleLayout

One Last Look

● If a logger decides to fire, it sends the message to all of it's appenders

● The logger then has the message bubble up the hierarchy to hit all the other appenders on the way up

Important Logging Behavior

This behavior effects our earlier examples!

Preventing Duplicate LogsTwo categories: Log Skywalkers to Screen

# Does this previous example do what you think?

log4perl.logger.Skywalker = WARN, Screen

log4perl.logger.Skywalker.Luke = DEBUG, Screen

log4perl.appender.Screen = Log::Dispatch::Screen

log4perl.appender.Screen.layout = SimpleLayout

Preventing Duplicate LogsUse Additivity To Your Advantage

log4perl.logger.Skywalker = WARN, Screen

log4perl.logger.Skywalker.Luke = DEBUG, Screen

log4perl.additivity.Skywalker.Luke = 0

log4perl.appender.Screen = Log::Dispatch::Screen

log4perl.appender.Screen.layout = SimpleLayout

Preventing Duplicate Logs: FiltersYoda Filter: Do or do not, there is no “try”

log4perl.logger.Skywalker.Luke = DEBUG, Screen

log4perl.appender.Screen = Log::Dispatch::Screen

log4perl.appender.Screen.layout = SimpleLayout

log4perl.appender.Screen.filter = MyFilter

log4perl.filter.MyFilter = sub { /try/i }

log4perl.filter.MyFilter.AcceptOnMatch = false

this door 0wnz j00

Performance Penalties

● Logging has a (small) price tag– Decision to log is made at runtime

● Calls are optimized to run as fast as possible● There are still things you can do to help

Bad Performance Example

# Current Log Level: WARN# (doesn't actually log)

use Data::Dumper;$logger->debug( “Contents: ”, Dumper( %config ));

Optimizing Performance: Example 1

# Pass in a subroutine reference

$logger->debug( “Contents: ”, sub { use Data::Dumper; Dumper( %config ) });

Optimizing Performance: Example 2

# Is it necessary to pass any parameters at all?

if ( $logger->is_debug ) { $logger->debug( “Array Contents: ”, @big_array );}

Log4perl

● Log4perl has a boatload of features– Different log levels– Multiple logging destinations– Multiple categories

● There's more!

Resources● Log4perl Homepage

http://log4perl.sourceforge.net

● Log4perl Documentation

http://log4perl.sourceforge.net/releases/LogLog4perl/docs/html/Log/Log4perl.htm

● Perl.com Article: Retire Your Debugger

http://www.perl.com/pub/a/2002/09/11/log4perl.html

● Most Recent Version of this Presentation

http://openthought.org

Thank You

More Cool Stuff(You've reached the bonus slides!)

Automatically Rotate Logs

# Rotate logs daily, keep no more than 5 files log4perl.logger = WARN, Logfile log4perl.appender.Logfile = Log::Dispatch::FileRotate log4perl.appender.Logfile.filename = /var/log/log4perl.log log4perl.appender.Logfile.max = 5 log4perl.appender.Logfile.DatePattern = yyyy-MM-dd log4perl.appender.Logfile.TZ = EST log4perl.appender.Logfile.layout = SimpleLayout

Reloading The Config

# To load the config, we originally wrote:

Log::Log4perl::init( “config.conf” );

# We can also check the config at regular intervals:

Log::Log4perl::init_and_watch( “config.conf”, 60 );

Remote Config

# We can also use:

my $config = “http://openthought.org/config.pl”;

Log::Log4perl::init( $config );

# (this doesn't work with init_and_watch... yet)

Decisions Based on Log LevelApplication Which Sends Email

my $email_address;

if ( $logger->is_debug ) { $email_address = “test_account”;}else { $email_address = “real_account”;}

send_email( $email_address );

More Decisions Based On Log LevelDatabase Application

if ( $logger->is_debug ) { $logger->debug( dumper(%data) ); return; # Don't even touch the database}elsif ( $logger->is_info ) { $database = “Test”;}else { $database = “Production”}

$self->insert_into_database( %data );

top related