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 <phosphor-logging/log.hpp>
19 
20 #include <algorithm>
21 #include <ctime>
22 #include <format>
23 #include <iomanip>
24 #include <sstream>
25 #include <vector>
26 
27 constexpr auto maxEntriesPerID = 40;
28 
29 namespace phosphor::fan::control::json
30 {
31 using json = nlohmann::json;
32 
instance()33 FlightRecorder& FlightRecorder::instance()
34 {
35     static FlightRecorder fr;
36     return fr;
37 }
38 
log(const std::string & id,const std::string & message)39 void FlightRecorder::log(const std::string& id, const std::string& message)
40 {
41     uint64_t ts = std::chrono::duration_cast<std::chrono::microseconds>(
42                       std::chrono::system_clock::now().time_since_epoch())
43                       .count();
44 
45     auto& entry = _entries[id];
46     entry.emplace_back(ts, message);
47     if (entry.size() > maxEntriesPerID)
48     {
49         entry.pop_front();
50     }
51 }
52 
dump(json & data)53 void FlightRecorder::dump(json& data)
54 {
55     using namespace std::chrono;
56     using Timepoint = time_point<system_clock, microseconds>;
57 
58     size_t idSize = 0;
59     std::vector<std::tuple<Timepoint, std::string, std::string>> output;
60 
61     for (const auto& [id, messages] : _entries)
62     {
63         for (const auto& [ts, msg] : messages)
64         {
65             idSize = std::max(idSize, id.size());
66             Timepoint tp{microseconds{ts}};
67             output.emplace_back(tp, id, msg);
68         }
69     }
70 
71     std::sort(output.begin(), output.end(),
72               [](const auto& left, const auto& right) {
73         return std::get<Timepoint>(left) < std::get<Timepoint>(right);
74     });
75 
76     auto formatTime = [](const Timepoint& tp) {
77         std::stringstream ss;
78         std::time_t tt = system_clock::to_time_t(tp);
79         uint64_t us =
80             duration_cast<microseconds>(tp.time_since_epoch()).count();
81 
82         // e.g. Oct 04 16:43:45.923555
83         ss << std::put_time(std::localtime(&tt), "%b %d %H:%M:%S.");
84         ss << std::setfill('0') << std::setw(6) << std::to_string(us % 1000000);
85         return ss.str();
86     };
87 
88     auto& fr = data["flight_recorder"];
89     std::stringstream ss;
90 
91     for (const auto& [ts, id, msg] : output)
92     {
93         ss << formatTime(ts) << ": " << std::setw(idSize) << id << ": " << msg;
94         fr.push_back(ss.str());
95         ss.str("");
96     }
97 }
98 
99 } // namespace phosphor::fan::control::json
100