-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlog.cpp
More file actions
128 lines (107 loc) · 3.84 KB
/
Copy pathlog.cpp
File metadata and controls
128 lines (107 loc) · 3.84 KB
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
#include "log.h"
#include <Windows.h>
#include <cstdarg>
#include <cstdio>
#include <cwchar>
#include <chrono>
#include <ctime>
#include <mutex>
namespace {
struct State {
bool quiet = false;
bool noColor = false;
bool verbose = false;
bool ansi = false;
FILE* logFp = nullptr;
std::mutex mu;
} g;
bool TryEnableAnsi() {
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
if (h == INVALID_HANDLE_VALUE) return false;
DWORD mode = 0;
if (!GetConsoleMode(h, &mode)) return false;
return SetConsoleMode(h, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0;
}
const wchar_t* TagFor(Log::Level lv) {
switch (lv) {
case Log::Level::Debug: return L"DBG";
case Log::Level::Info: return L"INF";
case Log::Level::Warn: return L"WRN";
case Log::Level::Err: return L"ERR";
case Log::Level::Ok: return L" OK";
}
return L" ";
}
const wchar_t* ColorFor(Log::Level lv) {
switch (lv) {
case Log::Level::Debug: return L"\x1b[90m"; // grey
case Log::Level::Info: return L"\x1b[36m"; // cyan
case Log::Level::Warn: return L"\x1b[33m"; // yellow
case Log::Level::Err: return L"\x1b[31m"; // red
case Log::Level::Ok: return L"\x1b[32m"; // green
}
return L"";
}
void Timestamp(wchar_t* buf, size_t n) {
using namespace std::chrono;
auto now = system_clock::now();
std::time_t t = system_clock::to_time_t(now);
auto ms = duration_cast<milliseconds>(now.time_since_epoch()) % 1000;
std::tm tm{};
localtime_s(&tm, &t);
std::swprintf(buf, n, L"%02d:%02d:%02d.%03lld",
tm.tm_hour, tm.tm_min, tm.tm_sec, (long long)ms.count());
}
void Emit(Log::Level lv, const wchar_t* fmt, va_list ap) {
if (g.quiet && lv != Log::Level::Err && lv != Log::Level::Ok) return;
if (!g.verbose && lv == Log::Level::Debug) return;
wchar_t ts[32];
Timestamp(ts, 32);
wchar_t msg[2048];
std::vswprintf(msg, 2048, fmt, ap);
std::lock_guard<std::mutex> lk(g.mu);
FILE* out = (lv == Log::Level::Err || lv == Log::Level::Warn) ? stderr : stdout;
if (g.ansi && !g.noColor) {
std::fwprintf(out, L"\x1b[90m[%ls]\x1b[0m %ls[%ls]\x1b[0m %ls\n",
ts, ColorFor(lv), TagFor(lv), msg);
} else {
std::fwprintf(out, L"[%ls] [%ls] %ls\n", ts, TagFor(lv), msg);
}
std::fflush(out);
if (g.logFp) {
std::fwprintf(g.logFp, L"[%ls] [%ls] %ls\n", ts, TagFor(lv), msg);
std::fflush(g.logFp);
}
}
} // anon
namespace Log {
void Init(const std::filesystem::path& logFile, bool quiet, bool noColor, bool verbose) {
g.quiet = quiet;
g.noColor = noColor;
g.verbose = verbose;
g.ansi = TryEnableAnsi();
if (!logFile.empty()) {
_wfopen_s(&g.logFp, logFile.c_str(), L"a, ccs=UTF-8");
if (g.logFp) {
wchar_t ts[32]; Timestamp(ts, 32);
std::fwprintf(g.logFp, L"\n===== Injectra session %ls =====\n", ts);
std::fflush(g.logFp);
}
}
}
void Shutdown() {
std::lock_guard<std::mutex> lk(g.mu);
if (g.logFp) { std::fclose(g.logFp); g.logFp = nullptr; }
}
void Debug(const wchar_t* fmt, ...) { va_list ap; va_start(ap, fmt); Emit(Level::Debug, fmt, ap); va_end(ap); }
void Info (const wchar_t* fmt, ...) { va_list ap; va_start(ap, fmt); Emit(Level::Info, fmt, ap); va_end(ap); }
void Warn (const wchar_t* fmt, ...) { va_list ap; va_start(ap, fmt); Emit(Level::Warn, fmt, ap); va_end(ap); }
void Err (const wchar_t* fmt, ...) { va_list ap; va_start(ap, fmt); Emit(Level::Err, fmt, ap); va_end(ap); }
void Ok (const wchar_t* fmt, ...) { va_list ap; va_start(ap, fmt); Emit(Level::Ok, fmt, ap); va_end(ap); }
void Line() {
if (g.quiet) return;
std::lock_guard<std::mutex> lk(g.mu);
std::fputwc(L'\n', stdout);
std::fflush(stdout);
}
} // namespace Log