// Copyright 2020 Siemens Digital Industries Software
// ==================================================
// Copyright 2011.
// Siemens Product Lifecycle Management Software Inc.
// All Rights Reserved.
// ==================================================
// Copyright 2020 Siemens Digital Industries Software
/**
    @file Logger.hxx
    Defines a facility to write messages to the Teamcenter system log file (syslog).

    It maintains a hierarchical tree of named loggers with distinct logging priority levels.
    <br/>When a log message is issued by the caller, the message is written to the log file
    if the priority level of the message is at least the level of the logger.

    <B>Example</B>
    <CODE><PRE>
        std::string str("Teamcenter.MyComponent.MyCode");
        Logger *logger = Logger::getLogger(str);
        if (logger->isErrorEnabled())
        {
            logger->error("Could not read the configuration file");
        }
    </PRE></CODE>

    <B>Configuration</B>
    <br/>Loggers are configured upon startup by a file named logger.properties in the TC_DATA directory
    or in a directory defined by the TC_LOGGER_CONFIGURATION environment variable.

    Each line of the file is of the form:
    <code>logging.logger.&lt;hierarchical_logger_name&gt;=&lt;level&gt;</code>
    <br/>If a logger does not have a specific priority level configured, it inherits its parent's level.

    <B>Example</B>
    <CODE><PRE>
    logging.logger.Teamcenter=INFO
    logging.logger.Teamcenter.SOA=INFO
    logging.logger.Teamcenter.SOA.Gateway=TRACE
    </PRE></CODE>

    In production, loggers should usually be configured to INFO priority level.
    <br/>For additional detail and diagonostics, loggers may be configured to DEBUG or TRACE priority level.
    A logger can be configured to write no messages by setting the priority level to OFF.

    In a Teamcenter J2EE Middle Tier environment, the administrator can change the priority level
    of a Teamcenter session's loggers dynamically while the session is running.
    See the System Administration Guide for more information.

    <B>Format</B>
    Log messages are written in a fixed format with the following fields separated by dashes:
    <ul>
    <li>Priority Level
    <li>Date/Time in UTC form
    <li>Log Correlation ID
    <li>Error Code (if supplied) and its localized error text (when applicable)
    <li>Message
    <li>Logger name
    <li>File name and line number (if supplied)
    </ul>

    <B>Examples</B>
    <CODE><PRE>
    TRACE - 2010/10/15-22:07:29 UTC - ws010.40171.02.usr1.00008 - Something Happened. - Teamcenter.Logging.Sample at D:/workdir/dynalog/src/foundation/mld/cppunit/LoggerTestFixture.cxx(825)
    </PRE></CODE>

    Additionally, information about errors added to the error stack (error, error message and traceback)
    will also be printed to the logger if the priority level for "Teamcenter.MonitorDebug.mld" is set to
    @c LOG_INFO or lower, and if the environment variable @c TC_CATCH_THIS_IFAIL is set as follows:
    <ul>
    <li>value of @c -42: all errors are logged.
    <li>value of an error base (i.e. a multiple of 1000): all errors using the error base (error_base+0 to
    error_base+999) are logged.
    <li>value of a specific error: only the addition of this error to the error stack is logged.
    </ul>

    <b>Call Stack Traces</b>
    <br/>To assist in development and debugging environments, a call stack trace is written for each
    high priority log message (fatal, error, warn). A maximum of 100 stack traces
    will be written in a session.
**/

#ifndef TEAMCENTER_LOGGING_LOGGER_HXX
#define TEAMCENTER_LOGGING_LOGGER_HXX

#include <string>
#include <vector>
#include <cstdarg>
#include <common/tc_deprecation_macros.h>

#include <mld/libmld_exports.h>

// The string representation of the allowed appenders.
#define APPENDER_SYSLOG_STR     "syslog"
#define APPENDER_STDOUT_STR     "stdout" 
#define APPENDER_STDERR_STR     "stderr"


