##################################################
package Log::Log4perl::Logger;
##################################################

use 5.006;
use strict;
use warnings;

use Log::Log4perl;
use Log::Log4perl::Level;
use Log::Log4perl::Layout;
use Log::Log4perl::Appender;
use Log::Log4perl::Appender::String;
use Log::Log4perl::Filter;
use Carp;

$Carp::Internal{"Log::Log4perl"}++;
$Carp::Internal{"Log::Log4perl::Logger"}++;

use constant _INTERNAL_DEBUG => 0;

    # Initialization
our $ROOT_LOGGER;
our $LOGGERS_BY_NAME = {};
our %APPENDER_BY_NAME = ();
our $INITIALIZED = 0;
our $NON_INIT_WARNED;
our $DIE_DEBUG = 0;
our $DIE_DEBUG_BUFFER = "";
    # Define the default appender that's used for formatting
    # warn/die/croak etc. messages.
our $STRING_APP_NAME = "_l4p_warn";
our $STRING_APP      = Log::Log4perl::Appender->new(
                          "Log::Log4perl::Appender::String",
                          name => $STRING_APP_NAME);
$STRING_APP->layout(Log::Log4perl::Layout::PatternLayout->new("%m"));
our $STRING_APP_CODEREF = generate_coderef([[$STRING_APP_NAME, $STRING_APP]]);

__PACKAGE__->reset();

###########################################
sub warning_render {
###########################################
    my($logger, @message) = @_;

    $STRING_APP->string("");
    $STRING_APP_CODEREF->($logger, 
                          @message, 
                          Log::Log4perl::Level::to_level($ALL));
    return $STRING_APP->string();
}

##################################################
sub cleanup {
##################################################
    # warn "Logger cleanup";

    # Nuke all convenience loggers to avoid them causing cleanup to 
    # be delayed until global destruction. Problem is that something like
    #     *{"DEBUG"} = sub { $logger->debug };
    # ties up a reference to $logger until global destruction, so we 
    # need to clean up all :easy shortcuts, hence freeing the last
    # logger references, to then rely on the garbage collector for cleaning
    # up the loggers.
    Log::Log4perl->easy_closure_global_cleanup();

    # Delete all loggers
    $LOGGERS_BY_NAME = {};

    # Delete the root logger
    undef $ROOT_LOGGER;

    # Delete all appenders
    %APPENDER_BY_NAME   = ();

    undef $INITIALIZED;
}

##################################################
sub DESTROY {
##################################################
    CORE::warn "Destroying logger $_[0] ($_[0]->{category})" 
            if $Log::Log4perl::CHATTY_DESTROY_METHODS;
}

##################################################
sub reset {
##################################################
    $ROOT_LOGGER        = __PACKAGE__->_new("", $OFF);
#    $LOGGERS_BY_NAME    = {};  #leave this alone, it's used by 
                                #reset_all_output_methods when 
                                #the config changes

    %APPENDER_BY_NAME   = ();
    undef $INITIALIZED;
    undef $NON_INIT_WARNED;
    Log::Log4perl::Appender::reset();

    #clear out all the existing appenders
    foreach my $logger (values %$LOGGERS_BY_NAME){
        $logger->{appender_names} = [];

	#this next bit deals with an init_and_watch case where a category
	#is deleted from the config file, we need to zero out the existing
	#loggers so ones not in the config file not continue with their old
	#behavior --kg
        next if $logger eq $ROOT_LOGGER;
        $logger->{level} = undef;
        $logger->level();  #set it from the hierarchy
    }

    # Clear all filters
    Log::Log4perl::Filter::reset();
}

