1 #include <wayfire/util/log.hpp>
2 #include <sstream>
3 #include <functional>
4 #include <iostream>
5 #include <map>
6 #include <chrono>
7 #include <iomanip>
8
9 template<>
to_string(void * arg)10 std::string wf::log::to_string<void*>(void *arg)
11 {
12 if (!arg)
13 {
14 return "(null)";
15 }
16
17 std::ostringstream out;
18 out << arg;
19 return out.str();
20 }
21
22 template<>
to_string(bool arg)23 std::string wf::log::to_string(bool arg)
24 {
25 return arg ? "true" : "false";
26 }
27
28 /**
29 * A singleton to hold log configuration.
30 */
31 struct log_global_t
32 {
33 std::reference_wrapper<std::ostream> out = std::ref(std::cout);
34
35 wf::log::log_level_t level = wf::log::LOG_LEVEL_INFO;
36 wf::log::color_mode_t color_mode = wf::log::LOG_COLOR_MODE_OFF;
37 std::string strip_path = "";
38
39 std::string clear_color = "";
40
getlog_global_t41 static log_global_t& get()
42 {
43 static log_global_t instance;
44 return instance;
45 }
46
47 private:
log_global_tlog_global_t48 log_global_t()
49 {}
50 };
51
initialize_logging(std::ostream & output_stream,log_level_t minimum_level,color_mode_t color_mode,std::string strip_path)52 void wf::log::initialize_logging(std::ostream& output_stream,
53 log_level_t minimum_level, color_mode_t color_mode, std::string strip_path)
54 {
55 auto& state = log_global_t::get();
56 state.out = std::ref(output_stream);
57 state.level = minimum_level;
58 state.color_mode = color_mode;
59 state.strip_path = strip_path;
60
61 if (state.color_mode == LOG_COLOR_MODE_ON)
62 {
63 state.clear_color = "\033[0m";
64 }
65 }
66
67 /** Get the line prefix for the given log level */
get_level_prefix(wf::log::log_level_t level)68 static std::string get_level_prefix(wf::log::log_level_t level)
69 {
70 bool color = log_global_t::get().color_mode == wf::log::LOG_COLOR_MODE_ON;
71 static std::map<wf::log::log_level_t, std::string> color_codes =
72 {
73 {wf::log::LOG_LEVEL_DEBUG, "\033[0m"},
74 {wf::log::LOG_LEVEL_INFO, "\033[0;34m"},
75 {wf::log::LOG_LEVEL_WARN, "\033[0;33m"},
76 {wf::log::LOG_LEVEL_ERROR, "\033[1;31m"},
77 };
78
79 static std::map<wf::log::log_level_t, std::string> line_prefix =
80 {
81 {wf::log::LOG_LEVEL_DEBUG, "DD"},
82 {wf::log::LOG_LEVEL_INFO, "II"},
83 {wf::log::LOG_LEVEL_WARN, "WW"},
84 {wf::log::LOG_LEVEL_ERROR, "EE"},
85 };
86
87 if (color)
88 {
89 return color_codes[level] + line_prefix[level];
90 }
91
92 return line_prefix[level];
93 }
94
95 /** Get the current time and date in a suitable format. */
get_formatted_date_time()96 static std::string get_formatted_date_time()
97 {
98 using namespace std::chrono;
99 auto now = system_clock::now();
100 auto tt = system_clock::to_time_t(now);
101 auto ms = duration_cast<milliseconds>(now.time_since_epoch()) % 1000;
102
103 std::ostringstream out;
104 out << std::put_time(std::localtime(&tt), "%d-%m-%y %H:%M:%S.");
105 out << std::setfill('0') << std::setw(3) << ms.count();
106 return out.str();
107 }
108
109 /** Strip the strip_path from the given path. */
strip_path(const std::string & path)110 static std::string strip_path(const std::string& path)
111 {
112 auto prefix = log_global_t::get().strip_path;
113 if ((prefix.length() > 0) && (path.find(prefix) == 0))
114 {
115 return path.substr(prefix.length());
116 }
117
118 std::string skip_chars = "./";
119 size_t idx = path.find_first_not_of(skip_chars);
120 if (idx != std::string::npos)
121 {
122 return path.substr(idx);
123 }
124
125 return path;
126 }
127
128 /**
129 * Log a plain message to the given output stream.
130 * The output format is:
131 *
132 * LL DD-MM-YY HH:MM:SS.MSS - [source:line] message
133 *
134 * @param level The log level of the passed message.
135 * @param contents The message to be printed.
136 * @param source The file where the message originates from. The prefix
137 * strip_path specified in initialize_logging will be removed, if it exists.
138 * @param line The line number of @source
139 */
log_plain(log_level_t level,const std::string & contents,const std::string & source,int line_nr)140 void wf::log::log_plain(log_level_t level, const std::string& contents,
141 const std::string& source, int line_nr)
142 {
143 auto& state = log_global_t::get();
144 if (state.level > level)
145 {
146 return;
147 }
148
149 std::string path_info;
150 if (!source.empty())
151 {
152 path_info = wf::log::detail::format_concat(
153 "[", strip_path(source), ":", line_nr, "] ");
154 }
155
156 state.out.get() <<
157 wf::log::detail::format_concat(
158 get_level_prefix(level), " ",
159 get_formatted_date_time(),
160 " - ", path_info, contents) <<
161 state.clear_color << std::endl;
162 }
163