Skip to content

node/logging.cpp

Attributes

Name
std::atomic_flag sgns::logging::logging_already_added ATOMIC_FLAG_INIT

Defines

Name
BOOST_LOG_USE_NATIVE_SYSLOG

Attributes Documentation

variable ATOMIC_FLAG_INIT

std::atomic_flag sgns::logging::logging_already_added ATOMIC_FLAG_INIT;

Macros Documentation

define BOOST_LOG_USE_NATIVE_SYSLOG

#define BOOST_LOG_USE_NATIVE_SYSLOG 

Source code

#include <lib/config.hpp>
#include <lib/jsonconfig.hpp>
#include <lib/logger_mt.hpp>
#include <lib/tomlconfig.hpp>
#include "logging.hpp"

#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/setup/console.hpp>
#include <boost/log/utility/setup/file.hpp>

#ifdef BOOST_WINDOWS
#include <boost/log/sinks/event_log_backend.hpp>
#else
#define BOOST_LOG_USE_NATIVE_SYSLOG
#include <boost/log/sinks/syslog_backend.hpp>
#endif

BOOST_LOG_ATTRIBUTE_KEYWORD (severity, "Severity", sgns::severity_level)

boost::shared_ptr<boost::log::sinks::synchronous_sink<boost::log::sinks::text_file_backend>> sgns::logging::file_sink;
std::atomic_flag sgns::logging::logging_already_added ATOMIC_FLAG_INIT;

void sgns::logging::init (boost::filesystem::path const & application_path_a)
{
    if (!logging_already_added.test_and_set ())
    {
        boost::log::add_common_attributes ();
        auto format = boost::log::expressions::stream << boost::log::expressions::attr<severity_level, severity_tag> ("Severity") << boost::log::expressions::smessage;
        auto format_with_timestamp = boost::log::expressions::stream << "[" << boost::log::expressions::attr<boost::posix_time::ptime> ("TimeStamp") << "]: " << boost::log::expressions::attr<severity_level, severity_tag> ("Severity") << boost::log::expressions::smessage;

        if (log_to_cerr ())
        {
            boost::log::add_console_log (std::cerr, boost::log::keywords::format = format_with_timestamp);
        }

        sgns::network_constants network_constants;
        if (!network_constants.is_test_network ())
        {
#ifdef BOOST_WINDOWS
            if (sgns::event_log_reg_entry_exists () || sgns::is_windows_elevated ())
            {
                static auto event_sink = boost::make_shared<boost::log::sinks::synchronous_sink<boost::log::sinks::simple_event_log_backend>> (boost::log::keywords::log_name = "Nano", boost::log::keywords::log_source = "Nano");
                event_sink->set_formatter (format);

                // Currently only mapping sys log errors
                boost::log::sinks::event_log::custom_event_type_mapping<sgns::severity_level> mapping ("Severity");
                mapping[sgns::severity_level::error] = boost::log::sinks::event_log::error;
                event_sink->locked_backend ()->set_event_type_mapper (mapping);

                // Only allow messages or error or greater severity to the event log
                event_sink->set_filter (severity >= sgns::severity_level::error);
                boost::log::core::get ()->add_sink (event_sink);
            }
#else
            static auto sys_sink = boost::make_shared<boost::log::sinks::synchronous_sink<boost::log::sinks::syslog_backend>> (boost::log::keywords::facility = boost::log::sinks::syslog::user, boost::log::keywords::use_impl = boost::log::sinks::syslog::impl_types::native);
            sys_sink->set_formatter (format);

            // Currently only mapping sys log errors
            boost::log::sinks::syslog::custom_severity_mapping<sgns::severity_level> mapping ("Severity");
            mapping[sgns::severity_level::error] = boost::log::sinks::syslog::error;
            sys_sink->locked_backend ()->set_severity_mapper (mapping);

            // Only allow messages or error or greater severity to the sys log
            sys_sink->set_filter (severity >= sgns::severity_level::error);
            boost::log::core::get ()->add_sink (sys_sink);
#endif
        }

//clang-format off
#if BOOST_VERSION < 107000
        if (stable_log_filename)
        {
            stable_log_filename = false;
            std::cerr << "The stable_log_filename config setting is only available when building with Boost 1.70 or later. Reverting to old behavior." << std::endl;
        }
#endif

        auto path = application_path_a / "log";
        if (stable_log_filename)
        {
#if BOOST_VERSION >= 107000
            // Logging to node.log and node_<pattern> instead of log_<pattern>.log is deliberate. This way,
            // existing log monitoring scripts expecting the old logfile structure will fail immediately instead
            // of reading only rotated files with old entries.
            file_sink = boost::log::add_file_log (boost::log::keywords::target = path,
            boost::log::keywords::file_name = path / "node.log",
            boost::log::keywords::target_file_name = path / "node_%Y-%m-%d_%H-%M-%S.%N.log",
            boost::log::keywords::open_mode = std::ios_base::out | std::ios_base::app, // append to node.log if it exists
            boost::log::keywords::enable_final_rotation = false, // for stable log filenames, don't rotate on destruction
            boost::log::keywords::rotation_size = rotation_size, // max file size in bytes before rotation
            boost::log::keywords::auto_flush = flush,
            boost::log::keywords::scan_method = boost::log::sinks::file::scan_method::scan_matching,
            boost::log::keywords::max_size = max_size, // max total size in bytes of all log files
            boost::log::keywords::format = format_with_timestamp);
#else
            debug_assert (false);
#endif
        }
        else
        {
            file_sink = boost::log::add_file_log (boost::log::keywords::target = path,
            boost::log::keywords::file_name = path / "log_%Y-%m-%d_%H-%M-%S.%N.log",
            boost::log::keywords::rotation_size = rotation_size,
            boost::log::keywords::auto_flush = flush,
            boost::log::keywords::scan_method = boost::log::sinks::file::scan_method::scan_matching,
            boost::log::keywords::max_size = max_size,
            boost::log::keywords::format = format_with_timestamp);
        }
    }
    //clang-format on
}

