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