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