namespace Teamcenter
{
    namespace Logging
    {
        class MLD_API Logger
        {
            public:
                friend class LoggerTree;
                friend class Node;

                /**
                 * Log message priority levels
                 */
                enum level{LOG_TRACE=1,LOG_DEBUG=2,LOG_INFO=3,LOG_WARN=4,LOG_ERROR=5,LOG_FATAL=6,LOG_NOTE=7,LOG_OFF=8};

                /**
                 * Appender (The logging framework uses the word "appender" to declare the output destination.)
                 */
                enum appender{APPENDER_SYSLOG=0, APPENDER_STDOUT=1, APPENDER_STDERR=2};

                /**
                 * Get the logger name
                 *
                 * @return string returns the logger name
                 */
                std::string getLoggerName() const { return loggerName; };

                /**
                 * Set the log level for the logger.
                 *
                 * @param lvl The new log level for the logger.
                 */
                void setLogLevel(level lvl);
                /**
                  * Return the log level set for the logger.
                  *
                  * @return level return the log level set for the logger.
                  */
                level getLogLevel() const { return logLevel; };

                /**
                  * Check whether log level trace is enabled
                  *
                  * @return boolean true if trace is enabled, false otherwise
                  */
                 bool isTraceEnabled() const { return ( LOG_TRACE >= logLevel ); };
                /**
                  * Check whether log level debug is enabled
                  *
                  * @return boolean true if debug is enabled, false otherwise
                  */
                 bool isDebugEnabled() const { return ( LOG_DEBUG >= logLevel ); };

                /**
                  * Check whether log level info is enabled
                  *
                  * @return boolean true if info is enabled, false otherwise
                  */
                 bool isInfoEnabled() const { return ( LOG_INFO >= logLevel ); };

                /**
                  * Check whether log level warn is enabled
                  *
                  * @return boolean true if warn is enabled, false otherwise
                  */
                 bool isWarnEnabled() const { return ( LOG_WARN >= logLevel ); };

                /**
                  * Check whether log level error is enabled
                  *
                  * @return boolean true if error is enabled, false otherwise
                  */
                 bool isErrorEnabled() const { return ( LOG_ERROR >= logLevel ); };

                /**
                  * Check whether log level fatal is enabled
                  *
                  * @return boolean true if fatal is enabled, false otherwise
                  */
                 bool isFatalEnabled() const { return ( LOG_FATAL >= logLevel ); };

                 /**
                  * Log a fatal message.
                  *
                  * @param msg       The message to log.
                  */
                 void fatal(const std::string &msg) { log(LOG_FATAL, msg); };

                /**
                  * Log a fatal message.
                  *
                  * @param errorCode The error code.
                  * @param msg       The message to log.
                  */
                 void fatal(const int errorCode, const std::string &msg) { log(LOG_FATAL, errorCode, msg); };

                 /**
                  * Log a fatal message.
                  *
                  * @param file      The file name logging a message.
                  * @param line      The line number from which a call to log a message called.
                  * @param errorCode The error code.
                  * @param msg       The message to log.
                  */
                 void fatal( const char* file, const int line, const int errorCode, const std::string &msg) { log(LOG_FATAL, file, line, errorCode, msg); };

                 /**
                  * Log a fatal message, formatting the message with a printf-style
                  * control string and variable arguments.
                  *
                  * @param file      The name of the source code file logging the message.
                  * @param line      The line number of the file logging the message.
                  * @param errorCode The error code.
                  * @param control_string    The message control string.
                  * @param ...       Substitution arguments
                  */
                 void fatal( const char* file, const int line, const int errorCode, const char* control_string, ... );

                /**
                  * Log an error message.
                  *
                  * @param msg       The message to log.
                  */
                 void error(const std::string &msg) { log(LOG_ERROR, msg); };

                 /**
                  * Log an error message.
                  *
                  * @param errorCode The error code.
                  * @param msg       The message to log.
                  */
                 void error(const int errorCode, const std::string &msg) { log(LOG_ERROR, errorCode, msg); };

