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