void sgns::logging::release_file_sink ()
{
    if (logging_already_added.test_and_set ())
    {
        boost::log::core::get ()->remove_sink (sgns::logging::file_sink);
        sgns::logging::file_sink.reset ();
    }
}

sgns::error_ sgns::logging::serialize_toml (sgns::tomlconfig & toml) const
{
    toml.put ("ledger", ledger_logging_value, "Log ledger related messages.\ntype:bool");
    toml.put ("ledger_duplicate", ledger_duplicate_logging_value, "Log when a duplicate block is attempted inserted into the ledger.\ntype:bool");
    toml.put ("vote", vote_logging_value, "Vote logging. Enabling this option leads to a high volume.\nof log messages which may affect node performance.\ntype:bool");
    toml.put ("network", network_logging_value, "Log network related messages.\ntype:bool");
    toml.put ("network_timeout", network_timeout_logging_value, "Log TCP timeouts.\ntype:bool");
    toml.put ("network_message", network_message_logging_value, "Log network errors and message details.\ntype:bool");
    toml.put ("network_publish", network_publish_logging_value, "Log publish related network messages.\ntype:bool");
    toml.put ("network_packet", network_packet_logging_value, "Log network packet activity.\ntype:bool");
    toml.put ("network_keepalive", network_keepalive_logging_value, "Log keepalive related messages.\ntype:bool");
    toml.put ("network_node_id_handshake", network_node_id_handshake_logging_value, "Log node-id handshake related messages.\ntype:bool");
    toml.put ("network_telemetry", network_telemetry_logging_value, "Log telemetry related messages.\ntype:bool");
    toml.put ("network_rejected", network_rejected_logging_value, "Log message when a connection is rejected.\ntype:bool");
    toml.put ("node_lifetime_tracing", node_lifetime_tracing_value, "Log node startup and shutdown messages.\ntype:bool");
    toml.put ("insufficient_work", insufficient_work_logging_value, "Log if insufficient work is detected.\ntype:bool");
    toml.put ("log_ipc", log_ipc_value, "Log IPC related activity.\ntype:bool");
    toml.put ("bulk_pull", bulk_pull_logging_value, "Log bulk pull errors and messages.\ntype:bool");
    toml.put ("work_generation_time", work_generation_time_value, "Log work generation timing information.\ntype:bool");
    toml.put ("upnp_details", upnp_details_logging_value, "Log UPNP discovery details..\nWarning: this may include information.\nabout discovered devices, such as product identification. Please review before sharing logs.\ntype:bool");
    toml.put ("timing", timing_logging_value, "Log detailed timing information for various node operations.\ntype:bool");
    toml.put ("active_update", active_update_value, "Log when a block is updated while in active transactions.\ntype:bool");
    toml.put ("log_to_cerr", log_to_cerr_value, "Log to standard error in addition to the log file. Not recommended for production systems.\ntype:bool");
    toml.put ("max_size", max_size, "Maximum log file size in bytes.\ntype:uint64");
    toml.put ("rotation_size", rotation_size, "Log file rotation size in character count.\ntype:uint64");
    toml.put ("flush", flush, "If enabled, immediately flush new entries to log file.\nWarning: this may negatively affect logging performance.\ntype:bool");
    toml.put ("min_time_between_output", min_time_between_log_output.count (), "Minimum time that must pass for low priority entries to be logged.\nWarning: decreasing this value may result in a very large amount of logs.\ntype:milliseconds");
    toml.put ("single_line_record", single_line_record_value, "Keep log entries on single lines.\ntype:bool");
    toml.put ("stable_log_filename", stable_log_filename, "Append to log/node.log without a timestamp in the filename.\nThe file is not emptied on startup if it exists, but appended to.\ntype:bool");

    return toml.get_error ();
}

