2

I want to log some results into a file withot messing with the leves of my logs. Is it possible to do that with Log::Log4perl. I have tried to follow the documentation, but I only was able to find that it depends on the log level to printo a file? as in here :

og::Log4perl->easy_init( { level    => $DEBUG,
                            file     => ">>test.log",
                            layout   => '%F{1}-%L-%M: %m%n' },
                          { level    => $DEBUG,
                            file     => "STDOUT",
                            layout   => '%m%n' },
                        );

But I want to keep logging my stuff in screen and only put another messages into the log file. But I have not been able to find a way to put the result to the log:

use strict;
use warnings;
use Log::Log4perl;
use Win32::Console::ANSI;

my $results = "result.txt";
my $conf = q(
  log4perl.appender.SCREEN         = Log::Log4perl::Appender::ScreenColoredLevels
  log4perl.appender.SCREEN.layout  = Log::Log4perl::Layout::PatternLayout
  log4perl.appender.SCREEN.color.INFO = bright_white    
  log4perl.appender.SCREEN.color.WARN = bright_yellow    
  log4perl.appender.SCREEN.color.ERROR = bright_red    
  log4perl.appender.SCREEN.layout.ConversionPattern = [%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5p] %m  %50C::%L %n
);

my $log_level = "TRACE";
my $log_level_conf = "log4perl.category.".__FILE__." = ".$log_level.", SCREEN\n";   
$conf = $log_level_conf.$conf;
    
Log::Log4perl::init( \$conf );

my $log = Log::Log4perl::get_logger(__FILE__);

$log->info("INFO");
$log->debug("DEBUG");
$log->error("error");
$log->fatal("FATAL");
$log->trace("TRACE");

print "\n\n";

$log->info("Im doing foo");
$log->debug( "doing foo");
$log->trace( "crap from foo");
$log->info( "Im doing bar");
$log->debug( "Im doing foo");
$log->trace( "crap from bar");

# $log->result "Foo and bar done";

The line where it says result is the one that I´m looking for, some way to have a diferent function to write into the log using the same $log.

Is this possible?

Edit: Following the suggestion by @amit bhosale I'm still not able to make it work:

use strict;
use warnings;
use Log::Log4perl;
use Win32::Console::ANSI;


my $conf = q(

    log4perl.category = TRACE, AppResult

    log4perl.appender.SCREEN         = Log::Log4perl::Appender::ScreenColoredLevels
    log4perl.appender.SCREEN.layout  = Log::Log4perl::Layout::PatternLayout
    log4perl.appender.SCREEN.color.INFO = bright_white    
    log4perl.appender.SCREEN.color.WARN = bright_yellow    
    log4perl.appender.SCREEN.color.ERROR = bright_red    
    log4perl.appender.SCREEN.layout.ConversionPattern = [%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5p] %m  %50C::%L %n
    
    
    # Custom RESULT logs
    log4perl.filter.ResultsFilter  = Log::Log4perl::Filter::LevelMatch
    log4perl.filter.ResultsFilter.LevelToMatch  = RESULT
    log4perl.filter.ResultsFilter.AcceptOnMatch = true  
     
    log4perl.appender.AppResult = Log::Log4perl::Appender::File
    log4perl.appender.AppResult.filename = results.log
    log4perl.appender.AppResult.mode=append  
    log4perl.appender.AppResult.Filter   = ResultsFilter
    log4perl.appender.AppResult.layout  = Log::Log4perl::Layout::PatternLayout
    log4perl.appender.AppResult.layout.ConversionPattern = [%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5p] %m  %50C::%L %n


);

my $log_level = "TRACE";
my $log_level_conf = "log4perl.category.".__FILE__." = ".$log_level.", SCREEN\n";   
$conf = $log_level_conf.$conf;

Log::Log4perl::Logger::create_custom_level("RESULT", "WARN");
Log::Log4perl::init( \$conf );

my $log = Log::Log4perl::get_logger(__FILE__);

