forked from RPCS3/rpcs3
-
Notifications
You must be signed in to change notification settings - Fork 3
/
logs.hpp
186 lines (146 loc) · 4.13 KB
/
logs.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
176
177
178
179
180
181
182
183
184
185
186
#pragma once // No BOM and only basic ASCII in this header, or a neko will die
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <initializer_list>
#include "util/atomic.hpp"
#include "Utilities/StrFmt.h"
namespace logs
{
enum class level : unsigned char
{
always = 0, // Highest log severity (cannot be disabled)
fatal = 1,
error = 2,
todo = 3,
success = 4,
warning = 5,
notice = 6,
trace = 7, // Lowest severity (usually disabled)
};
struct channel;
// Message information
struct message
{
// Default constructor
consteval message() = default;
// Cannot be moved because it relies on its location
message(const message&) = delete;
message& operator =(const message&) = delete;
// Send log message to the given channel with severity
template <typename... Args>
void operator()(const const_str& fmt, const Args&... args) const;
operator level() const
{
return level(uchar(reinterpret_cast<uptr>(this) & 7));
}
const channel* operator->() const
{
return reinterpret_cast<const channel*>(reinterpret_cast<uptr>(this) & -16);
}
private:
// Send log message to global logger instance
void broadcast(const char*, const fmt_type_info*, ...) const;
friend struct channel;
};
struct stored_message
{
const message& m;
u64 stamp;
std::string prefix;
std::string text;
};
class listener
{
// Next listener (linked list)
atomic_t<listener*> m_next{};
friend struct message;
public:
constexpr listener() = default;
virtual ~listener();
// Process log message
virtual void log(u64 stamp, const message& msg, const std::string& prefix, const std::string& text) = 0;
// Add new listener
static void add(listener*);
// Special purpose
void broadcast(const stored_message&) const;
};
struct alignas(16) channel : private message
{
// Channel prefix (added to every log message)
const char* const name;
// The lowest logging level enabled for this channel (used for early filtering)
atomic_t<level> enabled;
// Initialize channel
consteval channel(const char* name) noexcept
: message{}
, name(name)
, enabled(level::notice)
{
}
// Special access to "always visible" channel which shouldn't be used
const message& always() const
{
return *this;
}
#define GEN_LOG_METHOD(_sev)\
const message _sev{};\
GEN_LOG_METHOD(fatal)
GEN_LOG_METHOD(error)
GEN_LOG_METHOD(todo)
GEN_LOG_METHOD(success)
GEN_LOG_METHOD(warning)
GEN_LOG_METHOD(notice)
GEN_LOG_METHOD(trace)
#undef GEN_LOG_METHOD
};
template <typename... Args>
FORCE_INLINE SAFE_BUFFERS(void) message::operator()(const const_str& fmt, const Args&... args) const
{
if (*this <= (*this)->enabled.observe()) [[unlikely]]
{
if constexpr (sizeof...(Args) > 0)
{
broadcast(fmt, fmt::type_info_v<Args...>, u64{fmt_unveil<Args>::get(args)}...);
}
else
{
broadcast(fmt, nullptr);
}
}
}
struct registerer
{
registerer(channel& _ch);
};
// Log level control: set all channels to level::notice
void reset();
// Log level control: set all channels to level::always
void silence();
// Log level control: register channel if necessary, set channel level
void set_level(const std::string&, level);
// Log level control: get channel level
level get_level(const std::string&);
// Log level control: set specific channels to level::fatal
void set_channel_levels(const std::map<std::string, logs::level>& map);
// Get all registered log channels
std::vector<std::string> get_channels();
// Helper: no additional name specified
consteval const char* make_channel_name(const char* name, const char* alt = nullptr)
{
return alt ? alt : name;
}
// Called in main()
std::unique_ptr<logs::listener> make_file_listener(const std::string& path, u64 max_size);
// Called in main()
void set_init(std::initializer_list<stored_message>);
}
#if __cpp_constinit >= 201907
#define LOG_CONSTINIT constinit
#else
#define LOG_CONSTINIT
#endif
#define LOG_CHANNEL(ch, ...) LOG_CONSTINIT inline ::logs::channel ch(::logs::make_channel_name(#ch, ##__VA_ARGS__)); \
namespace logs { inline ::logs::registerer reg_##ch{ch}; }
LOG_CHANNEL(rsx_log, "RSX");