##################################################
sub _new {
##################################################
    my($class, $category, $level) = @_;

    print("_new: $class/$category/", defined $level ? $level : "undef",
          "\n") if _INTERNAL_DEBUG;

    die "usage: __PACKAGE__->_new(category)" unless
        defined $category;
    
    $category  =~ s/::/./g;

       # Have we created it previously?
    if(exists $LOGGERS_BY_NAME->{$category}) {
        print "_new: exists already\n" if _INTERNAL_DEBUG;
        return $LOGGERS_BY_NAME->{$category};
    }

    my $self  = {
        category  => $category,
        num_appenders => 0,
        additivity    => 1,
        level         => $level,
        layout        => undef,
                };

   bless $self, $class;

   $level ||= $self->level();

        # Save it in global structure
   $LOGGERS_BY_NAME->{$category} = $self;

   $self->set_output_methods;

   print("Created logger $self ($category)\n") if _INTERNAL_DEBUG;

   return $self;
}

##################################################
sub category {
##################################################
   my ($self) = @_;

   return $self->{ category };
}

##################################################
sub reset_all_output_methods {
##################################################
    print "reset_all_output_methods: \n" if _INTERNAL_DEBUG;

    foreach my $loggername ( keys %$LOGGERS_BY_NAME){
        $LOGGERS_BY_NAME->{$loggername}->set_output_methods;
    }
    $ROOT_LOGGER->set_output_methods;
}

##################################################
sub set_output_methods {
# Here's a big performance increase.  Instead of having the logger
# calculate whether to log and whom to log to every time log() is called,
# we calculate it once when the logger is created, and recalculate
# it if the config information ever changes.
#
##################################################
   my ($self) = @_;
    
   my (@appenders, %seen);

   my ($level) = $self->level();

   print "set_output_methods: $self->{category}/$level\n" if _INTERNAL_DEBUG;

   #collect the appenders in effect for this category    

   for(my $logger = $self; $logger; $logger = parent_logger($logger)) {

        foreach my $appender_name (@{$logger->{appender_names}}){

                #only one message per appender, (configurable)
            next if $seen{$appender_name} ++ && 
                    $Log::Log4perl::one_message_per_appender;

            push (@appenders,     
                   [$appender_name,
                    $APPENDER_BY_NAME{$appender_name},
                   ]
            );
        }
        last unless $logger->{additivity};
    }

        #make a no-op coderef for inactive levels
    my $noop = generate_noop_coderef();

       #make a coderef
    my $coderef = (! @appenders ? $noop : &generate_coderef(\@appenders)); 

    my %priority = %Log::Log4perl::Level::PRIORITY; #convenience and cvs

   # changed to >= from <= as level ints were reversed
    foreach my $levelname (keys %priority){
        if (Log::Log4perl::Level::isGreaterOrEqual($level,
						   $priority{$levelname}
						   )) {
            print "  ($priority{$levelname} <= $level)\n"
                  if _INTERNAL_DEBUG;
            $self->{$levelname}      = $coderef;
            $self->{"is_$levelname"} = generate_is_xxx_coderef("1");
            print "Setting is_$levelname to 1\n" if _INTERNAL_DEBUG;
        }else{
            print "  ($priority{$levelname} > $level)\n" if _INTERNAL_DEBUG;
            $self->{$levelname}      = $noop;
            $self->{"is_$levelname"} = generate_is_xxx_coderef("0");
            print "Setting is_$levelname to 0\n" if _INTERNAL_DEBUG;
        }

        print("  Setting [$self] $self->{category}.$levelname to ",
              ($self->{$levelname} == $noop ? "NOOP" : 
              ("Coderef [$coderef]: " . scalar @appenders . " appenders")), 
              "\n") if _INTERNAL_DEBUG;
    }
}