sgns::error_ sgns::logging::deserialize_toml (sgns::tomlconfig & toml)
{
    toml.get<bool> ("ledger", ledger_logging_value);
    toml.get<bool> ("ledger_duplicate", ledger_duplicate_logging_value);
    toml.get<bool> ("vote", vote_logging_value);
    toml.get<bool> ("network", network_logging_value);
    toml.get<bool> ("network_timeout", network_timeout_logging_value);
    toml.get<bool> ("network_message", network_message_logging_value);
    toml.get<bool> ("network_publish", network_publish_logging_value);
    toml.get<bool> ("network_packet", network_packet_logging_value);
    toml.get<bool> ("network_keepalive", network_keepalive_logging_value);
    toml.get<bool> ("network_node_id_handshake", network_node_id_handshake_logging_value);
    toml.get<bool> ("network_telemetry_logging", network_telemetry_logging_value);
    toml.get<bool> ("network_rejected_logging", network_rejected_logging_value);
    toml.get<bool> ("node_lifetime_tracing", node_lifetime_tracing_value);
    toml.get<bool> ("insufficient_work", insufficient_work_logging_value);
    toml.get<bool> ("log_ipc", log_ipc_value);
    toml.get<bool> ("bulk_pull", bulk_pull_logging_value);
    toml.get<bool> ("work_generation_time", work_generation_time_value);
    toml.get<bool> ("upnp_details", upnp_details_logging_value);
    toml.get<bool> ("timing", timing_logging_value);
    toml.get<bool> ("active_update", active_update_value);
    toml.get<bool> ("log_to_cerr", log_to_cerr_value);
    toml.get<bool> ("flush", flush);
    toml.get<bool> ("single_line_record", single_line_record_value);
    toml.get<uintmax_t> ("max_size", max_size);
    toml.get<uintmax_t> ("rotation_size", rotation_size);
    auto min_time_between_log_output_l = min_time_between_log_output.count ();
    toml.get ("min_time_between_output", min_time_between_log_output_l);
    min_time_between_log_output = std::chrono::milliseconds (min_time_between_log_output_l);
    toml.get ("stable_log_filename", stable_log_filename);

    return toml.get_error ();
}

