113270967SMatt Spinler /**
213270967SMatt Spinler  * Copyright © 2021 IBM Corporation
313270967SMatt Spinler  *
413270967SMatt Spinler  * Licensed under the Apache License, Version 2.0 (the "License");
513270967SMatt Spinler  * you may not use this file except in compliance with the License.
613270967SMatt Spinler  * You may obtain a copy of the License at
713270967SMatt Spinler  *
813270967SMatt Spinler  *     http://www.apache.org/licenses/LICENSE-2.0
913270967SMatt Spinler  *
1013270967SMatt Spinler  * Unless required by applicable law or agreed to in writing, software
1113270967SMatt Spinler  * distributed under the License is distributed on an "AS IS" BASIS,
1213270967SMatt Spinler  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313270967SMatt Spinler  * See the License for the specific language governing permissions and
1413270967SMatt Spinler  * limitations under the License.
1513270967SMatt Spinler  */
1613270967SMatt Spinler #include "flight_recorder.hpp"
1713270967SMatt Spinler 
1813270967SMatt Spinler #include <phosphor-logging/log.hpp>
1913270967SMatt Spinler 
2013270967SMatt Spinler #include <algorithm>
2113270967SMatt Spinler #include <ctime>
22*fbf4703fSPatrick Williams #include <format>
2313270967SMatt Spinler #include <iomanip>
247787def0SMatt Spinler #include <sstream>
2513270967SMatt Spinler #include <vector>
2613270967SMatt Spinler 
274907daceSMatt Spinler constexpr auto maxEntriesPerID = 40;
2813270967SMatt Spinler 
2913270967SMatt Spinler namespace phosphor::fan::control::json
3013270967SMatt Spinler {
317787def0SMatt Spinler using json = nlohmann::json;
3213270967SMatt Spinler 
instance()3313270967SMatt Spinler FlightRecorder& FlightRecorder::instance()
3413270967SMatt Spinler {
3513270967SMatt Spinler     static FlightRecorder fr;
3613270967SMatt Spinler     return fr;
3713270967SMatt Spinler }
3813270967SMatt Spinler 
log(const std::string & id,const std::string & message)3913270967SMatt Spinler void FlightRecorder::log(const std::string& id, const std::string& message)
4013270967SMatt Spinler {
4113270967SMatt Spinler     uint64_t ts = std::chrono::duration_cast<std::chrono::microseconds>(
4213270967SMatt Spinler                       std::chrono::system_clock::now().time_since_epoch())
4313270967SMatt Spinler                       .count();
4413270967SMatt Spinler 
4513270967SMatt Spinler     auto& entry = _entries[id];
4613270967SMatt Spinler     entry.emplace_back(ts, message);
4713270967SMatt Spinler     if (entry.size() > maxEntriesPerID)
4813270967SMatt Spinler     {
4913270967SMatt Spinler         entry.pop_front();
5013270967SMatt Spinler     }
5113270967SMatt Spinler }
5213270967SMatt Spinler 
dump(json & data)537787def0SMatt Spinler void FlightRecorder::dump(json& data)
5413270967SMatt Spinler {
5513270967SMatt Spinler     using namespace std::chrono;
5613270967SMatt Spinler     using Timepoint = time_point<system_clock, microseconds>;
5713270967SMatt Spinler 
5813270967SMatt Spinler     size_t idSize = 0;
5913270967SMatt Spinler     std::vector<std::tuple<Timepoint, std::string, std::string>> output;
6013270967SMatt Spinler 
6113270967SMatt Spinler     for (const auto& [id, messages] : _entries)
6213270967SMatt Spinler     {
6313270967SMatt Spinler         for (const auto& [ts, msg] : messages)
6413270967SMatt Spinler         {
6513270967SMatt Spinler             idSize = std::max(idSize, id.size());
6613270967SMatt Spinler             Timepoint tp{microseconds{ts}};
6713270967SMatt Spinler             output.emplace_back(tp, id, msg);
6813270967SMatt Spinler         }
6913270967SMatt Spinler     }
7013270967SMatt Spinler 
7113270967SMatt Spinler     std::sort(output.begin(), output.end(),
7213270967SMatt Spinler               [](const auto& left, const auto& right) {
7313270967SMatt Spinler         return std::get<Timepoint>(left) < std::get<Timepoint>(right);
7413270967SMatt Spinler     });
7513270967SMatt Spinler 
7613270967SMatt Spinler     auto formatTime = [](const Timepoint& tp) {
7713270967SMatt Spinler         std::stringstream ss;
7813270967SMatt Spinler         std::time_t tt = system_clock::to_time_t(tp);
7913270967SMatt Spinler         uint64_t us =
8013270967SMatt Spinler             duration_cast<microseconds>(tp.time_since_epoch()).count();
8113270967SMatt Spinler 
8213270967SMatt Spinler         // e.g. Oct 04 16:43:45.923555
8313270967SMatt Spinler         ss << std::put_time(std::localtime(&tt), "%b %d %H:%M:%S.");
8413270967SMatt Spinler         ss << std::setfill('0') << std::setw(6) << std::to_string(us % 1000000);
8513270967SMatt Spinler         return ss.str();
8613270967SMatt Spinler     };
8713270967SMatt Spinler 
887787def0SMatt Spinler     auto& fr = data["flight_recorder"];
897787def0SMatt Spinler     std::stringstream ss;
907787def0SMatt Spinler 
9113270967SMatt Spinler     for (const auto& [ts, id, msg] : output)
9213270967SMatt Spinler     {
937787def0SMatt Spinler         ss << formatTime(ts) << ": " << std::setw(idSize) << id << ": " << msg;
947787def0SMatt Spinler         fr.push_back(ss.str());
957787def0SMatt Spinler         ss.str("");
9613270967SMatt Spinler     }
9713270967SMatt Spinler }
9813270967SMatt Spinler 
9913270967SMatt Spinler } // namespace phosphor::fan::control::json
100