                 /**
                  * Log an error message.
                  *
                  * @param file      The file name logging a message.
                  * @param line      The line number from which a call to log a message called.
                  * @param errorCode The error code.
                  * @param msg       The message to log.
                  */

                 void error( const char* file, const int line, const int errorCode, const std::string &msg) { log(LOG_ERROR, file, line, errorCode, msg); };
                 /**
                  * Log an error message, formatting the message with a printf-style
                  * control string and variable arguments.
                  *
                  * @param file      The name of the source code file logging the message.
                  * @param line      The line number of the file logging the message.
                  * @param errorCode The error code.
                  * @param control_string    The message control string.
                  * @param ...       Substitution arguments
                  */
                 void error( const char* file, const int line, const int errorCode, const char* control_string, ... );

                /**
                  * Log a warning message
                  *
                  * @param msg       The message to log.
                  */
                 void warn(const std::string &msg) { log(LOG_WARN, msg); };

                 /**
                  * Log a warning message
                  *
                  * @param errorCode The error code.
                  * @param msg       The message to log.
                  */
                 void warn(const int errorCode, const std::string &msg) { log(LOG_WARN, errorCode, msg); };

                 /**
                  * Log a warn message.
                  *
                  * @param file      The file name logging a message.
                  * @param line      The line number from which a call to log a message called.
                  * @param errorCode The error code.
                  * @param msg       The message to log.
                  */
                 void warn ( const char* file, const int line, const int errorCode, const std::string &msg) { log(LOG_WARN, file, line, errorCode, msg); };

                 /**
                  * Log a warning message, formatting the message with a printf-style
                  * control string and variable arguments.
                  *
                  * @param file      The name of the source code file logging the message.
                  * @param line      The line number of the file logging the message.
                  * @param errorCode The error code.
                  * @param control_string    The message control string.
                  * @param ...       Substitution arguments
                  */
                 void warn( const char* file, const int line, const int errorCode, const char* control_string, ... );

                 /**
                  * Log an info message.
                  *
                  * @param msg       The message to log.
                  */
                 void info(const std::string &msg) { log(LOG_INFO, msg); };

                 /**
                  * Log an info message.
                  *
                  * @param file      The file name logging a message.
                  * @param line      The line number from which a call to log a message called.
                  * @param msg       The message to log.
                  */
                 void info ( const char* file, const int line, const std::string &msg) { log(LOG_INFO, file, line, msg); };

                 /**
                  * Log an info message.
                  *
                  * @param file      The file name logging a message.
                  * @param line      The line number from which a call to log a message called.
                  * @param errorCode A error or message code.
                  * @param msg       The message to log.
                  */
                 void info ( const char* file, const int line, const int errorCode, const std::string &msg) { log(LOG_INFO, file, line, errorCode, msg); };

                 /**
                  * Log an info message, formatting the message with a printf-style
                  * control string and variable arguments.
                  *
                  * @param file      The name of the source code file logging the message.
                  * @param line      The line number of the file logging the message.
                  * @param control_string    The message control string.
                  * @param ...       Substitution arguments
                  */
                 void info( const char* file, const int line, const char *control_string, ... );

                /**
                  * Log a debug message.
                  *
                  * @param msg       The message to log.
                  */
                 void debug(const std::string &msg) { log(LOG_DEBUG, msg); };

                 /**
                  * Log a debug message.
                  *
                  * @param file      The file name logging a message.
                  * @param line      The line number from which a call to log a message called.
                  * @param msg       The message to log.
                  */
                 void debug( const char* file, const int line, const std::string &msg) { log(LOG_DEBUG, file, line, msg); };

                 /**
                  * Log a debug message.
                  *
                  * @param file      The file name logging a message.
                  * @param line      The line number from which a call to log a message called.
                  * @param errorCode A error or message code.
                  * @param msg       The message to log.
                  */
                 void debug ( const char* file, const int line, const int errorCode, const std::string &msg) { log(LOG_DEBUG, file, line, errorCode, msg); };