sgns::error_ sgns::logging::serialize_json (sgns::jsonconfig & json) const
{
    json.put ("version", json_version ());
    json.put ("ledger", ledger_logging_value);
    json.put ("ledger_duplicate", ledger_duplicate_logging_value);
    json.put ("vote", vote_logging_value);
    json.put ("network", network_logging_value);
    json.put ("network_timeout", network_timeout_logging_value);
    json.put ("network_message", network_message_logging_value);
    json.put ("network_publish", network_publish_logging_value);
    json.put ("network_packet", network_packet_logging_value);
    json.put ("network_keepalive", network_keepalive_logging_value);
    json.put ("network_node_id_handshake", network_node_id_handshake_logging_value);
    json.put ("network_telemetry_logging", network_telemetry_logging_value);
    json.put ("network_rejected_logging", network_rejected_logging_value);
    json.put ("node_lifetime_tracing", node_lifetime_tracing_value);
    json.put ("insufficient_work", insufficient_work_logging_value);
    json.put ("log_ipc", log_ipc_value);
    json.put ("bulk_pull", bulk_pull_logging_value);
    json.put ("work_generation_time", work_generation_time_value);
    json.put ("upnp_details", upnp_details_logging_value);
    json.put ("timing", timing_logging_value);
    json.put ("log_to_cerr", log_to_cerr_value);
    json.put ("max_size", max_size);
    json.put ("rotation_size", rotation_size);
    json.put ("flush", flush);
    json.put ("min_time_between_output", min_time_between_log_output.count ());
    json.put ("single_line_record", single_line_record_value);
    return json.get_error ();
}

bool sgns::logging::upgrade_json (unsigned version_a, sgns::jsonconfig & json)
{
    json.put ("version", json_version ());
    switch (version_a)
    {
        case 1:
            json.put ("vote", vote_logging_value);
        case 2:
            json.put ("rotation_size", rotation_size);
            json.put ("flush", true);
        case 3:
            json.put ("network_node_id_handshake", false);
        case 4:
            json.put ("upnp_details", "false");
            json.put ("timing", "false");
        case 5:
            uintmax_t config_max_size;
            json.get<uintmax_t> ("max_size", config_max_size);
            max_size = std::max (max_size, config_max_size);
            json.put ("max_size", max_size);
            json.put ("log_ipc", true);
        case 6:
            json.put ("min_time_between_output", min_time_between_log_output.count ());
            json.put ("network_timeout", network_timeout_logging_value);
            json.erase ("log_rpc");
            break;
        case 7:
            json.put ("single_line_record", single_line_record_value);
        case 8:
            break;
        default:
            throw std::runtime_error ("Unknown logging_config version");
            break;
    }
    return version_a < json_version ();
}

