2 * Copyright (c) 2014-present, Facebook, Inc.
5 * This source code is licensed in accordance with the terms specified in
6 * the LICENSE file found in the root directory of this source tree.
18 #include <boost/noncopyable.hpp>
20 #include <osquery/data_logger.h>
21 #include <osquery/plugins/logger.h>
22 #include <osquery/registry_factory.h>
24 #include <osquery/utils/conversions/split.h>
25 #include <osquery/utils/json/json.h>
26 #include <osquery/utils/system/time.h>
28 namespace rj = rapidjson;
32 CREATE_REGISTRY(LoggerPlugin, "logger");
35 * @brief A custom Glog log sink for forwarding or buffering status logs.
37 * This log sink has two modes, it can buffer Glog status logs until an osquery
38 * logger is initialized or forward Glog status logs to an initialized and
39 * appropriate logger. The appropriateness is determined by the logger when its
40 * LoggerPlugin::init method is called. If the `init` method returns success
41 * then a BufferedLogSink will start forwarding status logs to
42 * LoggerPlugin::logStatus.
44 * This facility will start buffering when first used and stop buffering
45 * (aka remove itself as a Glog sink) using the exposed APIs. It will live
46 * throughout the life of the process for two reasons: (1) It makes sense when
47 * the active logger plugin is handling Glog status logs and (2) it must remove
48 * itself as a Glog target.
50 class BufferedLogSink : public google::LogSink, private boost::noncopyable {
52 /// We create this as a Singleton for proper disable/shutdown.
53 static BufferedLogSink& get();
55 /// The Glog-API LogSink call-in method.
56 void send(google::LogSeverity severity,
57 const char* full_filename,
58 const char* base_filename,
60 const struct ::tm* tm_time,
62 size_t message_len) override;
64 /// Pop from the aync sender queue and wait for one send to complete.
65 void WaitTillSent() override;
68 /// Accessor/mutator to dump all of the buffered logs.
69 std::vector<StatusLogLine>& dump();
71 /// Add the buffered log sink to Glog.
74 /// Start the Buffered Sink, without enabling forwarding to loggers.
78 * @brief Add a logger plugin that should receive status updates.
80 * Since the logger may support multiple active logger plugins the sink
81 * will keep track of those plugins that returned success after ::init.
82 * This list of plugins will received forwarded messages from the sink.
84 * This list is important because sending logs to plugins that also use
85 * and active Glog Sink (supports multiple) will create a logging loop.
87 void addPlugin(const std::string& name);
89 /// Clear the sinks list, clear the named plugins added by addPlugin.s
92 /// Retrieve the list of enabled plugins that should have logs forwarded.
93 const std::vector<std::string>& enabledPlugins() const;
96 /// Queue of sender functions that relay status logs to all plugins.
97 std::queue<std::future<void>> senders;
100 BufferedLogSink(BufferedLogSink const&) = delete;
101 void operator=(BufferedLogSink const&) = delete;
104 /// Create the log sink as buffering or forwarding.
105 BufferedLogSink() = default;
107 /// Stop the log sink.
111 /// Intermediate log storage until an osquery logger is initialized.
112 std::vector<StatusLogLine> logs_;
115 * @Brief Is the logger temporarily disabled.
117 * The Google Log Sink will still be active, but the send method also checks
118 * enabled and drops log lines to the flood if the forwarder is not enabled.
120 std::atomic<bool> enabled_{false};
122 /// Track multiple loggers that should receive sinks from the send forwarder.
123 std::vector<std::string> sinks_;
126 /// Mutex protecting accesses to buffered status logs.
127 Mutex kBufferedLogSinkLogs;
129 /// Mutex protecting queued status log futures.
130 Mutex kBufferedLogSinkSenders;
132 static void serializeIntermediateLog(const std::vector<StatusLogLine>& log,
133 PluginRequest& request) {
134 auto doc = JSON::newArray();
135 for (const auto& i : log) {
136 auto line = doc.getObject();
137 doc.add("s", static_cast<int>(i.severity), line);
138 doc.addRef("f", i.filename, line);
139 doc.add("i", i.line, line);
140 doc.addRef("m", i.message, line);
141 doc.addRef("h", i.identifier, line);
142 doc.addRef("c", i.calendar_time, line);
143 doc.add("u", i.time, line);
147 doc.toString(request["log"]);
150 void initStatusLogger(const std::string& name, bool init_glog) {
151 // Start the logging, and announce the daemon is starting.
153 google::InitGoogleLogging(name.c_str());
157 void initLogger(const std::string& name) {
158 BufferedLogSink::get().resetPlugins();
160 bool forward = false;
161 PluginRequest init_request = {{"init", name}};
162 PluginRequest features_request = {{"action", "features"}};
163 auto logger_plugin = RegistryFactory::get().getActive("logger");
164 // Allow multiple loggers, make sure each is accessible.
165 for (const auto& logger : osquery::split(logger_plugin, ",")) {
166 if (!RegistryFactory::get().exists("logger", logger)) {
170 Registry::call("logger", logger, init_request);
171 auto status = Registry::call("logger", logger, features_request);
172 if ((status.getCode() & LOGGER_FEATURE_LOGSTATUS) > 0) {
173 // Glog status logs are forwarded to logStatus.
175 // To support multiple plugins we only add the names of plugins that
176 // return a success status after initialization.
177 BufferedLogSink::get().addPlugin(logger);
182 // Begin forwarding after all plugins have been set up.
183 BufferedLogSink::get().enable();
184 relayStatusLogs(true);
188 BufferedLogSink& BufferedLogSink::get() {
189 static BufferedLogSink sink;
193 void BufferedLogSink::setUp() {
194 google::AddLogSink(&get());
197 void BufferedLogSink::enable() {
201 void BufferedLogSink::send(google::LogSeverity severity,
202 const char* full_filename,
203 const char* base_filename,
205 const struct ::tm* tm_time,
207 size_t message_len) {
208 // WARNING, be extremely careful when accessing data here.
209 // This should not cause any persistent storage or logging actions.
211 WriteLock lock(kBufferedLogSinkLogs);
212 logs_.push_back({(StatusLogSeverity)severity,
213 std::string(base_filename),
214 static_cast<size_t>(line),
215 std::string(message, message_len),
216 toAsciiTimeUTC(tm_time),
222 void BufferedLogSink::WaitTillSent() {
223 std::future<void> first;
226 WriteLock lock(kBufferedLogSinkSenders);
227 if (senders.empty()) {
230 first = std::move(senders.back());
237 std::vector<StatusLogLine>& BufferedLogSink::dump() {
241 void BufferedLogSink::addPlugin(const std::string& name) {
242 sinks_.push_back(name);
245 void BufferedLogSink::resetPlugins() {
249 const std::vector<std::string>& BufferedLogSink::enabledPlugins() const {
253 BufferedLogSink::~BufferedLogSink() {
257 Status logString(const std::string& message, const std::string& category) {
259 message, category, RegistryFactory::get().getActive("logger"));
262 Status logString(const std::string& message,
263 const std::string& category,
264 const std::string& receiver) {
266 for (const auto& logger : osquery::split(receiver, ",")) {
267 if (Registry::get().exists("logger", logger, true)) {
268 auto plugin = Registry::get().plugin("logger", logger);
269 auto logger_plugin = std::dynamic_pointer_cast<LoggerPlugin>(plugin);
270 status = logger_plugin->logString(message);
272 status = Registry::call(
273 "logger", logger, {{"string", message}, {"category", category}});
280 const std::string kTotalQueryCounterMonitorPath("query.total.count");
283 Status logQueryLogItem(const QueryLogItem& results) {
284 return logQueryLogItem(results, RegistryFactory::get().getActive("logger"));
287 Status logQueryLogItem(const QueryLogItem& results,
288 const std::string& receiver) {
289 std::vector<std::string> json_items;
292 status = serializeQueryLogItemJSON(results, json);
293 json_items.emplace_back(json);
299 for (const auto& json : json_items) {
300 status = logString(json, "event", receiver);
305 Status logSnapshotQuery(const QueryLogItem& item) {
306 std::vector<std::string> json_items;
309 status = serializeQueryLogItemJSON(item, json);
310 json_items.emplace_back(json);
316 for (const auto& json : json_items) {
317 auto receiver = RegistryFactory::get().getActive("logger");
318 for (const auto& logger : osquery::split(receiver, ",")) {
319 if (Registry::get().exists("logger", logger, true)) {
320 auto plugin = Registry::get().plugin("logger", logger);
321 auto logger_plugin = std::dynamic_pointer_cast<LoggerPlugin>(plugin);
322 status = logger_plugin->logSnapshot(json);
324 status = Registry::call("logger", logger, {{"snapshot", json}});
332 size_t queuedStatuses() {
333 ReadLock lock(kBufferedLogSinkLogs);
334 return BufferedLogSink::get().dump().size();
337 size_t queuedSenders() {
338 ReadLock lock(kBufferedLogSinkSenders);
339 return BufferedLogSink::get().senders.size();
342 void relayStatusLogs(bool async) {
344 ReadLock lock(kBufferedLogSinkLogs);
345 if (BufferedLogSink::get().dump().size() == 0) {
350 auto sender = ([]() {
351 auto identifier = getHostIdentifier();
353 // Construct a status log plugin request.
354 PluginRequest request = {{"status", "true"}};
356 WriteLock lock(kBufferedLogSinkLogs);
357 auto& status_logs = BufferedLogSink::get().dump();
358 for (auto& log : status_logs) {
359 // Copy the host identifier into each status log.
360 log.identifier = identifier;
363 serializeIntermediateLog(status_logs, request);
365 // Flush the buffered status logs.
369 auto logger_plugin = RegistryFactory::get().getActive("logger");
370 for (const auto& logger : osquery::split(logger_plugin, ",")) {
371 auto& enabled = BufferedLogSink::get().enabledPlugins();
372 if (std::find(enabled.begin(), enabled.end(), logger) != enabled.end()) {
373 // Skip the registry's logic, and send directly to the core's logger.
374 PluginResponse response;
375 Registry::call("logger", logger, request, response);
383 std::packaged_task<void()> task(std::move(sender));
384 auto result = task.get_future();
385 std::thread(std::move(task)).detach();
387 // Lock accesses to the sender queue.
388 WriteLock lock(kBufferedLogSinkSenders);
389 BufferedLogSink::get().senders.push(std::move(result));
393 void systemLog(const std::string& line) {
395 syslog(LOG_NOTICE, "%s", line.c_str());