##################################################
sub generate_coderef {
##################################################
    my $appenders = shift;
                    
    print "generate_coderef: ", scalar @$appenders, 
          " appenders\n" if _INTERNAL_DEBUG;

    my $watch_check_code = generate_watch_code("logger", 1);

    return sub {
      my $logger = shift;
      my $level  = pop;

      my $message;
      my $appenders_fired = 0;
      
      # Evaluate all parameters that need to be evaluated. Two kinds:
      #
      # (1) It's a hash like { filter => "filtername",
      #                        value  => "value" }
      #     => filtername(value)
      #
      # (2) It's a code ref
      #     => coderef()
      #

      $message   = [map { ref $_ eq "HASH" && 
                           exists $_->{filter} && 
                           ref $_->{filter} eq 'CODE' ?
                               $_->{filter}->($_->{value}) :
                           ref $_ eq "CODE" ?
                               $_->() : $_ 
                          } @_];                  

      print("coderef: $logger->{category}\n") if _INTERNAL_DEBUG;

      if(defined $Log::Log4perl::Config::WATCHER) {
          return unless $watch_check_code->($logger, @_, $level);
      }

      foreach my $a (@$appenders) {   #note the closure here
          my ($appender_name, $appender) = @$a;

          print("  Sending message '<$message->[0]>' ($level) " .
                "to $appender_name\n") if _INTERNAL_DEBUG;
                
          $appender->log(
              #these get passed through to Log::Dispatch
              { name    => $appender_name,
                level   => $Log::Log4perl::Level::L4P_TO_LD{
                               $level},   
                message => $message,
              },
              #these we need
              $logger->{category},
              $level,
          ) and $appenders_fired++;
              # Only counting it if it returns a true value. Otherwise
              # the appender threshold might have suppressed it after all.
    
      } #end foreach appenders
    
      return $appenders_fired;

    }; #end coderef
}

##################################################
sub generate_noop_coderef {
##################################################
    my $watch_delay_code;

    # This might seem crazy at first, but even in a Log4perl noop, we
    # need to check if the configuration changed in a init_and_watch 
    # situation. Why? Say, an application is running in a loop that
    # constantly tries to issue debug() messages, but they're suppressed by
    # the current Log4perl configuration. If debug() (which is a noop
    # here) wasn't watching the configuration for changes, it would never
    # catch the case where someone bumps up the log level and expects
    # the application to pick it up and start logging debug() statements.

    my $watch_check_code = generate_watch_code("logger", 1);

    my $coderef;

    if(defined $Log::Log4perl::Config::WATCHER) {
        $coderef = $watch_check_code;
    } else {
        $coderef = sub { undef };
    }

    return $coderef;
}

##################################################
sub generate_is_xxx_coderef {
##################################################
    my($return_token) = @_;

    return generate_watch_code("checker", $return_token);
}

##################################################
sub generate_watch_code {
##################################################
    my($type, $return_token) = @_;

    print "generate_watch_code:\n" if _INTERNAL_DEBUG;

      # No watcher configured, return a no-op as watch code.
    if(! defined $Log::Log4perl::Config::WATCHER) {
        return sub { $return_token };
    }

    my $cond = generate_watch_conditional();

    return sub {
        print "exe_watch_code:\n" if _INTERNAL_DEBUG;

       if(_INTERNAL_DEBUG) {
           print "Next check: ",
             "$Log::Log4perl::Config::Watch::NEXT_CHECK_TIME ",
             " Now: ", time(), " Mod: ",
             (stat($Log::Log4perl::Config::WATCHER->file()))[9],
             "\n";
       }

       if( $cond->() ) {
           my $init_permitted = 1;

           if(exists $Log::Log4perl::Config::OPTS->{ preinit_callback } ) {
               print "Calling preinit_callback\n" if _INTERNAL_DEBUG;
               $init_permitted = 
               $Log::Log4perl::Config::OPTS->{ preinit_callback }->( 
                   Log::Log4perl::Config->watcher()->file() );
               print "Callback returned $init_permitted\n" if _INTERNAL_DEBUG;
           }

           if( $init_permitted ) {
               Log::Log4perl->init_and_watch();
           } else {
               # It was time to reinit, but init wasn't permitted.
               # Return true, so that the logger continues as if
               # it wasn't time to reinit.
               return 1;
           }

           my $logger = shift;
           my $level  = pop;

           # Forward call to new configuration
           if($type eq "checker") {
               return $logger->$level();

           } elsif( $type eq "logger") {
               my $methodname = lc($level);

               # Bump up the caller level by three, since
               # we've artificially introduced additional levels.
               local $Log::Log4perl::caller_depth =
                     $Log::Log4perl::caller_depth + 3;

               # Get a new logger for the same category (the old
               # logger might be obsolete because of the re-init)
               $logger = Log::Log4perl::get_logger( $logger->{category} );

               $logger->$methodname(@_); # send the message
               # to the new configuration
               return undef;     # Return false, so the logger finishes
               # prematurely and doesn't log the same 
               # message again.
           } else {
               die "internal error: unknown type";
           }
       } else {
           if(_INTERNAL_DEBUG) {
               print "Conditional returned false\n";
           }
           return $return_token;
       }
   };
}

