Line data Source code
1 : // Copyright (c) 2013-2022 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/snaplogger
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 :
20 : /** \file
21 : * \brief Appenders are used to append data to somewhere.
22 : *
23 : * This file declares the base appender class.
24 : */
25 :
26 : // self
27 : //
28 : #include "snaplogger/console_appender.h"
29 :
30 : #include "snaplogger/guard.h"
31 :
32 :
33 : // snapdev lib
34 : //
35 : #include <snapdev/lockfile.h>
36 :
37 :
38 : // boost lib
39 : //
40 : #include <boost/algorithm/string/join.hpp>
41 :
42 :
43 : // C++ lib
44 : //
45 : #include <iostream>
46 : #include <set>
47 :
48 :
49 : // C lib
50 : //
51 : #include <fcntl.h>
52 : #include <sys/stat.h>
53 : #include <sys/types.h>
54 :
55 :
56 : // last include
57 : //
58 : #include <snapdev/poison.h>
59 :
60 :
61 :
62 : namespace snaplogger
63 : {
64 :
65 :
66 : namespace
67 : {
68 :
69 :
70 :
71 7 : APPENDER_FACTORY(console);
72 :
73 :
74 :
75 : struct name_to_style
76 : {
77 : char const * f_name = nullptr;
78 : char const * f_style = nullptr;
79 : char const * f_unstyle = nullptr;
80 : };
81 :
82 : // See: https://en.wikipedia.org/wiki/ANSI_escape_code
83 : name_to_style const g_name_to_style[] =
84 : {
85 : { "bg-black", "\x1B[40m", "\x1B[0m" },
86 : { "bg-blue", "\x1B[44m", "\x1B[0m" },
87 : { "bg-bright-black", "\x1B[100m", "\x1B[0m" },
88 : { "bg-bright-blue", "\x1B[104m", "\x1B[0m" },
89 : { "bg-bright-cyan", "\x1B[106m", "\x1B[0m" },
90 : { "bg-bright-green", "\x1B[102m", "\x1B[0m" },
91 : { "bg-bright-magenta", "\x1B[105m", "\x1B[0m" },
92 : { "bg-bright-red", "\x1B[101m", "\x1B[0m" },
93 : { "bg-bright-white", "\x1B[107m", "\x1B[0m" },
94 : { "bg-bright-yellow", "\x1B[103m", "\x1B[0m" },
95 : { "bg-cyan", "\x1B[46m", "\x1B[0m" },
96 : { "bg-green", "\x1B[42m", "\x1B[0m" },
97 : { "bg-orange", "\x1B[43m", "\x1B[0m" },
98 : { "bg-magenta", "\x1B[45m", "\x1B[0m" },
99 : { "bg-red", "\x1B[41m", "\x1B[0m" },
100 : { "bg-white", "\x1B[47m", "\x1B[0m" },
101 : { "bg-yellow", "\x1B[43m", "\x1B[0m" },
102 : { "black", "\x1B[30m", "\x1B[0m" },
103 : { "blink-off", "\x1B[25m", "\x1B[0m" },
104 : { "blue", "\x1B[34m", "\x1B[0m" },
105 : { "bold", "\x1B[1m", "\x1B[0m" },
106 : { "bright-black", "\x1B[90m", "\x1B[0m" },
107 : { "bright-blue", "\x1B[94m", "\x1B[0m" },
108 : { "bright-cyan", "\x1B[96m", "\x1B[0m" },
109 : { "bright-green", "\x1B[92m", "\x1B[0m" },
110 : { "bright-magenta", "\x1B[95m", "\x1B[0m" },
111 : { "bright-red", "\x1B[91m", "\x1B[0m" },
112 : { "bright-white", "\x1B[97m", "\x1B[0m" },
113 : { "bright-yellow", "\x1B[93m", "\x1B[0m" },
114 : { "conceal", "\x1B[8m", "\x1B[0m" },
115 : { "crossed-out", "\x1B[9m", "\x1B[0m" },
116 : { "cyan", "\x1B[36m", "\x1B[0m" },
117 : { "default-font", "\x1B[10m", "\x1B[0m" },
118 : { "default-background-color", "\x1B[49m", "\x1B[0m" },
119 : { "default-foreground-color", "\x1B[39m", "\x1B[0m" },
120 : { "double-underline", "\x1B[21m", "\x1B[0m" }, // may remove bold instead
121 : { "encircled", "\x1B[52m", "\x1B[0m" },
122 : { "faint", "\x1B[2m", "\x1B[0m" },
123 : { "font0", "\x1B[10m", "\x1B[0m" },
124 : { "font1", "\x1B[11m", "\x1B[0m" },
125 : { "font2", "\x1B[12m", "\x1B[0m" },
126 : { "font3", "\x1B[13m", "\x1B[0m" },
127 : { "font4", "\x1B[14m", "\x1B[0m" },
128 : { "font5", "\x1B[15m", "\x1B[0m" },
129 : { "font6", "\x1B[16m", "\x1B[0m" },
130 : { "font7", "\x1B[17m", "\x1B[0m" },
131 : { "font8", "\x1B[18m", "\x1B[0m" },
132 : { "font9", "\x1B[19m", "\x1B[0m" },
133 : { "font10", "\x1B[20m", "\x1B[0m" },
134 : { "fraktur", "\x1B[20m", "\x1B[0m" },
135 : { "framed", "\x1B[51m", "\x1B[0m" },
136 : { "green", "\x1B[32m", "\x1B[0m" },
137 : { "inverse-off", "\x1B[27m", "\x1B[0m" },
138 : { "italic", "\x1B[3m", "\x1B[0m" },
139 : { "magenta", "\x1B[35m", "\x1B[0m" },
140 : { "normal", "\x1B[22m", "\x1B[0m" }, // remove bold & faint
141 : { "not-crossed-out", "\x1B[29m", "\x1B[0m" },
142 : { "not-framed", "\x1B[54m", "\x1B[0m" }, // remove italic & fraktur (font10)
143 : { "not-italic", "\x1B[23m", "\x1B[0m" }, // remove italic & fraktur (font10)
144 : { "not-overline", "\x1B[55m", "\x1B[0m" },
145 : { "orange", "\x1B[33m", "\x1B[0m" },
146 : { "overline", "\x1B[53m", "\x1B[0m" },
147 : { "rapid-blink", "\x1B[6m", "\x1B[0m" },
148 : { "red", "\x1B[31m", "\x1B[0m" },
149 : { "reveal", "\x1B[28m", "\x1B[0m" },
150 : { "reverse-video", "\x1B[7m", "\x1B[0m" },
151 : { "slow-blink", "\x1B[5m", "\x1B[0m" },
152 : { "standout", "\x1B[7m", "\x1B[0m" },
153 : { "underline", "\x1B[4m", "\x1B[0m" },
154 : { "underline-off", "\x1B[24m", "\x1B[0m" },
155 : { "white", "\x1B[37m", "\x1B[0m" },
156 : { "yellow", "\x1B[33m", "\x1B[0m" }
157 : };
158 :
159 :
160 :
161 :
162 : }
163 : // no name namespace
164 :
165 :
166 :
167 0 : console_appender::console_appender(std::string const name)
168 0 : : appender(name, "console")
169 : {
170 0 : }
171 :
172 :
173 0 : console_appender::~console_appender()
174 : {
175 0 : }
176 :
177 :
178 0 : bool console_appender::unique() const
179 : {
180 0 : return true;
181 : }
182 :
183 :
184 0 : void console_appender::set_config(advgetopt::getopt const & opts)
185 : {
186 0 : appender::set_config(opts);
187 :
188 : // FORCE STYLE
189 : //
190 0 : std::string const force_style_field(get_name() + "::force_style");
191 0 : if(opts.is_defined(force_style_field))
192 : {
193 0 : f_force_style = advgetopt::is_true(opts.get_string(force_style_field));
194 : }
195 :
196 : // LOCK
197 : //
198 0 : std::string const lock_field(get_name() + "::lock");
199 0 : if(opts.is_defined(lock_field))
200 : {
201 0 : f_lock = advgetopt::is_true(opts.get_string(lock_field));
202 : }
203 :
204 : // FLUSH
205 : //
206 0 : std::string const flush_field(get_name() + "::flush");
207 0 : if(opts.is_defined(flush_field))
208 : {
209 0 : f_flush = advgetopt::is_true(opts.get_string(flush_field));
210 : }
211 :
212 : // OUTPUT
213 : //
214 0 : std::string const output_field(get_name() + "::output");
215 0 : if(opts.is_defined(output_field))
216 : {
217 0 : f_output = opts.get_string(output_field);
218 : }
219 0 : }
220 :
221 :
222 0 : void console_appender::process_message(message const & msg, std::string const & formatted_message)
223 : {
224 0 : guard g;
225 :
226 0 : if(!f_initialized)
227 : {
228 0 : f_initialized = true;
229 :
230 0 : if(f_output == "stderr")
231 : {
232 0 : f_fd = fileno(stderr);
233 : }
234 0 : else if(f_output == "stdout")
235 : {
236 0 : f_fd = fileno(stdout);
237 : }
238 0 : else if(f_output == "console")
239 : {
240 0 : f_console.reset(open("/dev/console", O_WRONLY | O_APPEND | O_CLOEXEC | O_NOCTTY));
241 0 : f_fd = f_console.get();
242 : }
243 :
244 0 : f_is_a_tty = isatty(f_fd);
245 : }
246 :
247 0 : if(f_fd == -1)
248 : {
249 0 : return;
250 : }
251 :
252 0 : std::unique_ptr<snapdev::lockfd> lock_file;
253 0 : if(f_lock)
254 : {
255 0 : lock_file = std::make_unique<snapdev::lockfd>(f_fd, snapdev::lockfd::mode_t::LOCKFILE_EXCLUSIVE);
256 : }
257 :
258 0 : std::string style;
259 0 : std::string unstyle;
260 0 : if(f_is_a_tty || f_force_style)
261 : {
262 0 : severity::pointer_t const sev(snaplogger::get_severity(msg, msg.get_severity()));
263 0 : if(sev != nullptr)
264 : {
265 0 : advgetopt::string_list_t styles;
266 0 : advgetopt::split_string(sev->get_styles(), styles, {","});
267 0 : if(!styles.empty())
268 : {
269 0 : std::set<std::string> style_set;
270 0 : std::set<std::string> unstyle_set;
271 0 : for(auto s : styles)
272 : {
273 0 : int i(0);
274 0 : int j(sizeof(g_name_to_style) / sizeof(g_name_to_style[0]));
275 0 : while(i < j)
276 : {
277 0 : int const p(i + (j - i) / 2);
278 0 : int const r(s.compare(g_name_to_style[p].f_name));
279 0 : if(r == 0)
280 : {
281 0 : style_set.insert(g_name_to_style[p].f_style);
282 0 : unstyle_set.insert(g_name_to_style[p].f_unstyle);
283 0 : break;
284 : }
285 0 : if(r < 0)
286 : {
287 0 : j = p;
288 : }
289 : else // if(r > 0)
290 : {
291 0 : i = p + 1;
292 : }
293 : }
294 : }
295 0 : if(!style_set.empty())
296 : {
297 0 : style = boost::algorithm::join(style_set, std::string());
298 : }
299 0 : if(!unstyle_set.empty())
300 : {
301 0 : unstyle = boost::algorithm::join(unstyle_set, std::string());
302 : }
303 : }
304 : }
305 : }
306 :
307 0 : ssize_t const l1(write(f_fd, style.c_str(), style.length()));
308 0 : if(static_cast<size_t>(l1) == style.length())
309 : {
310 0 : ssize_t const l2(write(f_fd, formatted_message.c_str(), formatted_message.length()));
311 0 : if(static_cast<size_t>(l2) == formatted_message.length())
312 : {
313 0 : ssize_t const l3(write(f_fd, unstyle.c_str(), unstyle.length()));
314 0 : if(static_cast<size_t>(l3) == unstyle.length())
315 : {
316 0 : return;
317 : }
318 : }
319 : }
320 :
321 : // an error occurred, what can we do about it?!
322 : }
323 :
324 :
325 :
326 :
327 :
328 6 : } // snaplogger namespace
329 : // vim: ts=4 sw=4 et
|