1#include "Loggers.h"
2
3#include <iostream>
4#include <Poco/SyslogChannel.h>
5#include <Poco/Util/AbstractConfiguration.h>
6#include "OwnFormattingChannel.h"
7#include "OwnPatternFormatter.h"
8#include <Poco/ConsoleChannel.h>
9#include <Poco/File.h>
10#include <Poco/Logger.h>
11#include <Poco/Net/RemoteSyslogChannel.h>
12#include <Poco/Path.h>
13
14namespace DB
15{
16 class SensitiveDataMasker;
17}
18
19
20// TODO: move to libcommon
21static std::string createDirectory(const std::string & file)
22{
23 auto path = Poco::Path(file).makeParent();
24 if (path.toString().empty())
25 return "";
26 Poco::File(path).createDirectories();
27 return path.toString();
28};
29
30void Loggers::setTextLog(std::shared_ptr<DB::TextLog> log)
31{
32 text_log = log;
33}
34
35void Loggers::buildLoggers(Poco::Util::AbstractConfiguration & config, Poco::Logger & logger /*_root*/, const std::string & cmd_name)
36{
37 if (split)
38 if (auto log = text_log.lock())
39 split->addTextLog(log);
40
41 auto current_logger = config.getString("logger", "");
42 if (config_logger == current_logger)
43 return;
44
45 config_logger = current_logger;
46
47 bool is_daemon = config.getBool("application.runAsDaemon", false);
48
49 /// Split logs to ordinary log, error log, syslog and console.
50 /// Use extended interface of Channel for more comprehensive logging.
51 split = new DB::OwnSplitChannel();
52
53 auto log_level = config.getString("logger.level", "trace");
54 const auto log_path = config.getString("logger.log", "");
55 if (!log_path.empty())
56 {
57 createDirectory(log_path);
58 std::cerr << "Logging " << log_level << " to " << log_path << std::endl;
59
60 // Set up two channel chains.
61 log_file = new Poco::FileChannel;
62 log_file->setProperty(Poco::FileChannel::PROP_PATH, Poco::Path(log_path).absolute().toString());
63 log_file->setProperty(Poco::FileChannel::PROP_ROTATION, config.getRawString("logger.size", "100M"));
64 log_file->setProperty(Poco::FileChannel::PROP_ARCHIVE, "number");
65 log_file->setProperty(Poco::FileChannel::PROP_COMPRESS, config.getRawString("logger.compress", "true"));
66 log_file->setProperty(Poco::FileChannel::PROP_PURGECOUNT, config.getRawString("logger.count", "1"));
67 log_file->setProperty(Poco::FileChannel::PROP_FLUSH, config.getRawString("logger.flush", "true"));
68 log_file->setProperty(Poco::FileChannel::PROP_ROTATEONOPEN, config.getRawString("logger.rotateOnOpen", "false"));
69 log_file->open();
70
71 Poco::AutoPtr<OwnPatternFormatter> pf = new OwnPatternFormatter(this);
72
73 Poco::AutoPtr<DB::OwnFormattingChannel> log = new DB::OwnFormattingChannel(pf, log_file);
74 split->addChannel(log);
75 }
76
77 const auto errorlog_path = config.getString("logger.errorlog", "");
78 if (!errorlog_path.empty())
79 {
80 createDirectory(errorlog_path);
81 std::cerr << "Logging errors to " << errorlog_path << std::endl;
82
83 error_log_file = new Poco::FileChannel;
84 error_log_file->setProperty(Poco::FileChannel::PROP_PATH, Poco::Path(errorlog_path).absolute().toString());
85 error_log_file->setProperty(Poco::FileChannel::PROP_ROTATION, config.getRawString("logger.size", "100M"));
86 error_log_file->setProperty(Poco::FileChannel::PROP_ARCHIVE, "number");
87 error_log_file->setProperty(Poco::FileChannel::PROP_COMPRESS, config.getRawString("logger.compress", "true"));
88 error_log_file->setProperty(Poco::FileChannel::PROP_PURGECOUNT, config.getRawString("logger.count", "1"));
89 error_log_file->setProperty(Poco::FileChannel::PROP_FLUSH, config.getRawString("logger.flush", "true"));
90 error_log_file->setProperty(Poco::FileChannel::PROP_ROTATEONOPEN, config.getRawString("logger.rotateOnOpen", "false"));
91
92 Poco::AutoPtr<OwnPatternFormatter> pf = new OwnPatternFormatter(this);
93
94 Poco::AutoPtr<DB::OwnFormattingChannel> errorlog = new DB::OwnFormattingChannel(pf, error_log_file);
95 errorlog->setLevel(Poco::Message::PRIO_NOTICE);
96 errorlog->open();
97 split->addChannel(errorlog);
98 }
99
100 /// "dynamic_layer_selection" is needed only for Yandex.Metrika, that share part of ClickHouse code.
101 /// We don't need this configuration parameter.
102
103 if (config.getBool("logger.use_syslog", false) || config.getBool("dynamic_layer_selection", false))
104 {
105 //const std::string & cmd_name = commandName();
106
107 if (config.has("logger.syslog.address"))
108 {
109 syslog_channel = new Poco::Net::RemoteSyslogChannel();
110 // syslog address
111 syslog_channel->setProperty(Poco::Net::RemoteSyslogChannel::PROP_LOGHOST, config.getString("logger.syslog.address"));
112 if (config.has("logger.syslog.hostname"))
113 {
114 syslog_channel->setProperty(Poco::Net::RemoteSyslogChannel::PROP_HOST, config.getString("logger.syslog.hostname"));
115 }
116 syslog_channel->setProperty(Poco::Net::RemoteSyslogChannel::PROP_FORMAT, config.getString("logger.syslog.format", "syslog"));
117 syslog_channel->setProperty(
118 Poco::Net::RemoteSyslogChannel::PROP_FACILITY, config.getString("logger.syslog.facility", "LOG_USER"));
119 }
120 else
121 {
122 syslog_channel = new Poco::SyslogChannel();
123 syslog_channel->setProperty(Poco::SyslogChannel::PROP_NAME, cmd_name);
124 syslog_channel->setProperty(Poco::SyslogChannel::PROP_OPTIONS, config.getString("logger.syslog.options", "LOG_CONS|LOG_PID"));
125 syslog_channel->setProperty(Poco::SyslogChannel::PROP_FACILITY, config.getString("logger.syslog.facility", "LOG_DAEMON"));
126 }
127 syslog_channel->open();
128
129 Poco::AutoPtr<OwnPatternFormatter> pf = new OwnPatternFormatter(this, OwnPatternFormatter::ADD_LAYER_TAG);
130
131 Poco::AutoPtr<DB::OwnFormattingChannel> log = new DB::OwnFormattingChannel(pf, syslog_channel);
132 split->addChannel(log);
133 }
134
135 if (config.getBool("logger.console", false)
136 || (!config.hasProperty("logger.console") && !is_daemon && (isatty(STDIN_FILENO) || isatty(STDERR_FILENO))))
137 {
138 Poco::AutoPtr<DB::OwnFormattingChannel> log = new DB::OwnFormattingChannel(new OwnPatternFormatter(this), new Poco::ConsoleChannel);
139 logger.warning("Logging " + log_level + " to console");
140 split->addChannel(log);
141 }
142
143 split->open();
144 logger.close();
145 logger.setChannel(split);
146
147 // Global logging level (it can be overridden for specific loggers).
148 logger.setLevel(log_level);
149
150 // Set level to all already created loggers
151 std::vector<std::string> names;
152 //logger_root = Logger::root();
153 logger.root().names(names);
154 for (const auto & name : names)
155 logger.root().get(name).setLevel(log_level);
156
157 // Attach to the root logger.
158 logger.root().setLevel(log_level);
159 logger.root().setChannel(logger.getChannel());
160
161 // Explicitly specified log levels for specific loggers.
162 Poco::Util::AbstractConfiguration::Keys levels;
163 config.keys("logger.levels", levels);
164
165 if (!levels.empty())
166 for (const auto & level : levels)
167 logger.root().get(level).setLevel(config.getString("logger.levels." + level, "trace"));
168}
169
170void Loggers::closeLogs(Poco::Logger & logger)
171{
172 if (log_file)
173 log_file->close();
174 if (error_log_file)
175 error_log_file->close();
176 // Shouldn't syslog_channel be closed here too?
177
178 if (!log_file)
179 logger.warning("Logging to console but received signal to close log file (ignoring).");
180}
181