##################################################
sub generate_watch_conditional {
##################################################

    if(defined $Log::Log4perl::Config::Watch::SIGNAL_CAUGHT) {
        # In this mode, we just check for the variable indicating
        # that the signal has been caught
        return sub {
            return $Log::Log4perl::Config::Watch::SIGNAL_CAUGHT;
        };
    }

    return sub {
        return 
            ( time() > $Log::Log4perl::Config::Watch::NEXT_CHECK_TIME and 
              $Log::Log4perl::Config::WATCHER->change_detected() );
    };
}

##################################################
sub parent_string {
##################################################
    my($string) = @_;

    if($string eq "") {
        return undef; # root doesn't have a parent.
    }

    my @components = split /\./, $string;
    
    if(@components == 1) {
        return "";
    }

    pop @components;

    return join('.', @components);
}

##################################################
sub level {
##################################################
    my($self, $level, $dont_reset_all) = @_;

        # 'Set' function
    if(defined $level) {
        croak "invalid level '$level'" 
                unless Log::Log4perl::Level::is_valid($level);
        if ($level =~ /\D/){
            $level = Log::Log4perl::Level::to_priority($level);
        }
        $self->{level} = $level;   

        &reset_all_output_methods
            unless $dont_reset_all;  #keep us from getting overworked 
                                     #if it's the config file calling us 

        return $level;
    }

        # 'Get' function
    if(defined $self->{level}) {
        return $self->{level};
    }

    for(my $logger = $self; $logger; $logger = parent_logger($logger)) {

        # Does the current logger have the level defined?

        if($logger->{category} eq "") {
            # It's the root logger
            return $ROOT_LOGGER->{level};
        }
            
        if(defined $LOGGERS_BY_NAME->{$logger->{category}}->{level}) {
            return $LOGGERS_BY_NAME->{$logger->{category}}->{level};
        }
    }

    # We should never get here because at least the root logger should
    # have a level defined
    die "We should never get here.";
}

##################################################
sub parent_logger {
# Get the parent of the current logger or undef
##################################################
    my($logger) = @_;

        # Is it the root logger?
    if($logger->{category} eq "") {
        # Root has no parent
        return undef;
    }

        # Go to the next defined (!) parent
    my $parent_class = parent_string($logger->{category});

    while($parent_class ne "" and
          ! exists $LOGGERS_BY_NAME->{$parent_class}) {
        $parent_class = parent_string($parent_class);
        $logger =  $LOGGERS_BY_NAME->{$parent_class};
    }

    if($parent_class eq "") {
        $logger = $ROOT_LOGGER;
    } else {
        $logger = $LOGGERS_BY_NAME->{$parent_class};
    }

    return $logger;
}

##################################################
sub get_root_logger {
##################################################
    my($class) = @_;
    return $ROOT_LOGGER;    
}

