You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

688 lines
21 KiB

5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
4 months ago
5 months ago
4 months ago
5 months ago
4 months ago
5 months ago
4 months ago
5 months ago
4 months ago
5 months ago
4 months ago
5 months ago
4 months ago
  1. //
  2. // Created by zhaohe on 19-5-31.
  3. //
  4. #include "logger_factory.hpp"
  5. #include <sys/stat.h>
  6. #include <sys/types.h>
  7. #include <fstream>
  8. #include <iostream>
  9. #include <mutex>
  10. #include <thread>
  11. #include "default_logger_config.hpp"
  12. #include "nlohmann/json.hpp"
  13. #include "spdlog/sinks/basic_file_sink.h"
  14. #include "spdlog/sinks/daily_file_sink.h"
  15. #include "spdlog/sinks/rotating_file_sink.h"
  16. #include "spdlog/sinks/stdout_sinks.h"
  17. // #include "zwtimecpp/core/utils/compliler.h"
  18. using namespace std;
  19. using namespace iflytop;
  20. using namespace core;
  21. using namespace nlohmann;
  22. using namespace spdlog;
  23. const static char* kRootLogerName = "root";
  24. // const static char* kSpdDefaultConfigPaths[] = {"spd_logger_cfg.json"};
  25. const static char* kDefaultPattern = "[%C-%m-%d %H:%M:%S.%e] [%-20n] [%^%L%$] %v";
  26. // const static string kDefaultPattern = "";
  27. // const string WEAK spdLoggerConfig() { return ""; }
  28. // const static string kDefaultLoggerBeforeConfig = "DefaultLoggerBeforeConfig";
  29. // {
  30. // "name": "LoggerName",
  31. // "level": 2,
  32. // "type":"daily_logger_mt",
  33. // "filename":"fileName",
  34. // "hour":0,
  35. // "minute":0,
  36. // "truncate":false
  37. // }
  38. // {
  39. // "name": "LoggerName",
  40. // "level": 2,
  41. // "type":"rotating_logger_mt",
  42. // "filename":"fileName",
  43. // "max_file_size":1000,
  44. // "max_files":100,
  45. // "rotate_on_open":true
  46. // }
  47. // Rotate files:
  48. // log.txt -> log.1.txt
  49. // log.1.txt -> log.2.txt
  50. // log.2.txt -> log.3.txt
  51. // log.3.txt -> delete
  52. // 1. 没设置type,统统被当做logger处理
  53. // 2. 所有的logger,sink loggerAndSink,name都不能重复
  54. // 3.
  55. /**
  56. * @brief
  57. *loggerSupportList:
  58. *
  59. *-->: daily_logger_mt
  60. *-->: basic_logger_mt
  61. *-->: logger
  62. *-->: daily_file_sink_mt
  63. *-->: stderr_color_sink_mt
  64. *-->: stdout_color_sink_mt
  65. *
  66. * rotating_logger_mt
  67. * rotating_file_sink_mt
  68. *
  69. */
  70. #define ASSININ_VALUE(value) \
  71. if (config_item.key() == #value) item.at(#value).get_to(config.value);
  72. #if 0
  73. static bool c_daily_logger_mt(json j) {
  74. try {
  75. string type = j.at("type").get<string>();
  76. if (type == "daily_logger_mt") {
  77. GET(string, name);
  78. GET(string, filename);
  79. mkdirIfNotExist(filename);
  80. TRY_GET(int, hour, 0);
  81. TRY_GET(int, minute, 0);
  82. TRY_GET(bool, truncate, false);
  83. auto var_logger =
  84. spdlog::daily_logger_mt(name, filename, hour, minute, truncate);
  85. logger_common_config(var_logger, j);
  86. insertLogger(var_logger);
  87. return true;
  88. } else {
  89. return false;
  90. }
  91. } catch (const std::exception& e) {
  92. spdlog::critical("c_daily_logger_mt fail {} reason {}", j.dump(1),
  93. e.what());
  94. exit(-1);
  95. }
  96. };
  97. #endif
  98. #if 0
  99. static bool c_daily_file_sink_mt(json j) {
  100. try {
  101. string type = j.at("type").get<string>();
  102. if (type == "daily_file_sink_mt") {
  103. GET(string, name);
  104. GET(string, filename);
  105. mkdirIfNotExist(filename);
  106. TRY_GET(int, hour, 0);
  107. TRY_GET(int, minute, 0);
  108. TRY_GET(bool, truncate, false);
  109. auto sink = make_shared<sinks::daily_file_sink_mt>(filename, hour, minute,
  110. truncate);
  111. sink_common_config(sink, j);
  112. insertSink(name, sink);
  113. return true;
  114. } else {
  115. return false;
  116. }
  117. } catch (const std::exception& e) {
  118. spdlog::critical("c_daily_file_sink_mt fail {} reason {}", j.dump(1),
  119. e.what());
  120. exit(-1);
  121. }
  122. };
  123. #endif
  124. static string default_config = R"(
  125. [
  126. {
  127. "name": "info-sink",
  128. "type": "daily_file_sink_mt",
  129. "filename": "logs/infolog.log",
  130. "max_files": 30,
  131. "rotate_on_open": true,
  132. "level" : 2
  133. },
  134. {
  135. "name": "debug-sink",
  136. "type": "daily_file_sink_mt",
  137. "filename": "logs/debuglog.log",
  138. "max_files": 10,
  139. "rotate_on_open": true,
  140. "level": 0
  141. },
  142. {
  143. "name": "terminal-sink",
  144. "type": "stdout_color_sink_mt"
  145. },
  146. {
  147. "name": "root",
  148. "type": "logger",
  149. "level": 2,
  150. "sinks": [
  151. "terminal-sink",
  152. "debug-sink",
  153. "info-sink"
  154. ]
  155. }
  156. ]
  157. )";
  158. #define LOGGER_ENABLE_BEGIN(_name) \
  159. static bool c_##_name(json j) { \
  160. logger_t var_logger; \
  161. try { \
  162. string type = j.at("type").get<string>(); \
  163. if (type == #_name) { \
  164. GET(string, name);
  165. #define LOGGER_ENABLE_END(name) \
  166. logger_common_config(var_logger, j); \
  167. insertLogger(var_logger); \
  168. return true; \
  169. } \
  170. else { \
  171. return false; \
  172. } \
  173. } \
  174. catch (const std::exception& e) { \
  175. spdlog::critical("c_{} fail {} reason {}", #name, j.dump(1), e.what()); \
  176. exit(-1); \
  177. } \
  178. } \
  179. ;
  180. #define SINK_DEFINE_BEGIN(var_name) \
  181. static bool c_##var_name(json j) { \
  182. sink_ptr sink; \
  183. try { \
  184. string type = j.at("type").get<string>(); \
  185. if (type == #var_name) { \
  186. GET(string, name);
  187. #define SINK_DEFINE_END(var_name) \
  188. sink_common_config(sink, j); \
  189. insertSink(name, sink); \
  190. return true; \
  191. } \
  192. else { \
  193. return false; \
  194. } \
  195. } \
  196. catch (const std::exception& e) { \
  197. spdlog::critical("c_" #var_name " fail {} reason {}", j.dump(1), e.what()); \
  198. exit(-1); \
  199. } \
  200. } \
  201. ;
  202. template <class type>
  203. type tryGet(json j, string value_name, type defaultValue) {
  204. try {
  205. if (j.find(value_name) == j.end()) {
  206. return defaultValue;
  207. }
  208. type value = j.at(value_name).get<type>();
  209. return value;
  210. } catch (const std::exception& e) {
  211. return defaultValue;
  212. }
  213. }
  214. static bool exist(const string& path) {
  215. struct stat statInfo;
  216. if (stat(path.c_str(), &statInfo) == 0) {
  217. return true;
  218. }
  219. return false;
  220. }
  221. static bool mkdirIfNotExist(const string& path) {
  222. string::size_type sepPos = path.find_last_of("/");
  223. if (sepPos == string::npos) {
  224. return false;
  225. }
  226. string dirPath = path.substr(0, sepPos);
  227. if (exist(dirPath)) {
  228. return true;
  229. }
  230. int ret = mkdir(dirPath.c_str(), S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
  231. return 0 == ret ? true : false;
  232. }
  233. class LoggerAndSinks {
  234. public:
  235. string loggerName;
  236. set<string> sinkNames;
  237. };
  238. /****************************************************************************************************/
  239. static map<string, sink_ptr> s_sinks = {};
  240. static map<string, shared_ptr<logger>> s_loggers = {};
  241. static set<shared_ptr<LoggerAndSinks>> s_loggerAndSinks = {};
  242. static void insertLogger(shared_ptr<logger> var_logger) {
  243. if (s_loggers.find(var_logger->name()) == s_loggers.end()) {
  244. s_loggers[var_logger->name()] = var_logger;
  245. } else {
  246. spdlog::critical("Add the logger {} fail", var_logger->name());
  247. exit(-1);
  248. }
  249. };
  250. static void insertSink(string name, sink_ptr sink) {
  251. if (s_sinks.find(name) == s_sinks.end()) {
  252. s_sinks[name] = sink;
  253. } else {
  254. spdlog::critical("Add the sink {} fail", name);
  255. exit(-1);
  256. }
  257. };
  258. static level::level_enum to_level(int value) {
  259. switch (value) {
  260. case SPDLOG_LEVEL_TRACE:
  261. return level::trace;
  262. case SPDLOG_LEVEL_DEBUG:
  263. return level::debug;
  264. case SPDLOG_LEVEL_INFO:
  265. return level::info;
  266. case SPDLOG_LEVEL_WARN:
  267. return level::warn;
  268. case SPDLOG_LEVEL_ERROR:
  269. return level::err;
  270. case SPDLOG_LEVEL_CRITICAL:
  271. return level::critical;
  272. default:
  273. spdlog::critical("level is out of range {} level must in [{},{}]", value, SPDLOG_LEVEL_TRACE, SPDLOG_LEVEL_CRITICAL);
  274. exit(-1);
  275. break;
  276. }
  277. return level::info;
  278. }
  279. // #define GET(type,value,error_msg,...)
  280. #define TRY_GET(type, value_name, default_value) type value_name = tryGet<type>(j, #value_name, default_value)
  281. #define GET(T, value_name) T value_name = j.at(#value_name).get<T>();
  282. static void logger_common_config(logger_t var_logger, json j) {
  283. TRY_GET(int, level, 2);
  284. TRY_GET(string, pattern, kDefaultPattern);
  285. TRY_GET(set<string>, sinks, {});
  286. var_logger->set_level(to_level(level));
  287. if (!sinks.empty()) {
  288. shared_ptr<LoggerAndSinks> las(new LoggerAndSinks());
  289. las->loggerName = var_logger->name();
  290. las->sinkNames = sinks;
  291. s_loggerAndSinks.insert(las);
  292. }
  293. }
  294. static void sink_common_config(sink_ptr sink, json j) {
  295. TRY_GET(int, level, 0);
  296. TRY_GET(string, pattern, kDefaultPattern);
  297. TRY_GET(set<string>, sinks, {});
  298. sink->set_level(to_level(level));
  299. if (!pattern.empty()) sink->set_pattern(pattern);
  300. }
  301. /**
  302. * logger-----------------------------------------------------------------------------------------------------
  303. */
  304. LOGGER_ENABLE_BEGIN(daily_logger_mt) {
  305. GET(string, filename);
  306. mkdirIfNotExist(filename);
  307. TRY_GET(int, hour, 0);
  308. TRY_GET(int, minute, 0);
  309. TRY_GET(bool, truncate, false);
  310. TRY_GET(int, max_files, 100);
  311. var_logger = spdlog::daily_logger_mt(name, filename, hour, minute, truncate, max_files);
  312. }
  313. LOGGER_ENABLE_END(daily_logger_mt)
  314. LOGGER_ENABLE_BEGIN(rotating_logger_mt) {
  315. GET(string, filename);
  316. mkdirIfNotExist(filename);
  317. TRY_GET(int, max_file_size, 1000);
  318. TRY_GET(int, max_files, 100);
  319. TRY_GET(bool, rotate_on_open, false);
  320. var_logger = spdlog::rotating_logger_mt(name, filename, max_file_size, max_files, rotate_on_open);
  321. }
  322. LOGGER_ENABLE_END(rotating_logger_mt)
  323. LOGGER_ENABLE_BEGIN(basic_logger_mt) {
  324. GET(string, filename);
  325. mkdirIfNotExist(filename);
  326. var_logger = spdlog::basic_logger_mt(name, filename);
  327. }
  328. LOGGER_ENABLE_END(basic_logger_mt)
  329. static bool c_logger(json j) {
  330. try {
  331. string type = j.at("type").get<string>();
  332. if (type == "logger") {
  333. GET(string, name);
  334. GET(set<string>, sinks);
  335. auto var_logger = make_shared<logger>(name, sinks_init_list{});
  336. if (sinks.empty()) {
  337. spdlog::critical("c_logger fail {} reason {}", j.dump(1), "Not set sink");
  338. exit(-1);
  339. }
  340. logger_common_config(var_logger, j);
  341. insertLogger(var_logger);
  342. return true;
  343. } else {
  344. return false;
  345. }
  346. } catch (const std::exception& e) {
  347. spdlog::critical("c_logger fail {} reason {}", j.dump(1), e.what());
  348. exit(-1);
  349. }
  350. };
  351. /**
  352. * sinks-----------------------------------------------------------------------------------------------------
  353. */
  354. SINK_DEFINE_BEGIN(daily_file_sink_mt) {
  355. GET(string, filename);
  356. mkdirIfNotExist(filename);
  357. TRY_GET(int, hour, 0);
  358. TRY_GET(int, minute, 0);
  359. TRY_GET(bool, truncate, false);
  360. TRY_GET(int, max_files, 100);
  361. // printf("filename:%s,max_files %d\n", filename.c_str(), max_files);
  362. sink = make_shared<sinks::daily_file_sink_mt>(filename, hour, minute, truncate, max_files);
  363. }
  364. SINK_DEFINE_END(daily_file_sink_mt)
  365. SINK_DEFINE_BEGIN(rotating_file_sink_mt) {
  366. GET(string, filename);
  367. mkdirIfNotExist(filename);
  368. TRY_GET(int, max_file_size, 1000);
  369. TRY_GET(int, max_files, 100);
  370. TRY_GET(bool, rotate_on_open, false);
  371. sink = make_shared<sinks::rotating_file_sink_mt>(filename, max_file_size, max_files, rotate_on_open);
  372. }
  373. SINK_DEFINE_END(rotating_file_sink_mt)
  374. SINK_DEFINE_BEGIN(stdout_color_sink_mt) { sink = make_shared<sinks::stdout_color_sink_mt>(); }
  375. SINK_DEFINE_END(stdout_color_sink_mt)
  376. SINK_DEFINE_BEGIN(stderr_color_sink_mt) { sink = make_shared<sinks::stderr_color_sink_mt>(); }
  377. SINK_DEFINE_END(stderr_color_sink_mt)
  378. /**
  379. * default_root_logger-----------------------------------------------------------------------------------------------------
  380. */
  381. static logger_t createRootLogger() {
  382. if (!get(kRootLogerName)) {
  383. auto rootLogger = spdlog::stdout_color_mt(kRootLogerName);
  384. if (!string(kDefaultPattern).empty()) {
  385. rootLogger->set_pattern(kDefaultPattern);
  386. }
  387. return rootLogger;
  388. }
  389. // auto stdoutsink = make_shared<sinks::stderr_color_sink_mt>();
  390. // stdoutsink->set_level(level::debug);
  391. // if (!kDefaultPattern.empty()) {
  392. // stdoutsink->set_pattern(kDefaultPattern);
  393. // }
  394. // auto rootLogger =
  395. // make_shared<logger>(kRootLogerName, sinks_init_list{stdoutsink});
  396. // rootLogger->set_level(level::info);
  397. // rootLogger->set_pattern(kDefaultPattern);
  398. return get(kRootLogerName);
  399. }
  400. /**
  401. * @brief
  402. */
  403. /**
  404. * @brief 使 daily_logger_mt时候会自动注册logger, 使make_shared<logger>
  405. * logger
  406. * @param var_logger
  407. */
  408. static void priRegLogger(logger_t var_logger) {
  409. if (var_logger->name() == kRootLogerName) {
  410. spdlog::set_default_logger(var_logger);
  411. }
  412. if (!get(var_logger->name())) {
  413. register_logger(var_logger);
  414. }
  415. }
  416. static void __parseSphLogConfig(json var) {
  417. if (c_daily_logger_mt(var)) {
  418. } else if (c_rotating_logger_mt(var)) {
  419. } else if (c_basic_logger_mt(var)) {
  420. } else if (c_daily_file_sink_mt(var)) {
  421. } else if (c_logger(var)) {
  422. } else if (c_stderr_color_sink_mt(var)) {
  423. } else if (c_stdout_color_sink_mt(var)) {
  424. } else if (c_rotating_file_sink_mt(var)) {
  425. } else {
  426. spdlog::critical("no such type {}", var.dump());
  427. exit(-1);
  428. }
  429. }
  430. static logger_t createLoggerWithoutType(json j) {
  431. TRY_GET(int, level, 2);
  432. GET(string, name);
  433. TRY_GET(string, pattern, kDefaultPattern);
  434. auto rootLogger = get(kRootLogerName);
  435. if (!rootLogger) {
  436. spdlog::critical("func: createLoggerWithoutType,can't find rootLogger");
  437. exit(-1);
  438. }
  439. auto newLogger = rootLogger->clone(name);
  440. newLogger->set_level(to_level(level));
  441. if (!pattern.empty()) newLogger->set_pattern(pattern);
  442. return newLogger;
  443. }
  444. /**
  445. * @brief
  446. *
  447. * logger思路
  448. * 1. type了的logger和sink
  449. * 2. logger和对应的sink
  450. * 3. rootLogger,rootLogger
  451. * 4. type的logger,rootLogger,level
  452. * @param path
  453. */
  454. void core::SpdLoggerFactory::parseSphLogConfig(string path) {
  455. try {
  456. // 这里必须清空,因为这个方法可能在main函数之前启动,所以数量可能未初始化
  457. s_sinks.clear();
  458. s_loggers.clear();
  459. s_loggerAndSinks.clear();
  460. fstream infile(path, ios::binary | ios::in);
  461. stringstream sstream;
  462. sstream << infile.rdbuf();
  463. infile.close();
  464. string jsonStr(sstream.str());
  465. sstream.clear();
  466. json configjson = json::parse(jsonStr);
  467. for (auto& j : configjson) {
  468. TRY_GET(string, type, "");
  469. if (!type.empty()) {
  470. __parseSphLogConfig(j);
  471. }
  472. }
  473. // 组装logger and sink
  474. for (auto& las : s_loggerAndSinks) {
  475. logger_t logger = s_loggers[las->loggerName];
  476. if (logger == nullptr) {
  477. spdlog::critical("can't find logger", las->loggerName);
  478. exit(-1);
  479. }
  480. set<sink_ptr> sinks;
  481. for (auto& sinkname : las->sinkNames) {
  482. auto result = s_sinks.find(sinkname);
  483. if (result == s_sinks.end()) {
  484. spdlog::critical("can't find sink {} ??", sinkname);
  485. exit(-1);
  486. }
  487. sinks.insert(result->second);
  488. }
  489. for (auto& sink : sinks) logger->sinks().push_back(sink);
  490. }
  491. for (auto& var : s_loggers) priRegLogger(var.second);
  492. // 如果没有rootLogger,构造rootLogger
  493. if (!get(kRootLogerName)) {
  494. for (auto& j : configjson) {
  495. GET(string, name);
  496. TRY_GET(string, type, "");
  497. if (name == kRootLogerName) {
  498. if (type.empty()) {
  499. TRY_GET(int, level, 2);
  500. TRY_GET(string, pattern, kDefaultPattern);
  501. auto rootLogger = createRootLogger();
  502. rootLogger->set_level(to_level(level));
  503. if (!pattern.empty()) rootLogger->set_pattern(pattern);
  504. priRegLogger(rootLogger);
  505. } else {
  506. spdlog::critical("shouldn't go here");
  507. exit(-1);
  508. }
  509. break;
  510. }
  511. }
  512. }
  513. // 如果依然没有构造rootLogger则构造默认logger
  514. if (!get(kRootLogerName)) priRegLogger(createRootLogger());
  515. // 构造没有type的logger
  516. for (auto& j : configjson) {
  517. TRY_GET(string, type, "");
  518. GET(string, name);
  519. if (type.empty() && name != kRootLogerName) {
  520. auto newlogger = createLoggerWithoutType(j);
  521. priRegLogger(newlogger);
  522. }
  523. }
  524. // spdlog::info("Logger initialize ok");
  525. } catch (const exception& e) {
  526. spdlog::critical("parse logger config fail {}", e.what());
  527. exit(-1);
  528. }
  529. }
  530. static string getConfigFilePath() {
  531. if (spdLoggerConfig) {
  532. if (exist(spdLoggerConfig())) {
  533. return spdLoggerConfig();
  534. } else {
  535. spdlog::warn("can't find spdLoggerConfig file {},use deafult config", spdLoggerConfig());
  536. }
  537. }
  538. return "spd_logger_cfg.json";
  539. }
  540. class MonitoringSpdLoggerConfigTask {
  541. unique_ptr<thread> wthread;
  542. public:
  543. MonitoringSpdLoggerConfigTask() {
  544. wthread.reset(new thread([]() {
  545. }));
  546. }
  547. ~MonitoringSpdLoggerConfigTask() { wthread->join(); }
  548. };
  549. shared_ptr<logger> SpdLoggerFactory::createLogger(string loggerName) {
  550. /**
  551. * @brief
  552. * main函数之前就创建了logger,
  553. */
  554. if (default_config.empty()) {
  555. spdlog::critical("you may construct a logger {} before main!!", loggerName);
  556. exit(-1);
  557. }
  558. lock_guard<mutex> lock_gu(createLogger_lock);
  559. if (!loggerName.empty()) {
  560. if (s_loggerNames.size() == 0) {
  561. s_loggerNames.insert(loggerName);
  562. } else {
  563. if (s_loggerNames.find(loggerName) == s_loggerNames.end()) {
  564. s_loggerNames.insert(loggerName);
  565. }
  566. }
  567. }
  568. // TODO:当使用gtest进行单元测试的时候,logger似乎会被清空,原因未知
  569. if (!get(kRootLogerName)) {
  570. initializeLogger = false;
  571. if (!string(kDefaultPattern).empty()) {
  572. spdlog::set_pattern(kDefaultPattern);
  573. }
  574. }
  575. if (!initializeLogger) {
  576. string configFilePath = getConfigFilePath();
  577. if (!configFilePath.empty() && exist(configFilePath)) {
  578. parseSphLogConfig(configFilePath);
  579. } else {
  580. spdlog::warn("can't find logger config file use default config {}", configFilePath);
  581. // 写字符串default_config到文件中configFilePath
  582. ofstream outfile(configFilePath);
  583. outfile << default_config;
  584. outfile.close();
  585. parseSphLogConfig(configFilePath);
  586. }
  587. initializeLogger = true;
  588. }
  589. logger_t ret_logger = get(loggerName);
  590. if (ret_logger) {
  591. return ret_logger;
  592. } else {
  593. logger_t rootLogger = get(kRootLogerName);
  594. if (!rootLogger) {
  595. spdlog::critical("can't find root logger ?????");
  596. exit(-1);
  597. }
  598. logger_t newLogger = rootLogger->clone(loggerName);
  599. priRegLogger(newLogger);
  600. return newLogger;
  601. }
  602. return nullptr;
  603. }
  604. set<string> SpdLoggerFactory::loggerNames() { return s_loggerNames; }
  605. sink_ptr SpdLoggerFactory::getSink(string name) {
  606. auto result = s_sinks.find(name);
  607. if (result == s_sinks.end()) {
  608. return nullptr;
  609. }
  610. return result->second;
  611. }
  612. shared_ptr<logger> SpdLoggerFactory::createRotatingFileLogger(const std::string& logger_name, size_t max_file_size, size_t max_files, bool bindTerminal,
  613. bool bindDebug, bool bindInfo) {
  614. auto newlogger = spdlog::rotating_logger_mt(logger_name, fmt::format("logs/{}.log", logger_name), 5 * 1024 * 1024 /*5M*/, 3 /*times*/);
  615. newlogger->set_level(spdlog::level::info);
  616. if (bindTerminal) newlogger->sinks().push_back(GET_SINK("terminal-sink"));
  617. if (bindDebug) newlogger->sinks().push_back(GET_SINK("debug-sink"));
  618. if (bindInfo) newlogger->sinks().push_back(GET_SINK("info-sink"));
  619. priRegLogger(newlogger);
  620. return newlogger;
  621. }