Line data Source code
1 : /*
2 : * Copyright (c) 2013-2021 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/file_appender.h"
31 :
32 : #include "snaplogger/guard.h"
33 : #include "snaplogger/map_diagnostic.h"
34 :
35 :
36 : // snapdev lib
37 : //
38 : #include <snapdev/lockfile.h>
39 :
40 :
41 : // C++ lib
42 : //
43 : #include <iostream>
44 :
45 :
46 : // C lib
47 : //
48 : #include <sys/types.h>
49 : #include <sys/stat.h>
50 : #include <fcntl.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 0 : }
147 :
148 :
149 0 : void file_appender::reopen()
150 : {
151 0 : guard g;
152 :
153 0 : f_fd.reset();
154 0 : f_initialized = false;
155 0 : }
156 :
157 :
158 0 : void file_appender::set_filename(std::string const & filename)
159 : {
160 0 : guard g;
161 :
162 0 : if(f_filename != filename)
163 : {
164 0 : f_filename = filename;
165 0 : f_initialized = false;
166 : }
167 0 : }
168 :
169 :
170 0 : void file_appender::process_message(message const & msg, std::string const & formatted_message)
171 : {
172 0 : snap::NOTUSED(msg);
173 :
174 0 : guard g;
175 :
176 0 : if(!f_initialized)
177 : {
178 0 : f_initialized = true;
179 :
180 0 : if(f_filename.empty())
181 : {
182 : // try to generate a filename
183 : //
184 0 : map_diagnostics_t map(get_map_diagnostics());
185 0 : auto const it(map.find("progname"));
186 0 : if(it == map.end())
187 : {
188 0 : return;
189 : }
190 0 : if(it->second.empty())
191 : {
192 0 : return;
193 : }
194 :
195 0 : f_filename = f_path + "/";
196 0 : if(f_secure)
197 : {
198 0 : f_filename += "secure/";
199 : }
200 0 : f_filename += it->second;
201 0 : f_filename += ".log";
202 : }
203 0 : else if(f_filename.find('/') == std::string::npos)
204 : {
205 0 : f_filename = f_path + "/" + f_filename;
206 : }
207 0 : std::string::size_type pos(f_filename.rfind('/'));
208 0 : if(pos == std::string::npos)
209 : {
210 0 : pos = 0;
211 : }
212 0 : if(f_filename.find('.', pos + 1) == std::string::npos)
213 : {
214 0 : f_filename += ".log";
215 : }
216 :
217 0 : if(access(f_filename.c_str(), R_OK | W_OK) != 0
218 0 : && errno != ENOENT)
219 : {
220 0 : return;
221 : }
222 :
223 0 : int flags(O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC | O_LARGEFILE | O_NOCTTY);
224 0 : int mode(S_IRUSR | S_IWUSR);
225 0 : if(!f_secure)
226 : {
227 0 : mode |= S_IRGRP;
228 : }
229 :
230 0 : f_fd.reset(open(f_filename.c_str(), flags, mode));
231 : }
232 :
233 0 : if(!f_fd)
234 : {
235 0 : return;
236 : }
237 :
238 0 : std::unique_ptr<snap::lockfd> lock_file;
239 0 : if(f_lock)
240 : {
241 0 : lock_file = std::make_unique<snap::lockfd>(f_fd.get(), snap::lockfd::mode_t::LOCKFILE_EXCLUSIVE);
242 : }
243 :
244 0 : ssize_t const l(write(f_fd.get(), formatted_message.c_str(), formatted_message.length()));
245 0 : if(static_cast<size_t>(l) != formatted_message.length())
246 : {
247 : // how could we report that? we are the logger...
248 0 : if(f_fallback_to_console
249 0 : && isatty(fileno(stdout)))
250 : {
251 0 : std::cout << formatted_message.c_str();
252 : }
253 : }
254 : }
255 :
256 :
257 :
258 :
259 :
260 6 : } // snaplogger namespace
261 : // vim: ts=4 sw=4 et
|