##################################################
sub additivity {
##################################################
    my($self, $onoff, $no_reinit) = @_;

    if(defined $onoff) {
        $self->{additivity} = $onoff;
    }

    if( ! $no_reinit ) {
        $self->set_output_methods();
    }

    return $self->{additivity};
}

##################################################
sub get_logger {
##################################################
    my($class, $category) = @_;

    unless(defined $ROOT_LOGGER) {
        Carp::confess "Internal error: Root Logger not initialized.";
    }

    return $ROOT_LOGGER if $category eq "";

    my $logger = $class->_new($category);
    return $logger;
}

##################################################
sub add_appender {
##################################################
    my($self, $appender, $dont_reset_all) = @_;

        # We take this as an indicator that we're initialized.
    $INITIALIZED = 1;

    my $appender_name = $appender->name();

    $self->{num_appenders}++;  #should this be inside the unless?

      # Add newly created appender to the end of the appender array
    unless (grep{$_ eq $appender_name} @{$self->{appender_names}}){
        $self->{appender_names} = [sort @{$self->{appender_names}}, 
                                        $appender_name];
    }

    $APPENDER_BY_NAME{$appender_name} = $appender;

    reset_all_output_methods
                unless $dont_reset_all;  # keep us from getting overworked
                                         # if it's  the config file calling us

        # For chaining calls ...
    return $appender;
}

##################################################
sub remove_appender {
##################################################
    my($self, $appender_name, $dont_reset_all, $sloppy) = @_;

    my %appender_names = map { $_ => 1 } @{$self->{appender_names}};
    
    if(!exists $appender_names{$appender_name}) {
        die "No such appender: $appender_name" unless $sloppy;
        return undef;
    }

    delete $appender_names{$appender_name};
    $self->{num_appenders}--;
    $self->{appender_names} = [sort keys %appender_names];

    &reset_all_output_methods
                unless $dont_reset_all; 
}

##################################################
sub eradicate_appender {
##################################################
        # If someone calls Logger->... and not Logger::...
    shift if $_[0] eq __PACKAGE__;

    my($appender_name, $dont_reset_all) = @_;

    return 0 unless exists 
        $APPENDER_BY_NAME{$appender_name};

        # Remove the given appender from all loggers
        # and delete all references to it, causing
        # its DESTROY method to be called.
    foreach my $logger (values %$LOGGERS_BY_NAME){
        $logger->remove_appender($appender_name, 0, 1);
    }
        # Also remove it from the root logger
    $ROOT_LOGGER->remove_appender($appender_name, 0, 1);
    
    delete $APPENDER_BY_NAME{$appender_name};

    &reset_all_output_methods
                unless $dont_reset_all; 

    return 1;
}

##################################################
sub has_appenders {
##################################################
    my($self) = @_;

    return $self->{num_appenders};
}

##################################################
sub log {
# external api
##################################################
    my ($self, $priority, @messages) = @_;

    confess("log: No priority given!") unless defined($priority);

       # Just in case of 'init_and_watch' -- see Changes 0.21
    $_[0] = $LOGGERS_BY_NAME->{$_[0]->{category}} if 
        defined $Log::Log4perl::Config::WATCHER;

    init_warn() unless $INITIALIZED or $NON_INIT_WARNED;

    croak "priority $priority isn't numeric" if ($priority =~ /\D/);

    my $which = Log::Log4perl::Level::to_level($priority);

    $self->{$which}->($self, @messages, 
                    Log::Log4perl::Level::to_level($priority));
}

