Line data Source code
1 : // Copyright (c) 2013-2021 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/file_appender.h"
29 :
30 : #include "snaplogger/syslog_appender.h"
31 : #include "snaplogger/guard.h"
32 : #include "snaplogger/map_diagnostic.h"
33 :
34 :
35 : // snapdev lib
36 : //
37 : #include <snapdev/lockfile.h>
38 :
39 :
40 : // C++ lib
41 : //
42 : #include <iostream>
43 :
44 :
45 : // C lib
46 : //
47 : #include <fcntl.h>
48 : #include <syslog.h>
49 : #include <sys/stat.h>
50 : #include <sys/types.h>
51 : #include <unistd.h>
52 :
53 :
54 : // last include
55 : //
56 : #include <snapdev/poison.h>
57 :
58 :
59 :
60 : namespace snaplogger
61 : {
62 :
63 :
64 : namespace
65 : {
66 :
67 :
68 8 : APPENDER_FACTORY(file);
69 :
70 :
71 : }
72 : // no name namespace
73 :
74 :
75 :
76 0 : file_appender::file_appender(std::string const & name)
77 0 : : appender(name, "file")
78 : {
79 0 : }
80 :
81 :
82 0 : file_appender::~file_appender()
83 : {
84 0 : }
85 :
86 :
87 0 : void file_appender::set_config(advgetopt::getopt const & opts)
88 : {
89 0 : guard g;
90 :
91 0 : appender::set_config(opts);
92 :
93 : // PATH
94 : //
95 0 : std::string const path_field(get_name() + "::path");
96 0 : if(opts.is_defined(path_field))
97 : {
98 0 : f_path = opts.get_string(path_field);
99 : }
100 0 : else if(opts.is_defined("path"))
101 : {
102 0 : f_path = opts.get_string("path");
103 : }
104 :
105 : // FILENAME
106 : //
107 0 : std::string const filename_field(get_name() + "::filename");
108 0 : if(opts.is_defined(filename_field))
109 : {
110 0 : f_filename = opts.get_string(filename_field);
111 : }
112 : // else -- we'll try to dynamically determine a filename when we
113 : // reach the send_message() function
114 :
115 : // LOCK
116 : //
117 0 : std::string const lock_field(get_name() + "::lock");
118 0 : if(opts.is_defined(lock_field))
119 : {
120 0 : f_lock = opts.get_string(lock_field) == "true";
121 : }
122 :
123 : // FLUSH
124 : //
125 0 : std::string const flush_field(get_name() + "::flush");
126 0 : if(opts.is_defined(flush_field))
127 : {
128 0 : f_flush = opts.get_string(flush_field) == "true";
129 : }
130 :
131 : // SECURE
132 : //
133 0 : std::string const secure_field(get_name() + "::secure");
134 0 : if(opts.is_defined(secure_field))
135 : {
136 0 : f_secure = opts.get_string(secure_field) != "false";
137 : }
138 :
139 : // FALLBACK TO CONSOLE
140 : //
141 0 : std::string const fallback_to_console_field(get_name() + "::fallback_to_console");
142 0 : if(opts.is_defined(fallback_to_console_field))
143 : {
144 0 : f_fallback_to_console = opts.get_string(fallback_to_console_field) == "true";
145 : }
146 :
147 : // FALLBACK TO SYSLOG
148 : //
149 0 : std::string const fallback_to_syslog_field(get_name() + "::fallback_to_syslog");
150 0 : if(opts.is_defined(fallback_to_syslog_field))
151 : {
152 0 : f_fallback_to_syslog = opts.get_string(fallback_to_syslog_field) == "true";
153 : }
154 0 : }
155 :
156 :
157 0 : void file_appender::reopen()
158 : {
159 0 : guard g;
160 :
161 0 : f_fd.reset();
162 0 : f_initialized = false;
163 0 : }
164 :
165 :
166 0 : void file_appender::set_filename(std::string const & filename)
167 : {
168 0 : guard g;
169 :
170 0 : if(f_filename != filename)
171 : {
172 0 : f_filename = filename;
173 0 : f_initialized = false;
174 : }
175 0 : }
176 :
177 :
178 0 : void file_appender::process_message(message const & msg, std::string const & formatted_message)
179 : {
180 0 : snapdev::NOT_USED(msg);
181 :
182 0 : guard g;
183 :
184 0 : if(!f_initialized)
185 : {
186 0 : f_initialized = true;
187 :
188 0 : if(f_filename.empty())
189 : {
190 : // try to generate a filename
191 : //
192 0 : map_diagnostics_t map(get_map_diagnostics());
193 0 : auto const it(map.find("progname"));
194 0 : if(it == map.end())
195 : {
196 0 : return;
197 : }
198 0 : if(it->second.empty())
199 : {
200 0 : return;
201 : }
202 :
203 0 : f_filename = f_path + "/";
204 0 : if(f_secure)
205 : {
206 0 : f_filename += "secure/";
207 : }
208 0 : f_filename += it->second;
209 0 : f_filename += ".log";
210 : }
211 0 : else if(f_filename.find('/') == std::string::npos)
212 : {
213 0 : f_filename = f_path + "/" + f_filename;
214 : }
215 0 : std::string::size_type pos(f_filename.rfind('/'));
216 0 : if(pos == std::string::npos)
217 : {
218 0 : pos = 0;
219 : }
220 0 : if(f_filename.find('.', pos + 1) == std::string::npos)
221 : {
222 0 : f_filename += ".log";
223 : }
224 :
225 0 : if(access(f_filename.c_str(), R_OK | W_OK) != 0
226 0 : && errno != ENOENT)
227 : {
228 0 : return;
229 : }
230 :
231 0 : int flags(O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC | O_LARGEFILE | O_NOCTTY);
232 0 : int mode(S_IRUSR | S_IWUSR);
233 0 : if(!f_secure)
234 : {
235 0 : mode |= S_IRGRP;
236 : }
237 :
238 0 : f_fd.reset(open(f_filename.c_str(), flags, mode));
239 : }
240 :
241 0 : if(!f_fd)
242 : {
243 0 : return;
244 : }
245 :
246 0 : std::unique_ptr<snapdev::lockfd> lock_file;
247 0 : if(f_lock)
248 : {
249 0 : lock_file = std::make_unique<snapdev::lockfd>(f_fd.get(), snapdev::lockfd::mode_t::LOCKFILE_EXCLUSIVE);
250 : }
251 :
252 0 : ssize_t const l(write(f_fd.get(), formatted_message.c_str(), formatted_message.length()));
253 0 : if(static_cast<size_t>(l) != formatted_message.length())
254 : {
255 : // how could we report that? we are the logger...
256 0 : if(f_fallback_to_console
257 0 : && isatty(fileno(stdout)))
258 : {
259 0 : std::cout << formatted_message.c_str();
260 : }
261 0 : else if(f_fallback_to_syslog)
262 : {
263 : // in this case we skip on the openlog() call...
264 : //
265 0 : int const priority(syslog_appender::message_severity_to_syslog_priority(msg.get_severity()));
266 0 : syslog(priority, "%s", formatted_message.c_str());
267 : }
268 : }
269 : }
270 :
271 :
272 :
273 :
274 :
275 6 : } // snaplogger namespace
276 : // vim: ts=4 sw=4 et
|