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 | |
24 | namespace Poco { |
25 | |
26 | |
27 | Logger::LoggerMap* Logger::_pLoggerMap = 0; |
28 | Mutex Logger::_mapMtx; |
29 | const std::string Logger::ROOT; |
30 | |
31 | |
32 | Logger::Logger(const std::string& rName, Channel* pChannel, int level): _name(rName), _pChannel(pChannel), _level(level) |
33 | { |
34 | if (pChannel) pChannel->duplicate(); |
35 | } |
36 | |
37 | |
38 | Logger::~Logger() |
39 | { |
40 | if (_pChannel) _pChannel->release(); |
41 | } |
42 | |
43 | |
44 | void Logger::setChannel(Channel* pChannel) |
45 | { |
46 | if (_pChannel) _pChannel->release(); |
47 | _pChannel = pChannel; |
48 | if (_pChannel) _pChannel->duplicate(); |
49 | } |
50 | |
51 | |
52 | Channel* Logger::getChannel() const |
53 | { |
54 | return _pChannel; |
55 | } |
56 | |
57 | |
58 | void Logger::setLevel(int level) |
59 | { |
60 | _level = level; |
61 | } |
62 | |
63 | |
64 | void Logger::setLevel(const std::string& level) |
65 | { |
66 | setLevel(parseLevel(level)); |
67 | } |
68 | |
69 | |
70 | void 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 | |
81 | void Logger::log(const Message& msg) |
82 | { |
83 | if (_level >= msg.getPriority() && _pChannel) |
84 | { |
85 | _pChannel->log(msg); |
86 | } |
87 | } |
88 | |
89 | |
90 | void Logger::log(const Exception& exc) |
91 | { |
92 | error(exc.displayText()); |
93 | } |
94 | |
95 | |
96 | void Logger::log(const Exception& exc, const char* file, int line) |
97 | { |
98 | error(exc.displayText(), file, line); |
99 | } |
100 | |
101 | |
102 | void 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 | |
113 | void 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 | |
132 | void 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 | |
151 | void 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 | |
170 | std::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 | |
180 | std::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 | |
191 | std::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 | |
203 | std::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 | |
216 | std::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 | |
248 | void 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 | |
283 | Logger& Logger::get(const std::string& name) |
284 | { |
285 | Mutex::ScopedLock lock(_mapMtx); |
286 | |
287 | return unsafeGet(name); |
288 | } |
289 | |
290 | |
291 | Logger& 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 | |
311 | Logger& 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 | |
322 | Logger& Logger::root() |
323 | { |
324 | Mutex::ScopedLock lock(_mapMtx); |
325 | |
326 | return unsafeGet(ROOT); |
327 | } |
328 | |
329 | |
330 | Logger* Logger::has(const std::string& name) |
331 | { |
332 | Mutex::ScopedLock lock(_mapMtx); |
333 | |
334 | return find(name); |
335 | } |
336 | |
337 | |
338 | void 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 | |
354 | Logger* 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 | |
366 | void 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 | |
382 | void 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 | |
397 | Logger& 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 | |
413 | int 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 | |
449 | class AutoLoggerShutdown |
450 | { |
451 | public: |
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 | |
469 | namespace |
470 | { |
471 | static AutoLoggerShutdown als; |
472 | } |
473 | |
474 | |
475 | void 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 | |