######################################################################
#
# create_custom_level 
# creates a custom level
# in theory, could be used to create the default ones
######################################################################
sub create_custom_level {
######################################################################
  my $level = shift || die("create_custom_level: " .
                           "forgot to pass in a level string!");
  my $after = shift || die("create_custom_level: " .
                           "forgot to pass in a level after which to " .
                           "place the new level!");
  my $syslog_equiv = shift; # can be undef
  my $log_dispatch_level = shift; # optional

  ## only let users create custom levels before initialization

  die("create_custom_level must be called before init or " .
      "first get_logger() call") if ($INITIALIZED);

  my %PRIORITY = %Log::Log4perl::Level::PRIORITY; #convenience

  die("create_custom_level: no such level \"$after\"! Use one of: ", 
     join(", ", sort keys %PRIORITY)) unless $PRIORITY{$after};

  # figure out new int value by AFTER + (AFTER+ 1) / 2

  my $next_prio = Log::Log4perl::Level::get_lower_level($PRIORITY{$after}, 1);
  my $cust_prio = int(($PRIORITY{$after} + $next_prio) / 2);

  die(qq{create_custom_level: Calculated level of $cust_prio already exists!
      This should only happen if you've made some insane number of custom
      levels (like 15 one after another)
      You can usually fix this by re-arranging your code from:
      create_custom_level("cust1", X);
      create_custom_level("cust2", X);
      create_custom_level("cust3", X);
      create_custom_level("cust4", X);
      create_custom_level("cust5", X);
      into:
      create_custom_level("cust3", X);
      create_custom_level("cust5", X);
      create_custom_level("cust4", 4);
      create_custom_level("cust2", cust3);
      create_custom_level("cust1", cust2);
   }) if (${Log::Log4perl::Level::LEVELS{$cust_prio}});

  Log::Log4perl::Level::add_priority($level, $cust_prio, $syslog_equiv,
                                     $log_dispatch_level);

  print("Adding prio $level at $cust_prio\n") if _INTERNAL_DEBUG;

  # get $LEVEL into namespace of Log::Log4perl::Logger to 
  # create $logger->foo nd $logger->is_foo
  my $name = "Log::Log4perl::Logger::";
  my $key = $level;

  no strict qw(refs);
  # be sure to use ${Log...} as CVS adds log entries for Log
  *{"$name$key"} = \${Log::Log4perl::Level::PRIORITY{$level}};

  # now, stick it in the caller's namespace
  $name = caller(0) . "::";
  *{"$name$key"} = \${Log::Log4perl::Level::PRIORITY{$level}};
  use strict qw(refs);

  create_log_level_methods($level);

  return 0;

}

########################################
#
# if we were hackin' lisp (or scheme), we'd be returning some lambda
# expressions. But we aren't. :) So we'll just create some strings and
# eval them.
########################################
sub create_log_level_methods {
########################################
  my $level = shift || die("create_log_level_methods: " .
                           "forgot to pass in a level string!");
  my $lclevel = lc($level);
  my $levelint = uc($level) . "_INT";
  my $initial_cap = ucfirst($lclevel);

  no strict qw(refs);

  # This is a bit better way to create code on the fly than eval'ing strings.
  # -erik

  *{__PACKAGE__ . "::$lclevel"} = sub {
        if(_INTERNAL_DEBUG) {
            my $level_disp = (defined $_[0]->{level} ? $_[0]->{level} 
                                                     : "[undef]");
            print "$lclevel: ($_[0]->{category}/$level_disp) [@_]\n";
        }
        init_warn() unless $INITIALIZED or $NON_INIT_WARNED;
        $_[0]->{$level}->(@_, $level) if defined $_[0]->{$level};
     };

  # Added these to have is_xxx functions as fast as xxx functions
  # -ms
  
  my $islevel   = "is_" . $level;
  my $islclevel = "is_" . $lclevel;

  *{__PACKAGE__ . "::is_$lclevel"} = sub {
      $_[0]->{$islevel}->($_[0], $islclevel);
  };
  
  # Add the isXxxEnabled() methods as identical to the is_xxx
  # functions. - dviner
  
  *{__PACKAGE__ . "::is".$initial_cap."Enabled"} = 
                           \&{__PACKAGE__ . "::is_$lclevel"};
  
  use strict qw(refs);

  return 0;
}

