1 /*
2 ** Copyright 2011-2014 Centreon
3 **
4 ** Licensed under the Apache License, Version 2.0 (the "License");
5 ** you may not use this file except in compliance with the License.
6 ** You may obtain a copy of the License at
7 **
8 **     http://www.apache.org/licenses/LICENSE-2.0
9 **
10 ** Unless required by applicable law or agreed to in writing, software
11 ** distributed under the License is distributed on an "AS IS" BASIS,
12 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 ** See the License for the specific language governing permissions and
14 ** limitations under the License.
15 **
16 ** For more information : contact@centreon.com
17 */
18 
19 #include "com/centreon/logging/file.hh"
20 #include <cerrno>
21 #include <cstdlib>
22 #include <cstring>
23 #include "com/centreon/exceptions/basic.hh"
24 #include "com/centreon/misc/stringifier.hh"
25 
26 using namespace com::centreon::logging;
27 
28 /**
29  *  Default constructor.
30  *
31  *  @param[in] file            The file to used.
32  *  @param[in] is_sync         Enable synchronization.
33  *  @param[in] show_pid        Enable show pid.
34  *  @param[in] show_timestamp  Enable show timestamp.
35  *  @param[in] show_thread_id  Enable show thread id.
36  *  @param[in] max_size        Maximum file size.
37  */
file(FILE * file,bool is_sync,bool show_pid,time_precision show_timestamp,bool show_thread_id,uint64_t max_size)38 file::file(FILE* file,
39            bool is_sync,
40            bool show_pid,
41            time_precision show_timestamp,
42            bool show_thread_id,
43            uint64_t max_size)
44     : backend(is_sync, show_pid, show_timestamp, show_thread_id),
45       _max_size(max_size),
46       _out(file),
47       _size(0) {}
48 
49 /**
50  *  Constructor with file path name.
51  *
52  *  @param[in] path            The path of the file to used.
53  *  @param[in] is_sync         Enable synchronization.
54  *  @param[in] show_pid        Enable show pid.
55  *  @param[in] show_timestamp  Enable show timestamp.
56  *  @param[in] show_thread_id  Enable show thread id.
57  *  @param[in] max_size        Maximum file size.
58  */
file(std::string const & path,bool is_sync,bool show_pid,time_precision show_timestamp,bool show_thread_id,uint64_t max_size)59 file::file(std::string const& path,
60            bool is_sync,
61            bool show_pid,
62            time_precision show_timestamp,
63            bool show_thread_id,
64            uint64_t max_size)
65     : backend(is_sync, show_pid, show_timestamp, show_thread_id),
66       _max_size(max_size),
67       _path(path),
68       _out(NULL),
69       _size(0) {
70   open();
71 }
72 
73 /**
74  *  Default destructor.
75  */
~file()76 file::~file() noexcept {
77   close();
78 }
79 
80 /**
81  *  Close file.
82  */
close()83 void file::close() noexcept {
84   std::lock_guard<std::recursive_mutex> lock(_lock);
85 
86   if (!_out || _out == stdout || _out == stderr)
87     return;
88 
89   int ret;
90   do {
91     ret = fclose(_out);
92   } while (ret == -1 && errno == EINTR);
93   _out = NULL;
94 }
95 
96 /**
97  *  Get filename.
98  *
99  *  @return The filename string.
100  */
filename() const101 std::string const& file::filename() const noexcept {
102   return (_path);
103 }
104 
105 /**
106  *  Write message into the file.
107  *  @remark This method is thread safe.
108  *
109  *  @param[in] type     Logging types.
110  *  @param[in] verbose  Verbosity level.
111  *  @param[in] msg      The message to write.
112  *  @param[in] size     The message's size.
113  */
log(uint64_t types,uint32_t verbose,char const * msg,uint32_t size)114 void file::log(uint64_t types,
115                uint32_t verbose,
116                char const* msg,
117                uint32_t size) noexcept {
118   (void)types;
119   (void)verbose;
120   (void)size;
121 
122   misc::stringifier header;
123   _build_header(header);
124 
125   // Split msg by line.
126   misc::stringifier buffer;
127   uint32_t i(0);
128   uint32_t last(0);
129   while (msg[i]) {
130     if (msg[i] == '\n') {
131       buffer << header;
132       buffer.append(msg + last, i - last) << "\n";
133       last = i + 1;
134     }
135     ++i;
136   }
137   if (last != i) {
138     buffer << header;
139     buffer.append(msg + last, i - last) << "\n";
140   }
141 
142   std::lock_guard<std::recursive_mutex> lock(_lock);
143   if (_out) {
144     // Size control.
145     if ((_max_size > 0) && (_size + buffer.size() > _max_size))
146       _max_size_reached();
147     _size += buffer.size();
148 
149     // Physical write.
150     size_t ret;
151     do {
152       clearerr(_out);
153       ret = fwrite(buffer.data(), buffer.size(), 1, _out);
154     } while (ret != 1 && ferror(_out) && errno == EINTR);
155 
156     // Flush data if is necessary.
157     while (_is_sync && fflush(_out) < 0 && errno == EINTR)
158       ;
159   }
160 }
161 
162 /**
163  *  Open file.
164  */
open()165 void file::open() {
166   std::lock_guard<std::recursive_mutex> lock(_lock);
167 
168   if (_out && _path.empty())
169     return;
170 
171   if (!(_out = fopen(_path.c_str(), "a")))
172     throw(basic_error() << "failed to open file '" << _path
173                         << "': " << strerror(errno));
174   _size = ftell(_out);
175 
176   return;
177 }
178 
179 /**
180  *  Close and open file.
181  */
reopen()182 void file::reopen() {
183   std::lock_guard<std::recursive_mutex> lock(_lock);
184 
185   if (!_out || _out == stdout || _out == stderr)
186     return;
187 
188   int ret;
189   do {
190     ret = fclose(_out);
191   } while (ret == -1 && errno == EINTR);
192 
193   if (!(_out = fopen(_path.c_str(), "a")))
194     throw(basic_error() << "failed to open file '" << _path
195                         << "': " << strerror(errno));
196   _size = ftell(_out);
197 
198   return;
199 }
200 
201 /**
202  *  Method called when max size is reached.
203  */
_max_size_reached()204 void file::_max_size_reached() {
205   if (!_out || _out == stdout || _out == stderr)
206     return;
207 
208   int ret;
209   do {
210     ret = fclose(_out);
211   } while (ret == -1 && errno == EINTR);
212 
213   remove(_path.c_str());
214 
215   if (!(_out = fopen(_path.c_str(), "a")))
216     throw(basic_error() << "failed to open file '" << _path
217                         << "': " << strerror(errno));
218   _size = ftell(_out);
219 }
220