|
5 | 5 | #else |
6 | 6 |
|
7 | 7 | #include <chrono> |
8 | | -#include <cstring> |
9 | | -#include <ctime> |
| 8 | +#include <iomanip> |
10 | 9 | #include <iostream> |
11 | | -#include <mutex> |
| 10 | +#include <sstream> |
12 | 11 | #include <string> |
13 | 12 |
|
14 | | -#ifdef _MSC_VER |
15 | | -#define gmt(a, b) gmtime_s(b, a) |
16 | | -#else |
17 | | -#define gmt(a, b) gmtime_r(a, b) |
18 | | -#endif |
| 13 | +#include "fmt/core.h" |
| 14 | +#include "fmt/ostream.h" |
19 | 15 |
|
20 | | -#define FILE_NAME \ |
21 | | - (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) |
| 16 | +#define STRINGIFY(x) STRINGIFY_(x) |
| 17 | +#define STRINGIFY_(x) #x |
22 | 18 |
|
23 | | -#define uLOG(lvl) \ |
24 | | - utl::log() << "[" << utl::log::str[lvl] << "]" \ |
25 | | - << "[" << utl::time() << "]" \ |
26 | | - << "[" << FILE_NAME << ":" << __LINE__ << "]" \ |
27 | | - << " " |
| 19 | +#define FILE_AND_LINE (__FILE__ ":" STRINGIFY(__LINE__)) |
| 20 | +#if defined(_WIN32) |
| 21 | +#define FILE_AND_LINE_SHORT \ |
| 22 | + (strrchr(FILE_AND_LINE, '\\') ? strrchr(FILE_AND_LINE, '\\') + 1 \ |
| 23 | + : FILE_AND_LINE) |
| 24 | +#else |
| 25 | +#define FILE_AND_LINE_SHORT \ |
| 26 | + (strrchr(FILE_AND_LINE, '/') ? strrchr(FILE_AND_LINE, '/') + 1 \ |
| 27 | + : FILE_AND_LINE) |
| 28 | +#endif |
28 | 29 |
|
29 | 30 | namespace utl { |
30 | 31 |
|
31 | | -/** |
32 | | - Entrypoint for all Motis logs |
33 | | -*/ |
34 | | -struct log { |
35 | | - log() = default; |
36 | | - |
37 | | - log(log const&) = delete; |
38 | | - log& operator=(log const&) = delete; |
39 | | - |
40 | | - log(log&&) = default; |
41 | | - log& operator=(log&&) = default; |
| 32 | +enum class log_level { debug, info, error }; |
42 | 33 |
|
43 | | - template <typename T> |
44 | | - friend log&& operator<<(log&& l, T&& t) { |
45 | | - std::clog << std::forward<T&&>(t); |
46 | | - return std::move(l); |
| 34 | +constexpr char const* to_str(log_level const level) { |
| 35 | + switch (level) { |
| 36 | + case log_level::debug: return "debug"; |
| 37 | + case log_level::info: return "info"; |
| 38 | + case log_level::error: return "error"; |
47 | 39 | } |
| 40 | + return ""; |
| 41 | +} |
48 | 42 |
|
49 | | - ~log() { std::clog << std::endl; } |
50 | | - |
51 | | - static constexpr const char* const str[]{"emrg", "alrt", "crit", "erro", |
52 | | - "warn", "note", "info", "debg"}; |
53 | | -}; |
| 43 | +static log_level s_verbosity; |
54 | 44 |
|
55 | | -enum log_level { emrg, alrt, crit, err, warn, notice, info, debug }; |
| 45 | +inline std::string now() { |
| 46 | + using clock = std::chrono::system_clock; |
| 47 | + auto const now = clock::to_time_t(clock::now()); |
| 48 | + struct tm tmp {}; |
| 49 | +#if _MSC_VER >= 1400 |
| 50 | + gmtime_s(&tmp, &now); |
| 51 | +#else |
| 52 | + gmtime_r(&now, &tmp); |
| 53 | +#endif |
56 | 54 |
|
57 | | -/** |
58 | | - Format a timestamp as an ISO 8601 string |
59 | | -*/ |
60 | | -inline std::string time(time_t const t) { |
61 | | - char buf[sizeof "2011-10-08t07:07:09z-0430"]; |
62 | | - struct tm result {}; |
63 | | - gmt(&t, &result); |
64 | | - strftime(buf, sizeof buf, "%FT%TZ%z", &result); |
65 | | - return buf; |
| 55 | + std::stringstream ss; |
| 56 | + ss << std::put_time(&tmp, "%FT%TZ"); |
| 57 | + return ss.str(); |
66 | 58 | } |
67 | 59 |
|
68 | | -/** |
69 | | - Format the current time as an ISO 8601 string |
70 | | -*/ |
71 | | -inline std::string time() { |
72 | | - time_t now; |
73 | | - std::time(&now); |
74 | | - return time(now); |
| 60 | +/// Produce a new log line at the given `level`, with the given prefix `ctx` and |
| 61 | +/// message |
| 62 | +template <typename... Args> |
| 63 | +void log(log_level const level, char const* ctx, |
| 64 | + fmt::format_string<Args...> fmt_str, Args&&... args) { |
| 65 | + if (level >= ::utl::s_verbosity) { |
| 66 | + fmt::print(std::clog, "{time} [{level}] [{ctx}] {msg}\n", |
| 67 | + fmt::arg("time", now()), fmt::arg("level", to_str(level)), |
| 68 | + fmt::arg("ctx", ctx), |
| 69 | + fmt::arg("msg", fmt::format(fmt::runtime(fmt_str), |
| 70 | + std::forward<Args>(args)...))); |
| 71 | + } |
75 | 72 | } |
76 | 73 |
|
77 | 74 | } // namespace utl |
78 | 75 |
|
79 | | -#endif |
| 76 | +/** |
| 77 | + * Shorthand to invoke utl::log without specifying the namespace |
| 78 | + */ |
| 79 | +#define log(level, ctx, fmt_str, ...) \ |
| 80 | + utl::log(utl::log_level::##level, ctx, fmt_str, __VA_ARGS__) |
| 81 | + |
| 82 | +/** |
| 83 | + * Invoke utl::log using the current C++ filename & line number as ctx |
| 84 | + */ |
| 85 | +#define logF(level, fmt_str, ...) \ |
| 86 | + log(level, FILE_AND_LINE_SHORT, fmt_str, __VA_ARGS__) |
| 87 | + |
| 88 | +#endif // LOGGING_HEADER |
0 commit comments