# Copyright 2008-2014 Cumulus Systems Incorporated.
# All Rights Reserved.

# This module provide functions for logging messages from a Perl script. We are using log4Perl module to generate logs from Perl scripts.
# Here log file will be created in "<base folder>/logs/<probe type>Probe" folder. Log file name will be: <probe_type>Probe.log
# like netappProbe.log. Once the size of current log file reaches 10 MB, logger will rename this file to <probe_type>Probe.log.<n> and
# create new <probe_type>Probe.log file. There will be maximum of 6 files exist at a time.

# Name of the Package.
package commonLog4PerlModule;

# It is used for strict compilation.
use strict;

# It helps in debugging.
use warnings;

# This is used to create folder recursively.
use File::Path qw(mkpath);

# This is used for using ready made log utility functions.
use Log::Log4perl;

# It is used to get base file name from the complete file name.
use File::Basename;
# ---------------------------------------------------------------------------------------------------------------------------------------------------
# GLOBAL Variables.
# We kept these variables out as getLogFileName() function can't access class member variables.
# ---------------------------------------------------------------------------------------------------------------------------------------------------
# Base folder where we will generate logs. In this folder, we will create "<probe-type>" folder where actual logs file will get created.
my $baseFolder = "";

# This variable stores probe type.
my $probeType = "";

# This stores an instance of commonLog4PerlModule class.
my $commLog4PerlModule = undef;

# --------------------------------------------------------------------------------------------------------------------------------------------------
# Sub-routines
# --------------------------------------------------------------------------------------------------------------------------------------------------
# Constructor of class commonLogModule.
#
# @param :
#   $_[0] - [In] Class name.
#   $_[1] - [In] Base log folder.
#   $_[2] - [In] Probe Type like NetApp.
#   $_[3] - [In] Probe Id "192.168.20.100".
#
# @affected global variables :
#   $baseFolder
#   $probeType
#
# @return :
#   Instance of commonLogModule class.
sub getInstance {
    # This variable stores package name.
    my $class = $_[0];

    # This variable stores base folder in which we generate logs.
    $baseFolder = $_[1];

    # This variable stores probe type.
    $probeType = $_[2];

    # This variable stores probe id.
    my $probeId = $_[3];

    # This variable stores final message Information.
    my $finalMessageInfo = "[".$probeType."-".$probeId."]";

    # It store the content of Configuration file.
    my $conf = q(
            log4perl.logger=DEBUG,LOG
            log4perl.appender.LOG=Log::Dispatch::FileRotate
            log4perl.appender.LOG.filename=sub{return commonLog4PerlModule::getLogFileName();}
            log4perl.appender.LOG.mode=append
            log4perl.appender.LOG.autoflush=1
            log4perl.appender.LOG.max=5
            log4perl.appender.LOG.size=1024000000
            log4perl.appender.LOG.layout=Log::Log4perl::Layout::PatternLayout
            log4perl.appender.LOG.layout.ConversionPattern=[%d] %m %n);

    # Read the log configuration string.
    Log::Log4perl::init(\$conf);

    # Get the logger object.
    my $logger = Log::Log4perl->get_logger();

    # Create class instance.
    $commLog4PerlModule = bless {
        logInfo => $finalMessageInfo,
        logger => $logger,
    }, shift unless $commLog4PerlModule;

    return $commLog4PerlModule;
}

# When the constructor of commonLogModule get invoked, it is not necessary that the value of $probeId is available at that time. So in that case,
# caller can invoke this method to update object's "logInfo" value so that proper log message can be formed.
#
# @param :
#   $_[0] - [In] Object Reference.
#   $_[1] - [In] Probe Id.
#
# @affected global variables :
#   None
#
# @return :
#   Nothing
sub setProbeId {
    # Update the final message information.
    my $finalMessageInfo = "[".$probeType."-".$_[1]."]";

    # Update object member variable "logInfo".
    $_[0]->{logInfo} = $finalMessageInfo;
}

# This function is used to log a debug message in the log file. It logs the message in the following format:
# [YYYY/MM/DD HH:MM:ss] [<ProbeType>-<ProbeId>] [<Log Source>:<Line Number>] [Debug] <Log message>
#
# @param :
#   $_[0] - [In] Object Reference.
#   $_[1] - [In] Log Message.
#
# @affected global variables :
#   None
#
# @return :
#   Nothing
sub debug {
    # This variable stores the file name from where the log is coming.
    my $logSource = basename((caller(0))[1]);

    # This variable stores the line number in the file from where the log is coming.
    my $lineNumber = (caller(0))[2];

    # Get the logger object from the commonLogModule class object reference.
    my $logger = $_[0]->{logger};

    # Create the final log message.
    my $logMessage = $_[0]->{logInfo}." [$logSource:$lineNumber] [Debug] ".$_[1];

    # Dump the final log message in the log file.
    $logger->debug($logMessage);
}

