xref: /openbmc/phosphor-fan-presence/control/json/utils/flight_recorder.cpp (revision 9d533806250cea56406bdd39e025f0d820c4ed90)
1 /**
2  * Copyright © 2021 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "flight_recorder.hpp"
17 
18 #include <algorithm>
19 #include <ctime>
20 #include <format>
21 #include <iomanip>
22 #include <sstream>
23 #include <vector>
24 
25 constexpr auto maxEntriesPerID = 40;
26 
27 namespace phosphor::fan::control::json
28 {
29 using json = nlohmann::json;
30 
instance()31 FlightRecorder& FlightRecorder::instance()
32 {
33     static FlightRecorder fr;
34     return fr;
35 }
36 
log(const std::string & id,const std::string & message)37 void FlightRecorder::log(const std::string& id, const std::string& message)
38 {
39     uint64_t ts = std::chrono::duration_cast<std::chrono::microseconds>(
40                       std::chrono::system_clock::now().time_since_epoch())
41                       .count();
42 
43     auto& entry = _entries[id];
44     entry.emplace_back(ts, message);
45     if (entry.size() > maxEntriesPerID)
46     {
47         entry.pop_front();
48     }
49 }
50 
dump(json & data)51 void FlightRecorder::dump(json& data)
52 {
53     using namespace std::chrono;
54     using Timepoint = time_point<system_clock, microseconds>;
55 
56     size_t idSize = 0;
57     std::vector<std::tuple<Timepoint, std::string, std::string>> output;
58 
59     for (const auto& [id, messages] : _entries)
60     {
61         for (const auto& [ts, msg] : messages)
62         {
63             idSize = std::max(idSize, id.size());
64             Timepoint tp{microseconds{ts}};
65             output.emplace_back(tp, id, msg);
66         }
67     }
68 
69     std::sort(output.begin(), output.end(),
70               [](const auto& left, const auto& right) {
71                   return std::get<Timepoint>(left) < std::get<Timepoint>(right);
72               });
73 
74     auto formatTime = [](const Timepoint& tp) {
75         std::stringstream ss;
76         std::time_t tt = system_clock::to_time_t(tp);
77         uint64_t us =
78             duration_cast<microseconds>(tp.time_since_epoch()).count();
79 
80         // e.g. Oct 04 16:43:45.923555
81         ss << std::put_time(std::localtime(&tt), "%b %d %H:%M:%S.");
82         ss << std::setfill('0') << std::setw(6) << std::to_string(us % 1000000);
83         return ss.str();
84     };
85 
86     auto& fr = data["flight_recorder"];
87     std::stringstream ss;
88 
89     for (const auto& [ts, id, msg] : output)
90     {
91         ss << formatTime(ts) << ": " << std::setw(idSize) << id << ": " << msg;
92         fr.push_back(ss.str());
93         ss.str("");
94     }
95 }
96 
97 } // namespace phosphor::fan::control::json
98