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/file_appender.h"
35 :
36 : #include "snaplogger/guard.h"
37 : #include "snaplogger/map_diagnostic.h"
38 :
39 :
40 : // snapdev lib
41 : //
42 : #include <snapdev/lockfile.h>
43 :
44 :
45 : // C++ lib
46 : //
47 : #include <iostream>
48 :
49 :
50 : // C lib
51 : //
52 : #include <sys/types.h>
53 : #include <sys/stat.h>
54 : #include <fcntl.h>
55 : #include <unistd.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 8 : APPENDER_FACTORY(file);
73 :
74 :
75 : }
76 : // no name namespace
77 :
78 :
79 :
80 0 : file_appender::file_appender(std::string const name)
81 0 : : appender(name, "file")
82 : {
83 0 : }
84 :
85 :
86 0 : file_appender::~file_appender()
87 : {
88 0 : }
89 :
90 :
91 0 : void file_appender::set_config(advgetopt::getopt const & opts)
92 : {
93 0 : appender::set_config(opts);
94 :
95 : // PATH
96 : //
97 0 : std::string const path_field(get_name() + "::path");
98 0 : if(opts.is_defined(path_field))
99 : {
100 0 : f_path = opts.get_string(path_field);
101 : }
102 0 : else if(opts.is_defined("path"))
103 : {
104 0 : f_path = opts.get_string("path");
105 : }
106 :
107 : // FILENAME
108 : //
109 0 : std::string const filename_field(get_name() + "::filename");
110 0 : if(opts.is_defined(filename_field))
111 : {
112 0 : f_filename = opts.get_string(filename_field);
113 : }
114 : // else -- we'll try to dynamically determine a filename when we
115 : // reach the send_message() function
116 :
117 : // LOCK
118 : //
119 0 : std::string const lock_field(get_name() + "::lock");
120 0 : if(opts.is_defined(lock_field))
121 : {
122 0 : f_lock = opts.get_string(lock_field) == "true";
123 : }
124 :
125 : // FLUSH
126 : //
127 0 : std::string const flush_field(get_name() + "::flush");
128 0 : if(opts.is_defined(flush_field))
129 : {
130 0 : f_flush = opts.get_string(flush_field) == "true";
131 : }
132 :
133 : // SECURE
134 : //
135 0 : std::string const secure_field(get_name() + "::secure");
136 0 : if(opts.is_defined(secure_field))
137 : {
138 0 : f_secure = opts.get_string(secure_field) != "false";
139 : }
140 :
141 : // FALLBACK TO CONSOLE
142 : //
143 0 : std::string const fallback_to_console_field(get_name() + "::fallback_to_console");
144 0 : if(opts.is_defined(fallback_to_console_field))
145 : {
146 0 : f_fallback_to_console = opts.get_string(fallback_to_console_field) == "true";
147 : }
148 0 : }
149 :
150 :
151 0 : void file_appender::reopen()
152 : {
153 0 : guard g;
154 :
155 0 : f_fd.reset();
156 0 : f_initialized = false;
157 0 : }
158 :
159 :
160 0 : void file_appender::set_filename(std::string const & filename)
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
|