/******************************************************************************** * Copyright (c) 2025-2026 ZF Friedrichshafen AG * * This program and the accompanying materials are made available under the * terms of the Apache License Version 2.0 which is available at * https://www.apache.org/licenses/LICENSE-2.0 * * SPDX-License-Identifier: Apache-2.0 * * Contributors: * Erik Verhoeven - initial API and implementation ********************************************************************************/ #include "log_csv_writer.h" #include CLogCSVWriter::CLogCSVWriter(std::shared_ptr ostream, const char separator /*= ';'*/) : m_streamOutput(std::move(ostream)) , m_cSeparator(separator) { if ((m_cSeparator <= 0x08) || (m_cSeparator >= 0x0A && m_cSeparator <= 0x1F) || (m_cSeparator >= 0x30 && m_cSeparator <= 0x39) || (m_cSeparator >= 0x41 && m_cSeparator <= 0x5A) || (m_cSeparator >= 0x61 && m_cSeparator <= 0x7A) || static_cast(m_cSeparator) >= 0x7F || m_cSeparator == '\"' || m_cSeparator == '.' || m_cSeparator == ':' || m_cSeparator == ' ') { throw SForbiddenSeparatorException((std::string("Forbidden Separator '") + m_cSeparator + "'").c_str()); } std::string intro = std::string("sep=") + m_cSeparator + "\nTimestamp" + m_cSeparator + "Severity" + m_cSeparator + "Tag" + m_cSeparator + "Message\n"; std::unique_lock lock(m_mtxOutput); *m_streamOutput << intro; m_streamOutput->flush(); } void CLogCSVWriter::Write(const std::string& message, const sdv::core::ELogSeverity severity, const std::chrono::time_point timestamp) { std::string output = GetDateTime(timestamp) + m_cSeparator + GetSeverityString(severity) + m_cSeparator + "\"" + m_cSeparator + "\"" + Escape(message) + "\"\n"; std::unique_lock lock(m_mtxOutput); *m_streamOutput << output; m_streamOutput->flush(); } std::string CLogCSVWriter::GetDateTime(const std::chrono::time_point timestamp) { using namespace std::chrono; std::stringstream output; microseconds micros = duration_cast(timestamp.time_since_epoch()); uint32_t fractionalSeconds = micros.count() % 1000000; std::string timeFormat = "%Y.%m.%d %H:%M:%S."; std::time_t time = system_clock::to_time_t(timestamp); output << std::put_time(std::localtime(&time), timeFormat.c_str()); output << std::setw(6) << std::setfill('0') << fractionalSeconds; return output.str(); } std::string CLogCSVWriter::GetSeverityString(sdv::core::ELogSeverity severity) { switch (severity) { case sdv::core::ELogSeverity::trace: return "Trace"; case sdv::core::ELogSeverity::debug: return "Debug"; case sdv::core::ELogSeverity::info: return "Info"; case sdv::core::ELogSeverity::warning: return "Warning"; case sdv::core::ELogSeverity::error: return "Error"; case sdv::core::ELogSeverity::fatal: return "Fatal"; default: return "Unknown"; } } std::string CLogCSVWriter::Escape(const std::string& toEscape) { std::string escaped = toEscape; const std::string with("\"\""); for (std::string::size_type pos{}; std::string::npos != (pos = escaped.find('\"', pos)); pos += with.length()) { escaped.replace(pos, 1, with.data(), with.length()); } return escaped; }