<commit_before>// Copyright (c) 2019 by <NAME>. All rights reserved.
// Copyright (c) 2021 - 2022 by Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

#include "iceoryx_hoofs/log/platform_building_blocks/console_logger.hpp"
#include "iceoryx_hoofs/cxx/attributes.hpp"
#include "iceoryx_platform/time.hpp"

#include <cstdio>
#include <cstring>
#include <ctime>

namespace iox
{
namespace pbb
{
// NOLINTJUSTIFICATION see at declaration in header
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
std::atomic<LogLevel> ConsoleLogger::m_activeLogLevel{LogLevel::INFO};

ConsoleLogger::ThreadLocalData& ConsoleLogger::getThreadLocalData()
{
    thread_local static ThreadLocalData data;
    return data;
}

LogLevel ConsoleLogger::getLogLevel() noexcept
{
    return m_activeLogLevel.load(std::memory_order_relaxed);
}

void ConsoleLogger::setLogLevel(const LogLevel logLevel) noexcept
{
    m_activeLogLevel.store(logLevel, std::memory_order_relaxed);
}

void ConsoleLogger::createLogMessageHeader(const char* file,
                                           const int line,
                                           const char* function,
                                           LogLevel logLevel) noexcept
{
    timespec timestamp{0, 0};
    if (clock_gettime(CLOCK_REALTIME, &timestamp) != 0)
    {
        timestamp = {0, 0};
        // intentionally do nothing since a timestamp from 01.01.1970 already indicates  an issue with the clock
    }

    time_t time = timestamp.tv_sec;

/// @todo iox-#1345 since this will be part of the platform at one point, we might not be able to handle this via the
/// platform abstraction; re-evaluate this when the move to the platform happens
#if defined(_WIN32)
    // seems to be thread-safe on Windows
    auto* timeInfo = localtime(&time);
#else
    // NOLINTJUSTIFICATION will be initialized with the call to localtime_r in the statement after the declaration
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
    struct tm calendarData;
    auto* timeInfo = localtime_r(&time, &calendarData);
#endif

    // NOLINTJUSTIFICATION this is used to get the size for the buffer where strftime writes the local time
    // NOLINTNEXTLINE(hicpp-avoid-c-arrays, cppcoreguidelines-avoid-c-arrays)
    constexpr const char TIME_FORMAT[]{"0000-00-00 00:00:00"};
    constexpr uint32_t NULL_TERMINATION{1};
    constexpr uint32_t YEAR_1M_PROBLEM{2}; // in case iceoryx is still in use, please change to 3
    constexpr auto NULL_TERMINATED_TIMESTAMP_BUFFER_SIZE{ConsoleLogger::bufferSize(TIME_FORMAT) + YEAR_1M_PROBLEM
                                                         + NULL_TERMINATION};

    // NOLINTJUSTIFICATION required for strftime and safe since array bounds are taken into account
    // NOLINTNEXTLINE(hicpp-avoid-c-arrays, cppcoreguidelines-avoid-c-arrays)
    char timestampString[NULL_TERMINATED_TIMESTAMP_BUFFER_SIZE]{0};

    bool timeStampConversionSuccessful{false};
    if (timeInfo != nullptr)
    {
        auto strftimeRetVal =
            strftime(&timestampString[0], NULL_TERMINATED_TIMESTAMP_BUFFER_SIZE, "%Y-%m-%d %H:%M:%S", timeInfo);
        timeStampConversionSuccessful = (strftimeRetVal != 0);
    }

    if (!timeStampConversionSuccessful)
    {
        // this will clearly indicate that something went wrong with the time conversion; no need to abort the log
        // output
        strncpy(&timestampString[0], &TIME_FORMAT[0], ConsoleLogger::bufferSize(TIME_FORMAT));
    }

    constexpr auto MILLISECS_PER_SECOND{1000};
    auto milliseconds = static_cast<int32_t>(timestamp.tv_nsec % MILLISECS_PER_SECOND);

    /// @todo iox-#1345 do we also want to always log the iceoryx version and commit sha? Maybe do that only in
    /// `initLogger` with LogDebug

    /// @todo iox-#1345 add an option to also print file, line and function
    unused(file);
    unused(line);
    unused(function);

    // NOLINTJUSTIFICATION snprintf required to populate char array so that it can be flushed in one piece
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
    auto retVal = snprintf(&getThreadLocalData().buffer[0],
                           ThreadLocalData::NULL_TERMINATED_BUFFER_SIZE,
                           "\033[0;90m%s.%03d %s%s\033[m: ",
                           &timestampString[0],
                           milliseconds,
                           logLevelDisplayColor(logLevel),
                           logLevelDisplayText(logLevel));
    if (retVal < 0)<commit_msg>iox-#1345 Move magic ascii color codes into constexpr<commit_after>// Copyright (c) 2019 by <NAME> GmbH. All rights reserved.
// Copyright (c) 2021 - 2022 by Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

#include "iceoryx_hoofs/log/platform_building_blocks/console_logger.hpp"
#include "iceoryx_hoofs/cxx/attributes.hpp"
#include "iceoryx_platform/time.hpp"

#include <cstdio>
#include <cstring>
#include <ctime>

namespace iox
{
namespace pbb
{
// NOLINTJUSTIFICATION see at declaration in header
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
std::atomic<LogLevel> ConsoleLogger::m_activeLogLevel{LogLevel::INFO};

ConsoleLogger::ThreadLocalData& ConsoleLogger::getThreadLocalData()
{
    thread_local static ThreadLocalData data;
    return data;
}

LogLevel ConsoleLogger::getLogLevel() noexcept
{
    return m_activeLogLevel.load(std::memory_order_relaxed);
}

void ConsoleLogger::setLogLevel(const LogLevel logLevel) noexcept
{
    m_activeLogLevel.store(logLevel, std::memory_order_relaxed);
}

void ConsoleLogger::createLogMessageHeader(const char* file,
                                           const int line,
                                           const char* function,
                                           LogLevel logLevel) noexcept
{
    timespec timestamp{0, 0};
    if (clock_gettime(CLOCK_REALTIME, &timestamp) != 0)
    {
        timestamp = {0, 0};
        // intentionally do nothing since a timestamp from 01.01.1970 already indicates  an issue with the clock
    }

    time_t time = timestamp.tv_sec;

/// @todo iox-#1345 since this will be part of the platform at one point, we might not be able to handle this via the
/// platform abstraction; re-evaluate this when the move to the platform happens
#if defined(_WIN32)
    // seems to be thread-safe on Windows
    auto* timeInfo = localtime(&time);
#else
    // NOLINTJUSTIFICATION will be initialized with the call to localtime_r in the statement after the declaration
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
    struct tm calendarData;
    auto* timeInfo = localtime_r(&time, &calendarData);
#endif

    // NOLINTJUSTIFICATION this is used to get the size for the buffer where strftime writes the local time
    // NOLINTNEXTLINE(hicpp-avoid-c-arrays, cppcoreguidelines-avoid-c-arrays)
    constexpr const char TIME_FORMAT[]{"0000-00-00 00:00:00"};
    constexpr uint32_t NULL_TERMINATION{1};
    constexpr uint32_t YEAR_1M_PROBLEM{2}; // in case iceoryx is still in use, please change to 3
    constexpr auto NULL_TERMINATED_TIMESTAMP_BUFFER_SIZE{ConsoleLogger::bufferSize(TIME_FORMAT) + YEAR_1M_PROBLEM
                                                         + NULL_TERMINATION};

    // NOLINTJUSTIFICATION required for strftime and safe since array bounds are taken into account
    // NOLINTNEXTLINE(hicpp-avoid-c-arrays, cppcoreguidelines-avoid-c-arrays)
    char timestampString[NULL_TERMINATED_TIMESTAMP_BUFFER_SIZE]{0};

    bool timeStampConversionSuccessful{false};
    if (timeInfo != nullptr)
    {
        auto strftimeRetVal =
            strftime(&timestampString[0], NULL_TERMINATED_TIMESTAMP_BUFFER_SIZE, "%Y-%m-%d %H:%M:%S", timeInfo);
        timeStampConversionSuccessful = (strftimeRetVal != 0);
    }

    if (!timeStampConversionSuccessful)
    {
        // this will clearly indicate that something went wrong with the time conversion; no need to abort the log
        // output
        strncpy(&timestampString[0], &TIME_FORMAT[0], ConsoleLogger::bufferSize(TIME_FORMAT));
    }

    constexpr auto MILLISECS_PER_SECOND{1000};
    auto milliseconds = static_cast<int32_t>(timestamp.tv_nsec % MILLISECS_PER_SECOND);

    /// @todo iox-#1345 do we also want to always log the iceoryx version and commit sha? Maybe do that only in
    /// `initLogger` with LogDebug

    /// @todo iox-#1345 add an option to also print file, line and function
    unused(file);
    unused(line);
    unused(function);

    constexpr const char COLOR_GRAY[]{"\033[0;90m"};
    constexpr const char COLOR_RESET[]{"\033[m"};
    // NOLINTJUSTIFICATION snprintf required to populate char array so that it can be flushed in one piece
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
    auto retVal = snprintf(&getThreadLocalData().buffer[0],
                           ThreadLocalData::NULL_TERMINATED_BUFFER_SIZE,
                           "%s%s.%03d %s%s%s: ",
                           COLOR_GRAY,
                           &timestampString[0],
                           milliseconds,
                           logLevelDisplayColor(logLevel),
                           logLevelDisplayText(logLevel),
                           COLOR_RESET);
    if (retVal < 0)
{'fraction_non_alphanumeric': 0.0899091343854615, 'fraction_numerical': 0.018173122907699665, 'mean_word_length': 3.012279355333845, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6721147', 'n_tokens_mistral': 3178, 'n_tokens_neox': 2988, 'n_words': 881}