-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathlogger.hpp
175 lines (151 loc) · 4.22 KB
/
logger.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#pragma once
#include "utility.hpp"
#include <unistd.h>
#include <nlohmann/json.hpp>
#include <phosphor-logging/log.hpp>
#include <cassert>
#include <ctime>
#include <filesystem>
#include <format>
#include <fstream>
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
namespace phosphor::fan
{
/**
* @class Logger
*
* A simple logging class that stores log messages in a vector along
* with their timestamp. When a messaged is logged, it will also be
* written to the journal.
*
* A saveToTempFile() function will write the log entries as JSON to
* a temporary file, so they can be added to event logs.
*
* The maximum number of entries to keep is specified in the
* constructor, and after that is hit the oldest entry will be
* removed when a new one is added.
*/
class Logger
{
public:
// timestamp, message
using LogEntry = std::tuple<std::string, std::string>;
enum Priority
{
error,
info,
quiet
};
Logger() = delete;
~Logger() = default;
Logger(const Logger&) = default;
Logger& operator=(const Logger&) = default;
Logger(Logger&&) = default;
Logger& operator=(Logger&&) = default;
/**
* @brief Constructor
*
* @param[in] maxEntries - The maximum number of log entries
* to keep.
*/
explicit Logger(size_t maxEntries) : _maxEntries(maxEntries)
{
assert(maxEntries != 0);
}
/**
* @brief Places an entry in the log and writes it to the journal.
*
* @param[in] message - The log message
*
* @param[in] priority - The priority for the journal
*/
void log(const std::string& message, Priority priority = Logger::info)
{
if (priority == Logger::error)
{
phosphor::logging::log<phosphor::logging::level::ERR>(
message.c_str());
}
else if (priority != Logger::quiet)
{
phosphor::logging::log<phosphor::logging::level::INFO>(
message.c_str());
}
if (_entries.size() == _maxEntries)
{
_entries.erase(_entries.begin());
}
// Generate a timestamp
auto t = std::time(nullptr);
auto tm = *std::localtime(&t);
// e.g. Sep 22 19:56:32
auto timestamp = std::put_time(&tm, "%b %d %H:%M:%S");
std::ostringstream stream;
stream << timestamp;
_entries.emplace_back(stream.str(), message);
}
/**
* @brief Returns the entries in a JSON array
*
* @return JSON
*/
const nlohmann::json getLogs() const
{
return _entries;
}
/**
* @brief Writes the data to a temporary file and returns the path
* to it.
*
* Uses a temp file because the only use case for this is for sending
* in to an event log where a temp file makes sense, and frankly it
* was just easier to encapsulate everything here.
*
* @return path - The path to the file.
*/
std::filesystem::path saveToTempFile()
{
using namespace std::literals::string_literals;
char tmpFile[] = "/tmp/loggertemp.XXXXXX";
util::FileDescriptor fd{mkstemp(tmpFile)};
if (fd() == -1)
{
throw std::runtime_error{"mkstemp failed!"};
}
std::filesystem::path path{tmpFile};
for (const auto& [time, message] : _entries)
{
auto line = std::format("{}: {}\n", time, message);
auto rc = write(fd(), line.data(), line.size());
if (rc == -1)
{
auto e = errno;
auto msg = std::format(
"Could not write to temp file {} errno {}", tmpFile, e);
log(msg, Logger::error);
throw std::runtime_error{msg};
}
}
return std::filesystem::path{tmpFile};
}
/**
* @brief Deletes all log entries
*/
void clear()
{
_entries.clear();
}
private:
/**
* @brief The maximum number of entries to hold
*/
const size_t _maxEntries;
/**
* @brief The vector of <timestamp, message> entries
*/
std::vector<LogEntry> _entries;
};
} // namespace phosphor::fan