<commit_before>/*
 * Logger.cpp
 * 
 * Copyright (C) 2019 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant to the terms of a commercial license agreement
 * with RStudio, then this program is licensed to you under the following terms:
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */

#include <shared_core/Logger.hpp>

#include <boost/noncopyable.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/optional.hpp>

#include <sstream>

#include <shared_core/Error.hpp>
#include <shared_core/DateTime.hpp>
#include <shared_core/ILogDestination.hpp>

namespace rstudio {
namespace core {
namespace log {

typedef std::map<unsigned int, std::shared_ptr<ILogDestination> > LogMap;
typedef std::map<LogSection, LogMap> SectionLogMap;

namespace {

constexpr const char* s_occurredAt = "OCCURRED AT";
constexpr const char* s_loggedFrom = "LOGGED FROM";
constexpr const char* s_causedBy = "CAUSED BY";

std::ostream& operator<<(std::ostream& io_ostream, LogLevel in_logLevel)
{
   switch (in_logLevel)
   {
      case LogLevel::ERR:
      {
         io_ostream << "ERROR";
         break;
      }
      case LogLevel::WARN:
      {
         io_ostream << "WARNING";
         break;
      }
      case LogLevel::DEBUG:
      {
         io_ostream << "DEBUG";
         break;
      }
      case LogLevel::INFO:
      {
         io_ostream << "INFO";
         break;
      }
      case LogLevel::OFF:
      {
         io_ostream << "OFF";
         break;
      }
      default:
      {
         assert(false); // This shouldn't be possible
         if (in_logLevel > LogLevel::INFO)
            io_ostream << "INFO";
         else
            io_ostream << "OFF";
      }
   }

   return io_ostream;
}

std::string formatLogMessage(
   LogLevel in_logLevel,
   const std::string& in_message,
   const std::string& in_programId,
   const ErrorLocation& in_loggedFrom = ErrorLocation(),
   bool in_formatForSyslog = false)
{
   // Add the time.
   using namespace boost::posix_time;
   ptime time = microsec_clock::universal_time();

   std::ostringstream oss;
   oss << system::date_time::format(time, "%d %b %Y %H:%M:%S")
       << " [" << in_programId << "] "
       << in_logLevel
       << " "
       << in_message;

   if (in_loggedFrom.hasLocation())
      oss << s_delim << " " << s_loggedFrom << ": " << cleanDelims(in_loggedFrom.asString());

   oss << std::endl;

   return oss.str();
}

} // anonymous namespace

// Log Section =========================================================================================================
struct LogSection::Impl
{
   std::string Name;
   boost::optional<LogLevel> MaxLogLevel;
};

LogSection::LogSection(const std::string& in_name) :
   m_impl(new Impl())
{
   m_impl->Name = in_name;
}

LogSection::LogSection(const std::string& in_name, LogLevel in_logLevel) :
   m_impl(new Impl())
{
   m_impl->Name = in_name;
   m_impl->MaxLogLevel = in_logLevel;
}

bool LogSection::operator<(const LogSection& in_other) const
{
   return m_impl->Name < in_other.m_impl->Name;
}

bool LogSection::operator==(const LogSection& in_other) const
{
   return m_impl->Name == in_other.m_impl->Name;
}

LogLevel LogSection::getMaxLogLevel(LogLevel in_defaultLevel) const
{
   return boost::get_optional_value_or(m_impl->MaxLogLevel, in_defaultLevel);
}

const std::string& LogSection::getName() const
{
   return m_impl->Name;
}

// Logger Object =======================================================================================================
/**
 * @brief Class which logs messages to destinations.
 *
 * Multiple destinations may be added to this logger in order to write the same message to each destination.
 * The default log level is ERROR.
 */
struct Logger : boost::noncopyable
{
public:
   /**
    * @brief Writes a pre-formatted message to all registered destinations.
    *
    * @param in_logLevel    The log level of the message, which is passed to the destination for informational purposes.
    * @param in_message     The pre-formatted message.
 * @param in_loggedFrom         The location from which the error message was logged.
    */
   void writeMessageToDestinations(
      LogLevel in_logLevel,
      const std::string& in_message,
      const std::string& in_section = std::string(),
      const ErrorLocation& in_loggedFrom = ErrorLocation());

   /**
    * @brief Constructor to prevent multiple instances of Logger.
    */
   Logger() :
      MaxLogLevel(LogLevel::OFF),
      DefaultLogLevel(LogLevel::OFF),
      ProgramId("")
   { };

   // The maximum level of message to write across all log sections.
   LogLevel MaxLogLevel;