$log->info("INFO");
$log->debug("DEBUG");
$log->error("ERROR");
$log->warn("WARN");
$log->fatal("FATAL");
$log->trace("TRACE");

print "\n\n";

$log->info("Im doing foo");
$log->debug( "doing foo");
$log->trace( "crap from foo");
$log->info( "Im doing bar");
$log->debug( "Im doing foo");
$log->trace( "crap from bar");

$log->log('RESULT', "Foo and bar done");  

I´m getting a priority RESULT isn't numeric at test_logs.pl line 60. exception

smonff
  • 3,399
  • 3
  • 36
  • 46
nck
  • 1,673
  • 16
  • 40
  • @amit thanks for this info. Could you please post this as an actual answer so it can be marked as correct and benefit other people too? Also, instead of just linking, write the full self-contained answer here on Stack Overflow (copy the content from the other site) so that even if that site's content goes away, the answer can still be read here. Those are the rules for engaging Stack Overflow to keep it successful. Thank you :-) – Will Sheppard Aug 13 '20 at 18:12
  • 1
    @WillSheppard thanks for the information. I will follow the guidelines. – amit bhosale Aug 14 '20 at 04:01
  • @amitbhosale could you provide an example that works? I have not been able to make it work adding the config to `$conf` `Log::Log4perl::Logger::create_custom_level('RESULT', "DEBUG");` and `$log->log('RESULT', "Foo and bar done");` I keep getting a `priority RESULT isn't numeric at test_logs.pl line 52.` – nck Aug 14 '20 at 09:45

2 Answers2

2

Finally I have been able to make it work thanks to the suggestion of @amit bhosale. As he didn´t send a response I´ll put this test example if anyone needs it:

use strict;
use warnings;
use Log::Log4perl;
use Win32::Console::ANSI;

my $result_file = "results.log";
my $conf = <<EOT;

    log4perl.category = FATAL, AppResult

    log4perl.appender.SCREEN         = Log::Log4perl::Appender::ScreenColoredLevels
    log4perl.appender.SCREEN.layout  = Log::Log4perl::Layout::PatternLayout
    log4perl.appender.SCREEN.color.INFO = bright_white    
    log4perl.appender.SCREEN.color.WARN = bright_yellow    
    log4perl.appender.SCREEN.color.ERROR = bright_red    
    log4perl.appender.SCREEN.layout.ConversionPattern = [%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5p] %m  %50C::%L %n
    
    # Filter to match RESULT
    log4perl.filter.MatchResult               = Log::Log4perl::Filter::LevelMatch
    log4perl.filter.MatchResult.LevelToMatch  = RESULT
    log4perl.filter.MatchResult.AcceptOnMatch = true
    
    # Custom RESULT logs     
    log4j.appender.AppResult            = Log::Log4perl::Appender::File
    log4j.appender.AppResult.filename   = $result_file
    log4j.appender.AppResult.Filter     = MatchResult
    log4j.appender.AppResult.layout     = Log::Log4perl::Layout::SimpleLayout
    log4j.category.result               = RESULT, AppResult
    log4j.additivity.result             = 0


);
EOT

my $log_level = "TRACE";
my $log_level_conf = "log4perl.category.".__FILE__." = ".$log_level.", SCREEN\n";   
$conf = $log_level_conf.$conf;


Log::Log4perl::Logger::create_custom_level("RESULT", "FATAL");
Log::Log4perl::init( \$conf );


my $log = Log::Log4perl::get_logger(__FILE__);
my $result = Log::Log4perl->get_logger("result");

$log->info("INFO");
$log->debug("DEBUG");
$log->error("ERROR");
$log->warn("WARN");
$log->fatal("FATAL");
$log->trace("TRACE");

print "\n\n";

$log->info("Im doing foo");
$log->debug( "doing foo");
$log->error( "error in foo");
$log->trace( "crap from foo");
$log->info( "Im doing bar");
$log->debug( "Im doing foo");
$log->trace( "crap from bar");

$result->result("this is a result message");