#now lets autogenerate the logger subs based on the defined priorities
foreach my $level (keys %Log::Log4perl::Level::PRIORITY){
  create_log_level_methods($level);
}

##################################################
sub init_warn {
##################################################
    CORE::warn "Log4perl: Seems like no initialization happened. " .
               "Forgot to call init()?\n";
    # Only tell this once;
    $NON_INIT_WARNED = 1;
}

#######################################################
# call me from a sub-func to spew the sub-func's caller
#######################################################
sub callerline {
  my $message = join ('', @_);

  my $caller_offset = 
    Log::Log4perl::caller_depth_offset( 
        $Log::Log4perl::caller_depth + 1 );

  my ($pack, $file, $line) = caller($caller_offset);

  if (not chomp $message) {     # no newline
    $message .= " at $file line $line";

    # Someday, we'll use Threads. Really.
    if (defined &Thread::tid) {
      my $tid = Thread->self->tid;
      $message .= " thread $tid" if $tid;
    }
  }

  return ($message, "\n");
}

#######################################################
sub and_warn {
#######################################################
  my $self = shift;
  CORE::warn(callerline($self->warning_render(@_)));
}

#######################################################
sub and_die {
#######################################################
  my $self = shift;
  my $arg  = $_[0];

  my($msg) = callerline($self->warning_render(@_));

  if($DIE_DEBUG) {
      $DIE_DEBUG_BUFFER = "DIE_DEBUG: $msg";
  } else {
      if( $Log::Log4perl::STRINGIFY_DIE_MESSAGE ) {
          die("$msg\n");
      }
      die $arg;
  }
}

##################################################
sub logwarn {
##################################################
  my $self = shift;

  local $Log::Log4perl::caller_depth = 
        $Log::Log4perl::caller_depth + 1;

  if ($self->is_warn()) {
        # Since we're one caller level off now, compensate for that.
    my @chomped = @_;
    chomp($chomped[-1]);
    $self->warn(@chomped);
  }

  $self->and_warn(@_);
}

##################################################
sub logdie {
##################################################
  my $self = shift;

  local $Log::Log4perl::caller_depth = 
        $Log::Log4perl::caller_depth + 1;

  if ($self->is_fatal()) {
        # Since we're one caller level off now, compensate for that.
    my @chomped = @_;
    chomp($chomped[-1]);
    $self->fatal(@chomped);
  }

  $Log::Log4perl::LOGDIE_MESSAGE_ON_STDERR ? 
      $self->and_die(@_) : 
        exit($Log::Log4perl::LOGEXIT_CODE);
}

##################################################
sub logexit {
##################################################
  my $self = shift;

  local $Log::Log4perl::caller_depth = 
        $Log::Log4perl::caller_depth + 1;

  if ($self->is_fatal()) {
        # Since we're one caller level off now, compensate for that.
    my @chomped = @_;
    chomp($chomped[-1]);
    $self->fatal(@chomped);
  }

  exit $Log::Log4perl::LOGEXIT_CODE;
}

##################################################
# clucks and carps are WARN level
sub logcluck {
##################################################
  my $self = shift;

  local $Log::Log4perl::caller_depth = 
        $Log::Log4perl::caller_depth + 1;

  local $Carp::CarpLevel = 
        $Carp::CarpLevel + 1;

  my $msg = $self->warning_render(@_);

  if ($self->is_warn()) {
    my $message = Carp::longmess($msg);
    foreach (split(/\n/, $message)) {
      $self->warn("$_\n");
    }
  }

  Carp::cluck($msg);
}

##################################################
sub logcarp {
##################################################
  my $self = shift;

  local $Carp::CarpLevel = $Carp::CarpLevel + 1;

  local $Log::Log4perl::caller_depth = 
        $Log::Log4perl::caller_depth + 1;

  my $msg = $self->warning_render(@_);

  if ($self->is_warn()) {
    my $message = Carp::shortmess($msg);
    foreach (split(/\n/, $message)) {
      $self->warn("$_\n");
    }
  }

  Carp::carp($msg);
}