# This function is used to log an info message in the log file. It logs the message in the following format:
# [YYYY/MM/DD HH:MM:ss] [<ProbeType>-<ProbeId>] [<Log Source>:<Line Number>] [Info] <Log message>
#
# @param :
#   $_[0] - [In] Object Reference.
#   $_[1] - [In] Log Message.
#
# @affected global variables :
#   None
#
# @return :
#   Nothing
sub info {
    # This variable stores the file name from where the log is coming.
    my $logSource = basename((caller(0))[1]);

    # This variable stores the line number in the file from where the log is coming.
    my $lineNumber = (caller(0))[2];

    # Get the logger object from the commonLogModule class object reference.
    my $logger = $_[0]->{logger};

    # Create the final log message.
    my $logMessage = $_[0]->{logInfo}." [$logSource:$lineNumber] [Info] ".$_[1];

    # Dump the final log message in the log file.
    $logger->info($logMessage);
}

# This function is used to log a warning message in the log file. It logs the message in the following format:
# [YYYY/MM/DD HH:MM:ss] [<ProbeType>-<ProbeId>] [<Log Source>:<Line Number>] [Warn] <Log message>
#
# @param :
#   $_[0] - [In] Object Reference.
#   $_[1] - [In] Log Message.
#
# @affected global variables :
#   None
#
# @return :
#   Nothing
sub warn {
    # This variable stores the file name from where the log is coming.
    my $logSource = basename((caller(0))[1]);

    # This variable stores the line number in the file from where the log is coming.
    my $lineNumber = (caller(0))[2];

    # Get the logger object from the commonLogModule class object reference.
    my $logger = $_[0]->{logger};

    # Create the final log message.
    my $logMessage = $_[0]->{logInfo}." [$logSource:$lineNumber] [Warn] ".$_[1];

    # Dump the final log message in the log file.
    $logger->warn($logMessage);
}

# This function is used to log an error message in the log file. It logs the message in the following format:
# [YYYY/MM/DD HH:MM:ss] [<ProbeType>-<ProbeId>] [<Log Source>:<Line Number>] [Error] <Log message>
#
# @param :
#   $_[0] - [In] Object Reference.
#   $_[1] - [In] Log Message.
#
# @affected global variables :
#   None
#
# @return :
#   Nothing
sub error {
    # This variable stores the file name from where the log is coming.
    my $logSource = basename((caller(0))[1]);

    # This variable stores the line number in the file from where the log is coming.
    my $lineNumber = (caller(0))[2];

    # Get the logger object from the commonLogModule class object reference.
    my $logger = $_[0]->{logger};

    # Create the final log message.
    my $logMessage = $_[0]->{logInfo}." [$logSource:$lineNumber] [Error] ".$_[1];

    # Dump the final log message in the log file.
    $logger->error($logMessage);
}

# This function is used to log a fatal message in the log file. It logs the message in the following format:
# [YYYY/MM/DD HH:MM:ss] [<ProbeType>-<ProbeId>] [<Log Source>:<Line Number>] [Fatal] <Log message>
#
# @param :
#   $_[0] - [In] Object Reference.
#   $_[1] - [In] Log Message.
#
# @affected global variables :
#   None
#
# @return :
#   Nothing
sub fatal {
    # This variable stores the file name from where the log is coming.
    my $logSource = basename((caller(0))[1]);

    # This variable stores the line number in the file from where the log is coming.
    my $lineNumber = (caller(0))[2];

    # Get the logger object from the commonLogModule class object reference.
    my $logger = $_[0]->{logger};

    # Create the final log message.
    my $logMessage = $_[0]->{logInfo}." [$logSource:$lineNumber] [Fatal] ".$_[1];

    # Dump the final log message in the log file.
    $logger->fatal($logMessage);
}

# This function is used to return the log file name.
#
# @param :
#   None
#
# @affected global variables :
#   None
#
# @return :
#   Log File Name.
sub getLogFileName {
    # Set Probe log folder path.
    my $logFolder = $baseFolder."/".lc($probeType)."Probe";

    # If log folder does not exist then create it.
    if (! (-e $logFolder))
    {
        mkpath($logFolder);
    }

    # Set the name of log file.
    my $logFile = $logFolder."/".lc($probeType)."Probe.log";

    return $logFile;
}

# The file must return true ("1") as the last statement to indicate successful execution of any initialization code. So it's customary to end such a
# file with 1 unless we are sure that we will return true otherwise. But it's better just to put the 1;, in case we add more statements.
1;