1 /*
2 * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3 *
4 * See the LICENSE file for terms of use.
5 */
6 #include <fstream>
7 #include <boost/algorithm/string.hpp>
8
9 #ifndef WT_DBO_LOGGER
10 #include "Wt/WLogger.h"
11 #include "Wt/WServer.h"
12 #include "Wt/WString.h"
13 #include "Wt/WLocalDateTime.h"
14 #include "Wt/WTime.h"
15 #include "WebUtils.h"
16 #include "WebSession.h"
17 #endif // WT_DBO_LOGGER
18
19 #include "StringUtils.h"
20
21 namespace Wt {
22
23 #ifdef WT_DBO_LOGGER
24 namespace Dbo {
25 #endif
26
27 namespace {
28 WLogger defaultLogger;
29 }
30
31 LOGGER("WLogger");
32
WLogEntry(WLogEntry && other)33 WLogEntry::WLogEntry(WLogEntry&& other)
34 : impl_(std::move(other.impl_))
35 { }
36
~WLogEntry()37 WLogEntry::~WLogEntry()
38 {
39 if (impl_) {
40 impl_->finish();
41 if (impl_->logger_)
42 impl_->logger_->addLine(impl_->type_, impl_->scope_, impl_->line_);
43 else if (impl_->customLogger_)
44 impl_->customLogger_->log(impl_->type_, impl_->scope_, impl_->line_.str());
45 }
46 }
47
48 WLogEntry& WLogEntry::operator<< (const WLogger::Sep&)
49 {
50 if (impl_)
51 impl_->nextField();
52
53 return *this;
54 }
55
56 #ifndef WT_DBO_LOGGER
57 WLogEntry& WLogEntry::operator<< (const WLogger::TimeStamp&)
58 {
59 std::string dt = WLocalDateTime::currentServerDateTime()
60 .toString("yyyy-MMM-dd hh:mm:ss.zzz").toUTF8();
61
62 return *this << '[' << dt << ']';
63 }
64 #endif // WT_DBO_LOGGER
65
66 WLogEntry& WLogEntry::operator<< (const char *s)
67 {
68 return *this << std::string(s);
69 }
70
71 #ifndef WT_DBO_LOGGER
72 WLogEntry& WLogEntry::operator<< (const WString& s)
73 {
74 return *this << s.toUTF8();
75 }
76 #endif // WT_DBO_LOGGER
77
78 WLogEntry& WLogEntry::operator<< (const std::string& s)
79 {
80 if (impl_) {
81 if (impl_->quote()) {
82 startField();
83
84 std::string ss(s);
85 Utils::replace(ss, '"', "\"\"");
86
87 impl_->line_ << ss;
88 } else {
89 if (!s.empty()) {
90 startField();
91 impl_->line_ << s;
92 }
93 }
94
95 if ((impl_->customLogger_ ||
96 impl_->field_ == (int)impl_->logger_->fields().size() - 1)
97 && impl_->scope_.empty())
98 impl_->scope_ = s;
99 }
100
101 return *this;
102 }
103
104 WLogEntry& WLogEntry::operator<< (char v)
105 {
106 startField();
107
108 if (impl_)
109 impl_->line_ << v;
110
111 return *this;
112 }
113
114 WLogEntry& WLogEntry::operator<< (int v)
115 {
116 startField();
117
118 if (impl_)
119 impl_->line_ << v;
120
121 return *this;
122 }
123
124 WLogEntry& WLogEntry::operator<< (long long v)
125 {
126 startField();
127
128 if (impl_)
129 impl_->line_ << v;
130
131 return *this;
132 }
133
134 WLogEntry& WLogEntry::operator<< (double v)
135 {
136 startField();
137
138 if (impl_)
139 impl_->line_ << v;
140
141 return *this;
142 }
143
WLogEntry(const WLogger & logger,const std::string & type,bool mute)144 WLogEntry::WLogEntry(const WLogger& logger, const std::string& type,
145 bool mute)
146 {
147 if (!mute)
148 impl_.reset(new Impl(logger, type));
149 }
150
WLogEntry(const WLogSink & customLogger,const std::string & type)151 WLogEntry::WLogEntry(const WLogSink& customLogger,
152 const std::string& type)
153 {
154 impl_.reset(new Impl(customLogger, type));
155 }
156
startField()157 void WLogEntry::startField()
158 {
159 if (impl_)
160 impl_->startField();
161 }
162
Impl(const WLogger & logger,const std::string & type)163 WLogEntry::Impl::Impl(const WLogger& logger, const std::string& type)
164 : logger_(&logger),
165 customLogger_(nullptr),
166 type_(type),
167 field_(0),
168 fieldStarted_(false)
169 { }
170
Impl(const WLogSink & customLogger,const std::string & type)171 WLogEntry::Impl::Impl(const WLogSink& customLogger,
172 const std::string& type)
173 : logger_(nullptr),
174 customLogger_(&customLogger),
175 type_(type),
176 field_(0),
177 fieldStarted_(false)
178 { }
179
startField()180 void WLogEntry::Impl::startField()
181 {
182 if (!fieldStarted_) {
183 if (quote())
184 line_ << '"';
185 fieldStarted_ = true;
186 }
187 }
188
finishField()189 void WLogEntry::Impl::finishField()
190 {
191 if (fieldStarted_) {
192 if (quote())
193 line_ << '"';
194 } else
195 line_ << '-';
196 }
197
nextField()198 void WLogEntry::Impl::nextField()
199 {
200 finishField();
201
202 line_ << ' ';
203 fieldStarted_ = false;
204 ++field_;
205 }
206
finish()207 void WLogEntry::Impl::finish()
208 {
209 if (!customLogger_) {
210 while (field_ < (int)logger_->fields().size() - 1)
211 nextField();
212 }
213
214 finishField();
215 }
216
quote()217 bool WLogEntry::Impl::quote() const
218 {
219 if (customLogger_)
220 return false;
221 else if (field_ < (int)logger_->fields().size())
222 return logger_->fields()[field_].isString();
223 else
224 return false;
225 }
226
227 const WLogger::Sep WLogger::sep = WLogger::Sep();
228 const WLogger::TimeStamp WLogger::timestamp = WLogger::TimeStamp();
229
Field(const std::string & name,bool isString)230 WLogger::Field::Field(const std::string& name, bool isString)
231 : name_(name),
232 string_(isString)
233 { }
234
WLogger()235 WLogger::WLogger()
236 : o_(&std::cerr),
237 ownStream_(false)
238 {
239 Rule r;
240 r.type = "*";
241 r.scope = "*";
242 r.include = true;
243 rules_.push_back(r);
244 r.type = "debug";
245 r.include = false;
246 rules_.push_back(r);
247 }
248
~WLogger()249 WLogger::~WLogger()
250 {
251 if (ownStream_)
252 delete o_;
253 }
254
setStream(std::ostream & o)255 void WLogger::setStream(std::ostream& o)
256 {
257 if (ownStream_)
258 delete o_;
259
260 o_ = &o;
261 ownStream_ = false;
262 }
263
setFile(const std::string & path)264 void WLogger::setFile(const std::string& path)
265 {
266 if (ownStream_) {
267 delete o_;
268 o_ = &std::cerr;
269 ownStream_ = false;
270 }
271
272 std::ofstream *ofs;
273 #ifdef _MSC_VER
274 FILE *file = _fsopen(path.c_str(), "at", _SH_DENYNO);
275 if (file) {
276 ofs = new std::ofstream(file);
277 } else {
278 ofs = new std::ofstream(path.c_str(), std::ios_base::out | std::ios_base::ate | std::ios_base::app);
279 }
280 #else
281 ofs = new std::ofstream(path.c_str(),
282 std::ios_base::out | std::ios_base::ate | std::ios_base::app);
283 if (!ofs->is_open()) {
284 // maybe a special file (pipe, /dev/null) that does not support ate?
285 delete ofs;
286 ofs = new std::ofstream(path.c_str(), std::ios_base::out);
287 }
288 #endif
289
290 if (ofs->is_open()) {
291 LOG_INFO("Opened log file (" << path << ").");
292 o_ = ofs;
293 ownStream_ = true;
294 } else {
295 delete ofs;
296
297 LOG_ERROR("Could not open log file (" << path << "). "
298 "We will be logging to std::cerr again.");
299 o_ = &std::cerr;
300 ownStream_ = false;
301 }
302 }
303
addField(const std::string & name,bool isString)304 void WLogger::addField(const std::string& name, bool isString)
305 {
306 fields_.push_back(Field(name, isString));
307 }
308
entry(const std::string & type)309 WLogEntry WLogger::entry(const std::string& type) const
310 {
311 return WLogEntry(*this, type, !logging(type));
312 }
313
addLine(const std::string & type,const std::string & scope,const WStringStream & s)314 void WLogger::addLine(const std::string& type,
315 const std::string& scope, const WStringStream& s) const
316 {
317 if (logging(type, scope))
318 if (o_)
319 *o_ << s.str() << std::endl;
320 }
321
configure(const std::string & config)322 void WLogger::configure(const std::string& config)
323 {
324 rules_.clear();
325
326 Wt::Utils::SplitVector rules;
327 boost::split(rules, config, boost::algorithm::is_space(),
328 boost::algorithm::token_compress_on);
329
330 for (unsigned i = 0; i < rules.size(); ++i) {
331 Wt::Utils::SplitVector type_scope;
332 boost::split(type_scope, rules[i], boost::is_any_of(":"));
333
334 Rule r;
335 r.type = std::string(type_scope[0].begin(), type_scope[0].end());
336
337 if (type_scope.size() == 1)
338 r.scope = "*";
339 else
340 r.scope = std::string(type_scope[1].begin(), type_scope[1].end());
341
342 r.include = true;
343
344 if (r.type[0] == '-') {
345 r.include = false;
346 r.type = r.type.substr(1);
347 } else if (r.type[0] == '+')
348 r.type = r.type.substr(1);
349
350 rules_.push_back(r);
351 }
352 }
353
logging(const std::string & type)354 bool WLogger::logging(const std::string& type) const
355 {
356 return logging(type.c_str());
357 }
358
logging(const char * type)359 bool WLogger::logging(const char *type) const
360 {
361 bool result = false;
362
363 for (unsigned i = 0; i < rules_.size(); ++i)
364 if (rules_[i].type == "*" || rules_[i].type == type) {
365 if (rules_[i].scope == "*")
366 result = rules_[i].include;
367 else if (rules_[i].include)
368 result = true;
369 }
370
371 return result;
372 }
373
logging(const std::string & type,const std::string & scope)374 bool WLogger::logging(const std::string& type, const std::string& scope) const
375 {
376 bool result = false;
377
378 for (unsigned i = 0; i < rules_.size(); ++i)
379 if (rules_[i].type == "*" || rules_[i].type == type)
380 if (rules_[i].scope == "*" || rules_[i].scope == scope)
381 result = rules_[i].include;
382
383 return result;
384 }
385
logInstance()386 WLogger& logInstance()
387 {
388 #ifdef WT_DBO_LOGGER
389 return defaultLogger;
390 #else // WT_DBO_LOGGER
391 WebSession *session = WebSession::instance();
392
393 if (session)
394 return session->logInstance();
395 else {
396 WServer *server = WServer::instance();
397
398 if (server)
399 return server->logger();
400 else
401 return defaultLogger;
402 }
403 #endif // WT_DBO_LOGGER
404 }
405
logging(const std::string & type,const std::string & scope)406 bool logging(const std::string &type,
407 const std::string &scope) noexcept
408 {
409 #ifdef WT_DBO_LOGGER
410 if (customLogger_)
411 return customLogger_->logging(type, scope);
412
413 return true;
414 #else // WT_DBO_LOGGER
415 WebSession *session = WebSession::instance();
416
417 Wt::WServer *server = session ? session->controller()->server() : WServer::instance();
418 if (server) {
419 if (server->customLogger())
420 return server->customLogger()->logging(type, scope);
421 else
422 return server->logger().logging(type, scope);
423 } else {
424 return defaultLogger.logging(type, scope);
425 }
426 #endif // WT_DBO_LOGGER
427 }
428
log(const std::string & type)429 WLogEntry log(const std::string& type)
430 {
431 #ifdef WT_DBO_LOGGER
432 if (customLogger_) {
433 return WLogEntry(*customLogger_, type);
434 }
435
436 return defaultLogger.entry(type);
437 #else // WT_DBO_LOGGER
438 WebSession *session = WebSession::instance();
439
440 if (session)
441 return session->log(type);
442 else {
443 WServer *server = WServer::instance();
444
445 if (server)
446 return server->log(type);
447 else
448 return defaultLogger.entry(type);
449 }
450 #endif // WT_DBO_LOGGER
451 }
452
453 #ifdef WT_DBO_LOGGER
454 } // namespace Dbo
455 #endif
456
457 }
458