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