1//
2// Logger.cpp
3//
4// Library: Foundation
5// Package: Logging
6// Module: Logger
7//
8// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/Logger.h"
16#include "Poco/Formatter.h"
17#include "Poco/LoggingRegistry.h"
18#include "Poco/Exception.h"
19#include "Poco/NumberFormatter.h"
20#include "Poco/NumberParser.h"
21#include "Poco/String.h"
22
23
24namespace Poco {
25
26
27Logger::LoggerMap* Logger::_pLoggerMap = 0;
28Mutex Logger::_mapMtx;
29const std::string Logger::ROOT;
30
31
32Logger::Logger(const std::string& rName, Channel* pChannel, int level): _name(rName), _pChannel(pChannel), _level(level)
33{
34 if (pChannel) pChannel->duplicate();
35}
36
37
38Logger::~Logger()
39{
40 if (_pChannel) _pChannel->release();
41}
42
43
44void Logger::setChannel(Channel* pChannel)
45{
46 if (_pChannel) _pChannel->release();
47 _pChannel = pChannel;
48 if (_pChannel) _pChannel->duplicate();
49}
50
51
52Channel* Logger::getChannel() const
53{
54 return _pChannel;
55}
56
57
58void Logger::setLevel(int level)
59{
60 _level = level;
61}
62
63
64void Logger::setLevel(const std::string& level)
65{
66 setLevel(parseLevel(level));
67}
68
69
70void Logger::setProperty(const std::string& rName, const std::string& rValue)
71{
72 if (rName == "channel")
73 setChannel(LoggingRegistry::defaultRegistry().channelForName(rValue));
74 else if (rName == "level")
75 setLevel(rValue);
76 else
77 Channel::setProperty(rName, rValue);
78}
79
80
81void Logger::log(const Message& msg)
82{
83 if (_level >= msg.getPriority() && _pChannel)
84 {
85 _pChannel->log(msg);
86 }
87}
88
89
90void Logger::log(const Exception& exc)
91{
92 error(exc.displayText());
93}
94
95
96void Logger::log(const Exception& exc, const char* file, int line)
97{
98 error(exc.displayText(), file, line);
99}
100
101
102void Logger::dump(const std::string& msg, const void* buffer, std::size_t length, Message::Priority prio)
103{
104 if (_level >= prio && _pChannel)
105 {
106 std::string text(msg);
107 formatDump(text, buffer, length);
108 _pChannel->log(Message(_name, text, prio));
109 }
110}
111
112
113void Logger::setLevel(const std::string& name, int level)
114{
115 Mutex::ScopedLock lock(_mapMtx);
116
117 if (_pLoggerMap)
118 {
119 std::string::size_type len = name.length();
120 for (LoggerMap::iterator it = _pLoggerMap->begin(); it != _pLoggerMap->end(); ++it)
121 {
122 if (len == 0 ||
123 (it->first.compare(0, len, name) == 0 && (it->first.length() == len || it->first[len] == '.')))
124 {
125 it->second->setLevel(level);
126 }
127 }
128 }
129}
130
131
132void Logger::setChannel(const std::string& name, Channel* pChannel)
133{
134 Mutex::ScopedLock lock(_mapMtx);
135
136 if (_pLoggerMap)
137 {
138 std::string::size_type len = name.length();
139 for (LoggerMap::iterator it = _pLoggerMap->begin(); it != _pLoggerMap->end(); ++it)
140 {
141 if (len == 0 ||
142 (it->first.compare(0, len, name) == 0 && (it->first.length() == len || it->first[len] == '.')))
143 {
144 it->second->setChannel(pChannel);
145 }
146 }
147 }
148}
149
150
151void Logger::setProperty(const std::string& loggerName, const std::string& propertyName, const std::string& value)
152{
153 Mutex::ScopedLock lock(_mapMtx);
154
155 if (_pLoggerMap)
156 {
157 std::string::size_type len = loggerName.length();
158 for (LoggerMap::iterator it = _pLoggerMap->begin(); it != _pLoggerMap->end(); ++it)
159 {
160 if (len == 0 ||
161 (it->first.compare(0, len, loggerName) == 0 && (it->first.length() == len || it->first[len] == '.')))
162 {
163 it->second->setProperty(propertyName, value);
164 }
165 }
166 }
167}
168
169
170std::string Logger::format(const std::string& fmt, const std::string& arg)
171{
172 std::string args[] =
173 {
174 arg
175 };
176 return format(fmt, 1, args);
177}
178
179
180std::string Logger::format(const std::string& fmt, const std::string& arg0, const std::string& arg1)
181{
182 std::string args[] =
183 {
184 arg0,
185 arg1
186 };
187 return format(fmt, 2, args);
188}
189
190
191std::string Logger::format(const std::string& fmt, const std::string& arg0, const std::string& arg1, const std::string& arg2)
192{
193 std::string args[] =
194 {
195 arg0,
196 arg1,
197 arg2
198 };
199 return format(fmt, 3, args);
200}
201
202
203std::string Logger::format(const std::string& fmt, const std::string& arg0, const std::string& arg1, const std::string& arg2, const std::string& arg3)
204{
205 std::string args[] =
206 {
207 arg0,
208 arg1,
209 arg2,
210 arg3
211 };
212 return format(fmt, 4, args);
213}
214
215
216std::string Logger::format(const std::string& fmt, int argc, std::string argv[])
217{
218 std::string result;
219 std::string::const_iterator it = fmt.begin();
220 while (it != fmt.end())
221 {
222 if (*it == '$')
223 {
224 ++it;
225 if (*it == '$')
226 {
227 result += '$';
228 }
229 else if (*it >= '0' && *it <= '9')
230 {
231 int i = *it - '0';
232 if (i < argc)
233 result += argv[i];
234 }
235 else
236 {
237 result += '$';
238 result += *it;
239 }
240 }
241 else result += *it;
242 ++it;
243 }
244 return result;
245}
246
247
248void Logger::formatDump(std::string& message, const void* buffer, std::size_t length)
249{
250 const int BYTES_PER_LINE = 16;
251
252 message.reserve(message.size() + length*6);
253 if (!message.empty()) message.append("\n");
254 unsigned char* base = (unsigned char*) buffer;
255 int addr = 0;
256 while (addr < length)
257 {
258 if (addr > 0) message.append("\n");
259 message.append(NumberFormatter::formatHex(addr, 4));
260 message.append(" ");
261 int offset = 0;
262 while (addr + offset < length && offset < BYTES_PER_LINE)
263 {
264 message.append(NumberFormatter::formatHex(base[addr + offset], 2));
265 message.append(offset == 7 ? " " : " ");
266 ++offset;
267 }
268 if (offset < 7) message.append(" ");
269 while (offset < BYTES_PER_LINE) { message.append(" "); ++offset; }
270 message.append(" ");
271 offset = 0;
272 while (addr + offset < length && offset < BYTES_PER_LINE)
273 {
274 unsigned char c = base[addr + offset];
275 message += (c >= 32 && c < 127) ? (char) c : '.';
276 ++offset;
277 }
278 addr += BYTES_PER_LINE;
279 }
280}
281
282
283Logger& Logger::get(const std::string& name)
284{
285 Mutex::ScopedLock lock(_mapMtx);
286
287 return unsafeGet(name);
288}
289
290
291Logger& Logger::unsafeGet(const std::string& name)
292{
293 Logger* pLogger = find(name);
294 if (!pLogger)
295 {
296 if (name == ROOT)
297 {
298 pLogger = new Logger(name, 0, Message::PRIO_INFORMATION);
299 }
300 else
301 {
302 Logger& par = parent(name);
303 pLogger = new Logger(name, par.getChannel(), par.getLevel());
304 }
305 add(pLogger);
306 }
307 return *pLogger;
308}
309
310
311Logger& Logger::create(const std::string& name, Channel* pChannel, int level)
312{
313 Mutex::ScopedLock lock(_mapMtx);
314
315 if (find(name)) throw ExistsException();
316 Logger* pLogger = new Logger(name, pChannel, level);
317 add(pLogger);
318 return *pLogger;
319}
320
321
322Logger& Logger::root()
323{
324 Mutex::ScopedLock lock(_mapMtx);
325
326 return unsafeGet(ROOT);
327}
328
329
330Logger* Logger::has(const std::string& name)
331{
332 Mutex::ScopedLock lock(_mapMtx);
333
334 return find(name);
335}
336
337
338void Logger::shutdown()
339{
340 Mutex::ScopedLock lock(_mapMtx);
341
342 if (_pLoggerMap)
343 {
344 for (LoggerMap::iterator it = _pLoggerMap->begin(); it != _pLoggerMap->end(); ++it)
345 {
346 it->second->release();
347 }
348 delete _pLoggerMap;
349 _pLoggerMap = 0;
350 }
351}
352
353
354Logger* Logger::find(const std::string& name)
355{
356 if (_pLoggerMap)
357 {
358 LoggerMap::iterator it = _pLoggerMap->find(name);
359 if (it != _pLoggerMap->end())
360 return it->second;
361 }
362 return 0;
363}
364
365
366void Logger::destroy(const std::string& name)
367{
368 Mutex::ScopedLock lock(_mapMtx);
369
370 if (_pLoggerMap)
371 {
372 LoggerMap::iterator it = _pLoggerMap->find(name);
373 if (it != _pLoggerMap->end())
374 {
375 it->second->release();
376 _pLoggerMap->erase(it);
377 }
378 }
379}
380
381
382void Logger::names(std::vector<std::string>& names)
383{
384 Mutex::ScopedLock lock(_mapMtx);
385
386 names.clear();
387 if (_pLoggerMap)
388 {
389 for (LoggerMap::const_iterator it = _pLoggerMap->begin(); it != _pLoggerMap->end(); ++it)
390 {
391 names.push_back(it->first);
392 }
393 }
394}
395
396
397Logger& Logger::parent(const std::string& name)
398{
399 std::string::size_type pos = name.rfind('.');
400 if (pos != std::string::npos)
401 {
402 std::string pname = name.substr(0, pos);
403 Logger* pParent = find(pname);
404 if (pParent)
405 return *pParent;
406 else
407 return parent(pname);
408 }
409 else return unsafeGet(ROOT);
410}
411
412
413int Logger::parseLevel(const std::string& level)
414{
415 if (icompare(level, "none") == 0)
416 return 0;
417 else if (icompare(level, "fatal") == 0)
418 return Message::PRIO_FATAL;
419 else if (icompare(level, "critical") == 0)
420 return Message::PRIO_CRITICAL;
421 else if (icompare(level, "error") == 0)
422 return Message::PRIO_ERROR;
423 else if (icompare(level, "warning") == 0)
424 return Message::PRIO_WARNING;
425 else if (icompare(level, "notice") == 0)
426 return Message::PRIO_NOTICE;
427 else if (icompare(level, "information") == 0)
428 return Message::PRIO_INFORMATION;
429 else if (icompare(level, "debug") == 0)
430 return Message::PRIO_DEBUG;
431 else if (icompare(level, "trace") == 0)
432 return Message::PRIO_TRACE;
433 else
434 {
435 int numLevel;
436 if (Poco::NumberParser::tryParse(level, numLevel))
437 {
438 if (numLevel > 0 && numLevel < 9)
439 return numLevel;
440 else
441 throw InvalidArgumentException("Log level out of range ", level);
442 }
443 else
444 throw InvalidArgumentException("Not a valid log level", level);
445 }
446}
447
448
449class AutoLoggerShutdown
450{
451public:
452 AutoLoggerShutdown()
453 {
454 }
455 ~AutoLoggerShutdown()
456 {
457 try
458 {
459 Logger::shutdown();
460 }
461 catch (...)
462 {
463 poco_unexpected();
464 }
465 }
466};
467
468
469namespace
470{
471 static AutoLoggerShutdown als;
472}
473
474
475void Logger::add(Logger* pLogger)
476{
477 if (!_pLoggerMap)
478 _pLoggerMap = new LoggerMap;
479 _pLoggerMap->insert(LoggerMap::value_type(pLogger->name(), pLogger));
480}
481
482
483} // namespace Poco
484