/* * Copyright (c) 2024 Project CHIP Authors * 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. * */ #pragma once #include #include #include #include #include #include #include #include #include #include class DataModelLogger { public: static CHIP_ERROR LogAttribute(const chip::app::ConcreteDataAttributePath & path, chip::TLV::TLVReader * data); static CHIP_ERROR LogCommand(const chip::app::ConcreteCommandPath & path, chip::TLV::TLVReader * data); static CHIP_ERROR LogEvent(const chip::app::EventHeader & header, chip::TLV::TLVReader * data); private: static CHIP_ERROR LogValue(const char * label, size_t indent, bool value) { DataModelLogger::LogString(label, indent, value ? "TRUE" : "FALSE"); return CHIP_NO_ERROR; } static CHIP_ERROR LogValue(const char * label, size_t indent, chip::CharSpan value) { DataModelLogger::LogString(label, indent, std::string(value.data(), value.size())); return CHIP_NO_ERROR; } static CHIP_ERROR LogValue(const char * label, size_t indent, chip::ByteSpan value) { // CHIP_CONFIG_LOG_MESSAGE_MAX_SIZE includes various prefixes we don't // control (timestamps, process ids, etc). Let's assume (hope?) that // those prefixes use up no more than half the total available space. // Right now it looks like the prefixes are 45 chars out of a 255 char // buffer. char buffer[CHIP_CONFIG_LOG_MESSAGE_MAX_SIZE / 2]; size_t prefixSize = ComputePrefixSize(label, indent); if (prefixSize > ArraySize(buffer)) { DataModelLogger::LogString("", 0, "Prefix is too long to fit in buffer"); return CHIP_ERROR_INTERNAL; } const size_t availableSize = ArraySize(buffer) - prefixSize; // Each byte ends up as two hex characters. const size_t bytesPerLogCall = availableSize / 2; std::string labelStr(label); while (value.size() > bytesPerLogCall) { ReturnErrorOnFailure( chip::Encoding::BytesToUppercaseHexString(value.data(), bytesPerLogCall, &buffer[0], ArraySize(buffer))); LogString(labelStr, indent, buffer); value = value.SubSpan(bytesPerLogCall); // For the second and following lines, make it clear that they are // continuation lines by replacing the label with "....". labelStr.replace(labelStr.begin(), labelStr.end(), labelStr.size(), '.'); } ReturnErrorOnFailure(chip::Encoding::BytesToUppercaseHexString(value.data(), value.size(), &buffer[0], ArraySize(buffer))); LogString(labelStr, indent, buffer); return CHIP_NO_ERROR; } template ::value && !std::is_same>, bool>::value, int> = 0> static CHIP_ERROR LogValue(const char * label, size_t indent, X value) { DataModelLogger::LogString(label, indent, std::to_string(value)); return CHIP_NO_ERROR; } template ::value, int> = 0> static CHIP_ERROR LogValue(const char * label, size_t indent, X value) { DataModelLogger::LogString(label, indent, std::to_string(value)); return CHIP_NO_ERROR; } template ::value, int> = 0> static CHIP_ERROR LogValue(const char * label, size_t indent, X value) { return DataModelLogger::LogValue(label, indent, chip::to_underlying(value)); } template static CHIP_ERROR LogValue(const char * label, size_t indent, chip::BitFlags value) { return DataModelLogger::LogValue(label, indent, value.Raw()); } template static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::DataModel::DecodableList & value) { size_t count = 0; ReturnErrorOnFailure(value.ComputeSize(&count)); DataModelLogger::LogString(label, indent, std::to_string(count) + " entries"); auto iter = value.begin(); size_t i = 0; while (iter.Next()) { ++i; std::string itemLabel = std::string("[") + std::to_string(i) + "]"; ReturnErrorOnFailure(DataModelLogger::LogValue(itemLabel.c_str(), indent + 1, iter.GetValue())); } if (iter.GetStatus() != CHIP_NO_ERROR) { DataModelLogger::LogString(indent + 1, "List truncated due to invalid value"); } return iter.GetStatus(); } template static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::DataModel::Nullable & value) { if (value.IsNull()) { DataModelLogger::LogString(label, indent, "null"); return CHIP_NO_ERROR; } return DataModelLogger::LogValue(label, indent, value.Value()); } template static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::Optional & value) { if (value.HasValue()) { return DataModelLogger::LogValue(label, indent, value.Value()); } return CHIP_NO_ERROR; } static CHIP_ERROR LogClusterId(const char * label, size_t indent, const chip::app::DataModel::DecodableList & value) { size_t count = 0; ReturnErrorOnFailure(value.ComputeSize(&count)); DataModelLogger::LogString(label, indent, std::to_string(count) + " entries"); auto iter = value.begin(); size_t i = 0; while (iter.Next()) { ++i; std::string index = std::string("[") + std::to_string(i) + "]"; std::string item = std::to_string(iter.GetValue()) + " (" + ClusterIdToText(iter.GetValue()) + ")"; DataModelLogger::LogString(index, indent + 1, item); } if (iter.GetStatus() != CHIP_NO_ERROR) { DataModelLogger::LogString(indent + 1, "List truncated due to invalid value"); } return iter.GetStatus(); } static CHIP_ERROR LogAttributeId(const char * label, size_t indent, const chip::app::DataModel::DecodableList & value, chip::ClusterId cluster) { size_t count = 0; ReturnErrorOnFailure(value.ComputeSize(&count)); DataModelLogger::LogString(label, indent, std::to_string(count) + " entries"); auto iter = value.begin(); size_t i = 0; while (iter.Next()) { ++i; std::string index = std::string("[") + std::to_string(i) + "]"; std::string item = std::to_string(iter.GetValue()) + " (" + AttributeIdToText(cluster, iter.GetValue()) + ")"; DataModelLogger::LogString(index, indent + 1, item); } if (iter.GetStatus() != CHIP_NO_ERROR) { DataModelLogger::LogString(indent + 1, "List truncated due to invalid value"); } return iter.GetStatus(); } static CHIP_ERROR LogAcceptedCommandId(const char * label, size_t indent, const chip::app::DataModel::DecodableList & value, chip::ClusterId cluster) { size_t count = 0; ReturnErrorOnFailure(value.ComputeSize(&count)); DataModelLogger::LogString(label, indent, std::to_string(count) + " entries"); auto iter = value.begin(); size_t i = 0; while (iter.Next()) { ++i; std::string index = std::string("[") + std::to_string(i) + "]"; std::string item = std::to_string(iter.GetValue()) + " (" + AcceptedCommandIdToText(cluster, iter.GetValue()) + ")"; DataModelLogger::LogString(index, indent + 1, item); } if (iter.GetStatus() != CHIP_NO_ERROR) { DataModelLogger::LogString(indent + 1, "List truncated due to invalid value"); } return iter.GetStatus(); } static CHIP_ERROR LogGeneratedCommandId(const char * label, size_t indent, const chip::app::DataModel::DecodableList & value, chip::ClusterId cluster) { size_t count = 0; ReturnErrorOnFailure(value.ComputeSize(&count)); DataModelLogger::LogString(label, indent, std::to_string(count) + " entries"); auto iter = value.begin(); size_t i = 0; while (iter.Next()) { ++i; std::string index = std::string("[") + std::to_string(i) + "]"; std::string item = std::to_string(iter.GetValue()) + " (" + GeneratedCommandIdToText(cluster, iter.GetValue()) + ")"; DataModelLogger::LogString(index, indent + 1, item); } if (iter.GetStatus() != CHIP_NO_ERROR) { DataModelLogger::LogString(indent + 1, "List truncated due to invalid value"); } return iter.GetStatus(); } #include static void LogString(size_t indent, const std::string string) { LogString("", indent, string); } static void LogString(const std::string label, size_t indent, const std::string string) { std::string prefix = ComputePrefix(label, indent); ChipLogProgress(NotSpecified, "%s%s", prefix.c_str(), string.c_str()); } private: static std::string ComputePrefix(const std::string label, size_t indent) { std::string prefix; for (size_t i = 0; i < indent; ++i) { prefix.append(" "); } if (label.size() > 0) { prefix.append(label); prefix.append(":"); } prefix.append(" "); return prefix; } static size_t ComputePrefixSize(const std::string label, size_t indent) { return ComputePrefix(label, indent).size(); } };