sgns::error_ sgns::logging::deserialize_json (bool & upgraded_a, sgns::jsonconfig & json)
{
    int version_l{ 1 };
    if (!json.has_key ("version"))
    {
        json.put ("version", version_l);

        auto work_peers_l (json.get_optional_child ("work_peers"));
        if (!work_peers_l)
        {
            sgns::jsonconfig peers;
            json.put_child ("work_peers", peers);
        }
        upgraded_a = true;
    }
    else
    {
        json.get_required<int> ("version", version_l);
    }

    upgraded_a |= upgrade_json (version_l, json);
    json.get<bool> ("ledger", ledger_logging_value);
    json.get<bool> ("ledger_duplicate", ledger_duplicate_logging_value);
    json.get<bool> ("vote", vote_logging_value);
    json.get<bool> ("network", network_logging_value);
    json.get<bool> ("network_timeout", network_timeout_logging_value);
    json.get<bool> ("network_message", network_message_logging_value);
    json.get<bool> ("network_publish", network_publish_logging_value);
    json.get<bool> ("network_packet", network_packet_logging_value);
    json.get<bool> ("network_keepalive", network_keepalive_logging_value);
    json.get<bool> ("network_node_id_handshake", network_node_id_handshake_logging_value);
    json.get<bool> ("node_lifetime_tracing", node_lifetime_tracing_value);
    json.get<bool> ("insufficient_work", insufficient_work_logging_value);
    json.get<bool> ("log_ipc", log_ipc_value);
    json.get<bool> ("bulk_pull", bulk_pull_logging_value);
    json.get<bool> ("work_generation_time", work_generation_time_value);
    json.get<bool> ("upnp_details", upnp_details_logging_value);
    json.get<bool> ("timing", timing_logging_value);
    json.get<bool> ("log_to_cerr", log_to_cerr_value);
    json.get<bool> ("flush", flush);
    json.get<bool> ("single_line_record", single_line_record_value);
    json.get<uintmax_t> ("max_size", max_size);
    json.get<uintmax_t> ("rotation_size", rotation_size);
    uintmax_t min_time_between_log_output_raw;
    json.get<uintmax_t> ("min_time_between_output", min_time_between_log_output_raw);
    min_time_between_log_output = std::chrono::milliseconds (min_time_between_log_output_raw);
    return json.get_error ();
}

bool sgns::logging::ledger_logging () const
{
    return ledger_logging_value;
}

bool sgns::logging::ledger_duplicate_logging () const
{
    return ledger_logging () && ledger_duplicate_logging_value;
}

bool sgns::logging::vote_logging () const
{
    return vote_logging_value;
}

bool sgns::logging::network_logging () const
{
    return network_logging_value;
}

bool sgns::logging::network_timeout_logging () const
{
    return network_logging () && network_timeout_logging_value;
}

bool sgns::logging::network_message_logging () const
{
    return network_logging () && network_message_logging_value;
}

bool sgns::logging::network_publish_logging () const
{
    return network_logging () && network_publish_logging_value;
}

bool sgns::logging::network_packet_logging () const
{
    return network_logging () && network_packet_logging_value;
}

bool sgns::logging::network_keepalive_logging () const
{
    return network_logging () && network_keepalive_logging_value;
}

bool sgns::logging::network_node_id_handshake_logging () const
{
    return network_logging () && network_node_id_handshake_logging_value;
}

bool sgns::logging::network_telemetry_logging () const
{
    return network_logging () && network_telemetry_logging_value;
}

bool sgns::logging::network_rejected_logging () const
{
    return network_logging () && network_rejected_logging_value;
}

bool sgns::logging::node_lifetime_tracing () const
{
    return node_lifetime_tracing_value;
}

bool sgns::logging::insufficient_work_logging () const
{
    return network_logging () && insufficient_work_logging_value;
}

bool sgns::logging::log_ipc () const
{
    return network_logging () && log_ipc_value;
}

bool sgns::logging::bulk_pull_logging () const
{
    return network_logging () && bulk_pull_logging_value;
}

bool sgns::logging::callback_logging () const
{
    return network_logging ();
}

bool sgns::logging::work_generation_time () const
{
    return work_generation_time_value;
}

bool sgns::logging::upnp_details_logging () const
{
    return upnp_details_logging_value;
}

bool sgns::logging::timing_logging () const
{
    return timing_logging_value;
}

bool sgns::logging::active_update_logging () const
{
    return active_update_value;
}

bool sgns::logging::log_to_cerr () const
{
    return log_to_cerr_value;
}

bool sgns::logging::single_line_record () const
{
    return single_line_record_value;
}

Updated on 2026-03-04 at 13:10:44 -0800