package Dancer2::Logger::Log4perl;
# ABSTRACT: Dancer2 logger interface for Log4perl. 

use Log::Log4perl qw( :easy );
use Moo;
use Dancer2::Core::Types;

with 'Dancer2::Core::Role::Logger';

has config_file => (
    is => 'ro',
    isa => ReadableFilePath,

has config_watch_interval => (
    is => 'ro',
    isa => sub {
        my $config_watch_interval = shift;
        # Can either be 'HUP' or a number
        Str->( $config_watch_interval );
        if( $config_watch_interval eq 'HUP' ) {
        else {
            Num->( $config_watch_interval );

sub _initialize_log4perl {
    my $self = shift;
    # If config_file is defined, then use that
    if( defined $self->config_file ) {
        # ...optionally with the watch interval
        if( defined $self->config_watch_interval ) {
            Log::Log4perl->init_and_watch( $self->config_file, $self->config_watch_interval );
        else {
            Log::Log4perl->init( $self->config_file );
    # Otherwise we'll easy init with the appropriate log level
    else {
        my $log_level_mapping = {
            'error'   => $ERROR,
            'warn'    => $WARN,
            'warning' => $WARN,
            'info'    => $INFO,
            'debug'   => $DEBUG,
            'trace'   => $TRACE,
            'core'    => $TRACE,
        Log::Log4perl->easy_init( $log_level_mapping->{ $self->log_level } );

sub log {
    my ( $self, $level, $message ) = @_;
    # Need to initialize Log4perl if it isn't yet
    if( !Log::Log4perl->initialized() ) {
    # Need to convert Dancer2 log levels to Log4perl levels
    $level = 'warn' if $level eq 'warning';
    $level = 'trace' if $level eq 'core';

    # TODO: Couldn't get $Log::Log4perl::caller_depth to work
    Log::Log4perl->get_logger( scalar caller(4) )->$level( $message );




=encoding UTF-8

=head1 NAME

Dancer2::Logger::Log4perl - Dancer2 logger interface for Log4perl. 

=head1 VERSION

version 0.06


In your F<config.yml>:

   logger: log4perl
   log: core
            config_file: log4perl.conf

In your F<log4perl.conf>:

   log4perl.rootLogger              = DEBUG, LOG1
   log4perl.appender.LOG1           = Log::Log4perl::Appender::File
   log4perl.appender.LOG1.filename  = /var/log/mylog.log
   log4perl.appender.LOG1.mode      = append
   log4perl.appender.LOG1.layout    = Log::Log4perl::Layout::PatternLayout
   log4perl.appender.LOG1.layout.ConversionPattern = %d %p %m %n


This class is an interface between L<Dancer2>'s logging engine abstraction
layer and the L<Log::Log4perl> library. In order to use it, you have to
set the C<logger> engine to C<log4perl>.

L<Dancer2>'s C<core> level messages are passed to L<Log4perl> as level C<trace>
but will not be passed unless L<Dancer2>'s C<log> config is C<core>.

C<log> should be set a lower priority than the lowest priority as set in your
L<Log4perl> configuration. If it isn't, the log messages will not be passed 
to L<Log4perl>.


If you don't specify C<config_file> then Log4perl will easy init with the 
appropriate log level, as specified by Dancer2.

=over 4

=item B<< config_file >>

You can specify the log4perl configuration file using the C<config_file> option:

   logger: log4perl
            config_file: log4perl.conf

=item B<< config_watch_interval >>

You can optionally specify the watch interval, either in seconds or as 'HUP':

   logger: log4perl
            config_file: log4perl.conf
            config_watch_interval: 30


=head1 CREDITS

This was originally developed by Ryan Larscheidt and Jon Miner at the
University of Wisconsin. The initial version of this was built upon their
final version. I cannot thank them enough for their work on this - it not
only solved my needs at C<$work>, but also plugs a hole in our ecosystem.

=head1 AUTHOR

Jason A. Crome <>


This software is copyright (c) 2018 by Jason A. Crome.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.