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