##################################################
# croaks and confess are FATAL level
##################################################
sub logcroak {
##################################################
  my $self = shift;
  my $arg  = $_[0];

  my $msg = $self->warning_render(@_);

  local $Carp::CarpLevel = 
        $Carp::CarpLevel + 1;

  local $Log::Log4perl::caller_depth = 
        $Log::Log4perl::caller_depth + 1;

  if ($self->is_fatal()) {
    my $message = Carp::shortmess($msg);
    foreach (split(/\n/, $message)) {
      $self->fatal("$_\n");
    }
  }

  my $croak_msg = $arg;

  if( $Log::Log4perl::STRINGIFY_DIE_MESSAGE ) {
      $croak_msg = $msg;
  }

  $Log::Log4perl::LOGDIE_MESSAGE_ON_STDERR ? 
      Carp::croak($croak_msg) : 
        exit($Log::Log4perl::LOGEXIT_CODE);
}

##################################################
sub logconfess {
##################################################
  my $self = shift;
  my $arg  = $_[0];

  local $Carp::CarpLevel = 
        $Carp::CarpLevel + 1;

  local $Log::Log4perl::caller_depth = 
        $Log::Log4perl::caller_depth + 1;

  my $msg = $self->warning_render(@_);

  if ($self->is_fatal()) {
    my $message = Carp::longmess($msg);
    foreach (split(/\n/, $message)) {
      $self->fatal("$_\n");
    }
  }

  my $confess_msg = $arg;

  if( $Log::Log4perl::STRINGIFY_DIE_MESSAGE ) {
      $confess_msg = $msg;
  }

  $Log::Log4perl::LOGDIE_MESSAGE_ON_STDERR ? 
      confess($confess_msg) :
        exit($Log::Log4perl::LOGEXIT_CODE);
}

##################################################
# in case people prefer to use error for warning
##################################################
sub error_warn {
##################################################
  my $self = shift;

  local $Log::Log4perl::caller_depth = 
        $Log::Log4perl::caller_depth + 1;

  if ($self->is_error()) {
    $self->error(@_);
  }

  $self->and_warn(@_);
}

##################################################
sub error_die {
##################################################
  my $self = shift;

  local $Log::Log4perl::caller_depth = 
        $Log::Log4perl::caller_depth + 1;

  my $msg = $self->warning_render(@_);

  if ($self->is_error()) {
    $self->error($msg);
  }

  $Log::Log4perl::LOGDIE_MESSAGE_ON_STDERR ? 
      $self->and_die($msg) :
        exit($Log::Log4perl::LOGEXIT_CODE);
}

##################################################
sub more_logging {
##################################################
  my ($self) = shift;
  return $self->dec_level(@_);
}

##################################################
sub inc_level {
##################################################
    my ($self, $delta) = @_;

    $delta ||= 1;

    $self->level(Log::Log4perl::Level::get_higher_level($self->level(), 
                                                        $delta));

    $self->set_output_methods;
}

##################################################
sub less_logging {
##################################################
  my ($self) = shift;
  return $self->inc_level(@_);
}

##################################################
sub dec_level {
##################################################
    my ($self, $delta) = @_;

    $delta ||= 1;

    $self->level(Log::Log4perl::Level::get_lower_level($self->level(), $delta));

    $self->set_output_methods;
}

1;

__END__

=encoding utf8

=head1 NAME

Log::Log4perl::Logger - Main Logger Class

=head1 SYNOPSIS

    # It's not here

=head1 DESCRIPTION

While everything that makes Log4perl tick is implemented here,
please refer to L<Log::Log4perl> for documentation.

=head1 LICENSE

Copyright 2002-2026 by Mike Schilli E<lt>m@perlmeister.comE<gt>
and Kevin Goess E<lt>cpan@goess.orgE<gt>.

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