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