                 /**
                  * Log a debug message, formatting the message with a printf-style
                  * control string and variable arguments.
                  *
                  * @param file      The name of the source code file logging the message.
                  * @param line      The line number of the file logging the message.
                  * @param control_string    The message control string.
                  * @param ...       Substitution arguments
                  */
                 void debug( const char* file, const int line, const char* control_string, ... );

                /**
                  * Log a trace message.
                  *
                  * @param msg       The message to log.
                  */
                 void trace(const std::string &msg) { log(LOG_TRACE, msg); };

                 /**
                  * Log a trace message.
                  *
                  * @param file      The file name logging a message.
                  * @param line      The line number from which a call to log a message called.
                  * @param msg       The message to log.
                  */
                 void trace( const char* file, const int line, const std::string &msg) { log(LOG_TRACE, file, line, msg); };

                 /**
                  * Log a trace message.
                  *
                  * @param file      The file name logging a message.
                  * @param line      The line number from which a call to log a message called.
                  * @param errorCode A error or message code.
                  * @param msg       The message to log.
                  */
                 void trace ( const char* file, const int line, const int errorCode, const std::string &msg) { log(LOG_TRACE, file, line, errorCode, msg); };

                 /**
                  * Log a trace message, formatting the message with a printf-style
                  * control string and variable arguments.
                  *
                  * @param file      The name of the source code file logging the message.
                  * @param line      The line number of the file logging the message.
                  * @param control_string    The message control string.
                  * @param ...       Substitution arguments
                  */
                 void trace( const char* file, const int line, const char *control_string, ... );

                 /**
                  * @deprecated #Teamcenter::Logging::Logger::note deprecated in Teamcenter 11.4.
                  * <br/>Use an appropriate logging level method: fatal, error, warn, info, debug, trace.
                  *
                  * Log a note message.
                  * A note message is always written to the log file regardless
                  * of the priority level setting of the logger.
                  *
                  * @param msg       The message to log.
                  */
                 TC_DEPRECATED_NO_REPLACEMENT( "11.4", "Teamcenter::Logging::Logger::note" )
                 void note(const std::string &msg) { log(LOG_NOTE, msg); };

                 /**
                  * @deprecated #Teamcenter::Logging::Logger::note deprecated in Teamcenter 11.4.
                  * <br/> Use an appropriate logging level method: fatal, error, warn, info, debug, trace.
                  *
                  * Log a note message, formatting the message with a printf-style
                  * control string and variable arguments.
                  * A note message is always written to the log file regardless
                  * of the priority level setting of the logger.
                  *
                  * @param file      The name of the source code file logging the message.
                  * @param line      The line number of the file logging the message.
                  * @param control_string    The message control string.
                  * @param ...       Substitution arguments
                  */
                 TC_DEPRECATED_NO_REPLACEMENT( "11.4", "Teamcenter::Logging::Logger::note" )
                 void note( const char* file, const int line, const char *control_string, ... );

                /**
                  * Check whether the input level is
                  * enabled for logging and log the messages.
                  *
                  * @param lvl              The priority level of the message
                  * @param msg              The message to log
                  */
                 void log (const Logger::level lvl, const std::string &msg);

                 /**
                  * Checks whether the input level is
                  * enabled for logging and log the message
                  *
                  * @param lvl              The priority level of the message
                  * @param errorCode        An error code, in string form
                  * @param msg              The message to log.
                  */
                 void log (const Logger::level lvl, const std::string errorCode, const std::string &msg);

                 /**
                  * Checks whether the input level is
                  * enabled for logging and log the message
                  *
                  * @param lvl              The priority level of the message
                  * @param errorCode        An error code, in int form
                  * @param msg              The message to log.
                  */
                 void log (const Logger::level lvl, const int errorCode, const std::string &msg);

                 /**
                  * Checks whether the input level is
                  * enabled for logging and log the message
                  * Also add the file and line number from where
                  * the log message is logged.
                  *
                  * @param lvl              The priority level of the message
                  * @param file             The file name logging a message.
                  * @param line             The line number from which a call to log a message called.
                  * @param errorCode        An error code, in int form
                  * @param msg              The message to log.
                  */
                 void log ( const Logger::level lvl, const char *file, const int line, const int errorCode, const std::string &msg);