I´m not really good with understanding this library, but it seems that in the config you set the category to the minimum level, I wrote FATAL.

Then you create a filter for a new filter to only show the custom level. Then the appender to use that custom level and then you create a subcategory which you call instead of the main $log only for the results.

I made it work from using as reference this tests: https://github.com/apple-opensource-mirror/CPANInternal/blob/35e8475f777c3c6eb7d4629f37daac0a1d2ea10e/Log-Log4perl-1.44/t/025CustLevels.t

nck
  • 1,673
  • 16
  • 40
2

Another way to achieve (without creating custom level). (above solution is valid this is just another way)

Created log.conf file

############################################################
# A simple root logger with a Log::Log4perl::Appender::File 
# file appender in Perl.
############################################################
#There are six predefined log levels: FATAL, ERROR, WARN, INFO, DEBUG, and TRACE
# (in descending priority). Your configured logging level has to at least match 
#the priority of the logging message.

#If your configured logging level is TRACE, then messages logged with info(), 
#debug(), and trace() fatal(), error() and warn() will make their way through, 
#because their priority is higher or equal than the configured setting.

#This enables messages of priority TRACE or higher in the root hierarchy
# if a function/method wants a reference to the logger, it just calls the Logger's static get_logger($category) 
#method to obtain a reference to the one and only possible logger object 
#of a certain category.
log4perl.category.My.SCREEN = TRACE, Screen
#Appenders will be triggered whenever the configured logging 
#level requires a message to be logged
# log a message (display) on screen
log4perl.appender.Screen        = Log::Log4perl::Appender::ScreenColoredLevels
log4perl.appender.Screen.layout  = Log::Log4perl::Layout::PatternLayout
log4perl.appender.Screen.color.INFO = bright_white    
log4perl.appender.Screen.color.WARN = bright_yellow    
log4perl.appender.Screen.color.ERROR = bright_red    
log4perl.appender.Screen.layout.ConversionPattern = [%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5p] %m  %50C::%L %n

# RESULT appender
log4perl.category.My.Result= INFO, resultlog
# log a message to log file (in this case app.result file)
log4perl.appender.resultlog= Log::Log4perl::Appender::File
log4perl.appender.resultlog.filename = app.result
log4perl.appender.resultlog.mode=append
log4perl.appender.resultlog.layout=PatternLayout
log4perl.appender.resultlog.layout.ConversionPattern=[%p] %F %L %c - %m%n 

Main perl script

use strict;
use warnings;
use Log::Log4perl;
use Win32::Console::ANSI;

#configuration file is saved as log.conf, you need to read it in the startup section of your code
# After this done somewhere in the code, you can retrieve logger objects anywhere in the code.
# provide log configuration file path
Log::Log4perl->init("log.conf");

#Log::Log4perl uses categories to determine if a log statement 
#in a component should be executed or suppressed at the current logging level. 
#Most of the time, these categories are just the classes the log statements
my $log_screen = Log::Log4perl->get_logger("My::SCREEN");
my $log_result = Log::Log4perl->get_logger("My::Result");
# sample logging statement
$log_screen->info("INFO");
$log_screen->debug("DEBUG");
$log_screen->error("ERROR");
$log_screen->warn("WARN");
$log_screen->fatal("FATAL");
$log_screen->trace("TRACE");
# result
$log_result->info("this is a result message1");

Output to file (example)

[INFO] log.pl 24 My.Result - this is a result message1

Output to Screen (different colour)

[2020-08-16 20:52:40.721] [INFO ] INFO                                                main::16
[2020-08-16 20:52:40.723] [DEBUG] DEBUG                                                main::17
[2020-08-16 20:52:40.723] [ERROR] ERROR                                                main::18
[2020-08-16 20:52:40.725] [WARN ] WARN                                                main::19
[2020-08-16 20:52:40.726] [FATAL] FATAL                                                main::20
[2020-08-16 20:52:40.727] [TRACE] TRACE                                                main::21
amit bhosale
  • 482
  • 4
  • 9