   // The default maximum level of message to write.
   LogLevel DefaultLogLevel;<commit_msg>fix a comment that was missing a param<commit_after>/*
 * Logger.cpp
 * 
 * Copyright (C) 2019 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant to the terms of a commercial license agreement
 * with RStudio, then this program is licensed to you under the following terms:
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */

#include <shared_core/Logger.hpp>

#include <boost/noncopyable.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/optional.hpp>

#include <sstream>

#include <shared_core/Error.hpp>
#include <shared_core/DateTime.hpp>
#include <shared_core/ILogDestination.hpp>

namespace rstudio {
namespace core {
namespace log {

typedef std::map<unsigned int, std::shared_ptr<ILogDestination> > LogMap;
typedef std::map<LogSection, LogMap> SectionLogMap;

namespace {

constexpr const char* s_occurredAt = "OCCURRED AT";
constexpr const char* s_loggedFrom = "LOGGED FROM";
constexpr const char* s_causedBy = "CAUSED BY";

std::ostream& operator<<(std::ostream& io_ostream, LogLevel in_logLevel)
{
   switch (in_logLevel)
   {
      case LogLevel::ERR:
      {
         io_ostream << "ERROR";
         break;
      }
      case LogLevel::WARN:
      {
         io_ostream << "WARNING";
         break;
      }
      case LogLevel::DEBUG:
      {
         io_ostream << "DEBUG";
         break;
      }
      case LogLevel::INFO:
      {
         io_ostream << "INFO";
         break;
      }
      case LogLevel::OFF:
      {
         io_ostream << "OFF";
         break;
      }
      default:
      {
         assert(false); // This shouldn't be possible
         if (in_logLevel > LogLevel::INFO)
            io_ostream << "INFO";
         else
            io_ostream << "OFF";
      }
   }

   return io_ostream;
}

std::string formatLogMessage(
   LogLevel in_logLevel,
   const std::string& in_message,
   const std::string& in_programId,
   const ErrorLocation& in_loggedFrom = ErrorLocation(),
   bool in_formatForSyslog = false)
{
   // Add the time.
   using namespace boost::posix_time;
   ptime time = microsec_clock::universal_time();

   std::ostringstream oss;
   oss << system::date_time::format(time, "%d %b %Y %H:%M:%S")
       << " [" << in_programId << "] "
       << in_logLevel
       << " "
       << in_message;

   if (in_loggedFrom.hasLocation())
      oss << s_delim << " " << s_loggedFrom << ": " << cleanDelims(in_loggedFrom.asString());

   oss << std::endl;

   return oss.str();
}

} // anonymous namespace

// Log Section =========================================================================================================
struct LogSection::Impl
{
   std::string Name;
   boost::optional<LogLevel> MaxLogLevel;
};

LogSection::LogSection(const std::string& in_name) :
   m_impl(new Impl())
{
   m_impl->Name = in_name;
}

LogSection::LogSection(const std::string& in_name, LogLevel in_logLevel) :
   m_impl(new Impl())
{
   m_impl->Name = in_name;
   m_impl->MaxLogLevel = in_logLevel;
}

bool LogSection::operator<(const LogSection& in_other) const
{
   return m_impl->Name < in_other.m_impl->Name;
}

bool LogSection::operator==(const LogSection& in_other) const
{
   return m_impl->Name == in_other.m_impl->Name;
}

LogLevel LogSection::getMaxLogLevel(LogLevel in_defaultLevel) const
{
   return boost::get_optional_value_or(m_impl->MaxLogLevel, in_defaultLevel);
}

const std::string& LogSection::getName() const
{
   return m_impl->Name;
}

// Logger Object =======================================================================================================
/**
 * @brief Class which logs messages to destinations.
 *
 * Multiple destinations may be added to this logger in order to write the same message to each destination.
 * The default log level is ERROR.
 */
struct Logger : boost::noncopyable
{
public:
   /**
    * @brief Writes a pre-formatted message to all registered destinations.
    *
    * @param in_logLevel        The log level of the message, which is passed to the destination for informational purposes.
    * @param in_message         The pre-formatted message.
    * @param in_section         The section to which to log this message.
    * @param in_loggedFrom      The location from which the error message was logged.
    */
   void writeMessageToDestinations(
      LogLevel in_logLevel,
      const std::string& in_message,
      const std::string& in_section = std::string(),
      const ErrorLocation& in_loggedFrom = ErrorLocation());

   /**
    * @brief Constructor to prevent multiple instances of Logger.
    */
   Logger() :
      MaxLogLevel(LogLevel::OFF),
      DefaultLogLevel(LogLevel::OFF),
      ProgramId("")
   { };

   // The maximum level of message to write across all log sections.
   LogLevel MaxLogLevel;

   // The default maximum level of message to write.
   LogLevel DefaultLogLevel;
{'fraction_non_alphanumeric': 0.1329727873637222, 'fraction_numerical': 0.000686754227830715, 'mean_word_length': 3.5120061967467078, 'pattern_counts': {'":': 2, '<': 115, '<?xml version=': 0, '>': 47, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2861847', 'n_tokens_mistral': 3409, 'n_tokens_neox': 3283, 'n_words': 1080}