                 /**
                  * Checks whether the input level is
                  * enabled for logging and log the message
                  * Also add the file and line number from where
                  * the log message is logged.
                  *
                  * @param lvl              The priority level of the message
                  * @param file             The file name logging a message.
                  * @param line             The line number from which a call to log a message called.
                  * @param msg              The message to log.
                  */
                 void log ( const Logger::level lvl, const char *file, const int line, const std::string &msg);

                /**
                 * Log the message with no stack trace.
                 * Add the file and line number from where
                 * the log message is logged.
                 *
                 * @param lvl              The priority level of the message
                 * @param file             The file name logging a message.
                 * @param line             The line number from which a call to log a message called.
                 * @param errorCode        An error code, in int form
                 * @param msg              The message to log.
                 */
                 void logNoStackTrace ( const Logger::level lvl, const char *file, const int line, const int errorCode, const std::string &msg);

                /**
                  * Checks whether the logger level
                  * is set to the input level
                  *
                  * @param lvl            The priority level of the message
                  * @return  the boolean whether input level is set
                  */
                 bool isEnabled (const Logger::level lvl) const { return ( lvl >= logLevel ); };
                /**
                  * If a logger exists for the key in the tree, a pointer
                  * to the existing logger. Otherwise created a new logger
                  * object and return a pointer to the new logger
                  *
                  * @param key            The logger name
                  * @return               Logger pointer.
                  */
                 static Logger* getLogger(const std::string &key);

                /**
                 * Log a message, formatting the message with a printf-style,
                 * control string and a variable number of arguments.
                 *
                 * @param lvl              The priority level of the message
                 * @param errorCode        An error code, in int form
                 * @param control_string   The control string to be printed in the log
                 */
                void logf( const Logger::level lvl, const int errorCode, const char *control_string, ... ) ;

                /**
                 * Log a message, formatting the message with a printf-style,
                 * control string and a variable number of arguments.
                 *
                 * @param lvl              The priority level of the message
                 * @param file             The file name logging a message.
                 * @param line             The line number from which a call to log a message called.
                 * @param errorCode        An error code, in int form
                 * @param control_string   The control string to be printed in the log
                 */
                void logf( const Logger::level lvl, const char *file, const int line, const int errorCode, const char *control_string, ... );

                /**
                 * Print formatted text to the log file,
                 * without log message fields or terminating newline,
                 * if the logger is enabled for the given priority.
                 *
                 * @param lvl              The priority level of the message
                 * @param control_string   The control string to be printed in the log
                 */
                void printf( const Logger::level lvl, const char *control_string, ... );

                /**
                 * Print formatted text to the log file,
                 * without log message fields or a terminating newline.
                 * The text will be printed regardless of the logger's level.
                 *
                 * @param control_string   The control string to be printed in the log
                 */
                void printf( const char *control_string, ... );

                /**
                 * Get the logger object from the tree given the logger name.
                 *
                 * @param key       logger name
                 */
                static Logger* getLoggerObject(const std::string &key);







                // this method sets the given name to the logger.
                void setLoggerName(const std::string &name) { loggerName=name; };

                //Returns all the child loggers for a logger.
                static std::vector<Logger *> getListOfLoggers(std::string key);

                // returns the log level inheritance flag
                bool isLogLevelInherited () const { return logLevelInherited; };

                // sets the log level inheritance flag
                void setInheritanceLevel ( bool isInherited) { logLevelInherited = isInherited; };

                // set the logLevelInherited member variable
                // if set to true log level will be inherited from parent.
                // if set to false log level will not be inherited from parent.
                void setLogLevelInheritance (bool isInherited);

            private:

                // unique name for each logger.
                std::string loggerName;

                // logger level
                level logLevel;

                // We used a static data member to remember the choice of the appender, which is defined in the logger property file.
                // The syntax is as the following, if there is no appender specified, or if the value is invalid, syslog will be the appender
                //    logging.rootLogger=INFO{,syslog|stderr|stdout}
                // Note that currently we only support a single choice of appender defined inside the rootLogger only. All loggers will inherit the same appender as the rootLogger.
                // We may consider supporting any individual logger setting its own appender, and even allowing multiple appenders for a single loggers.
                static int appender;
                
                // flag to check whether log level is inherited from parent.
                bool  logLevelInherited;


                // Copy constructor and assignment operator are disabled and unimplemented
                // Since a Logger need not exist outside a LoggerTree
                Logger( const Logger& );
                Logger& operator=( const Logger& );

                /**
                 * Constructor, set the logger name and
                 * gets the syslog file pointer
                 *
                 * @param name   logger name
                 */
                explicit Logger(const std::string &name);

                /**
                 * Set the logger name log level and
                 * gets the syslog file pointer
                 *
                 * @param name   name for the logger name
                 * @param lvl    log level to be set for this logger
                 */
                Logger(const std::string &name, level lvl);

                /**
                 * get the time in UTC format
                 *
                 */
                std::string getTime() const;

                 /**
                 * get the log correlation id
                 *
                 */
                std::string getId() const;

                /**
                 * Format the first portion of the log message
                 *
                 */
                std::string formatLogEntry ( const Logger::level lvl, const std::string errCode ) const;
                std::string formatLogEntry ( const Logger::level lvl ) const;
                std::string formatLogEntry ( const Logger::level lvl, const int errCode ) const;

                /**
                 * Obtain the file pointer of the log file
                 */
                FILE* getLogFilePointer();

                /**
                 * Print the message to the syslog file.
                 */
                void writeToLog( const std::string &str );

                /**
                 * Add file name and the line number to the
                 * message to be logged.
                 */
                void appendFileAndLine(std::string &str, const char* file, const int line) const;

                ~Logger();

                Logger();

                /**
                 * Set the log level for the logger.
                 *
                 * @param lvl  the log level for the logger.
                 */
                void setLogLevelAttribute(level lvl) { logLevel = lvl; logLevelInherited = false; };

                /**
                 * Check for invalid variable arguments, and return a substitute message if necessary.
                 * @param control_string The control string to be written
                 * @param ap1 The variable argument list of substitution values
                 * @return null if OK, or a safe string that can be written in place of the original.
                 */
                std::string substituteForInvalidArgs ( const char* control_string, va_list ap1 );

                /**
                 * Check whether any %s format spec has a null argument.
                 * This would cause vfprintf (and printf) to seg fault
                 * on some platforms (SUN, Linux, AIX), though not on others (Windows, HP)
                 *
                 * @param control_string The control string to be written
                 * @param ap1 The variable argument list of substitution values
                 * @return 0 if OK, or the argument number of the first invalid null substitution for a %s specification.
                 */
                int checkForNullPointer ( const char* control_string, va_list ap1);

                void logv( const Logger::level lvl, const char* file, const int line, const int errorCode, const char* control_string, std::va_list va );

                /**
                 * Write a call stack trace if appropriate.
                 *
                 * @param lvl  The priority level of the message.
                 */
                void stackTrace( const Logger::level lvl );

                /**
                 * This function appends the log file to each log event if Centralized Logging is enabled on
                 * host machine. This is to group similar log lines together in Centralized Logging 
                 * by using the filename as an identifier.
                 *
                 * Enable by setting environment variable "TEAMCENTER_CENTRALIZED_LOGGING" to "TRUE" on host machine.
                 *
                 * @param str  The Log line that will have filename appended.
                 */
                 void appendFileNameForCentralizedLogging(std::string &str);

                /**
                 * static function to extract leafName from absolute path
                 *
                 * @param absolutePath Absolute path to extract lwoest folder or filename
                 */
                 static std::string getLeafName(std::string absolutePath );
        };
    }
}

#include <mld/libmld_undef.h>
#endif
