/** * Copyright (c) 2014-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under both the Apache 2.0 license (found in the * LICENSE file in the root directory of this source tree) and the GPLv2 (found * in the COPYING file in the root directory of this source tree). * You may select, at your option, one of the above-listed licenses. */ #pragma once #include #include #ifdef WIN32 #define GLOG_NO_ABBREVIATED_SEVERITIES #define GOOGLE_GLOG_DLL_DECL #endif #include #include #include #include #include #include namespace osquery { /** * @brief An internal severity set mapping to Glog's LogSeverity levels. */ enum StatusLogSeverity { O_INFO = 0, O_WARNING = 1, O_ERROR = 2, O_FATAL = 3, }; /// An intermediate status log line. struct StatusLogLine { public: /// An integer severity level mimicking Glog's. StatusLogSeverity severity; /// The name of the file emitting the status log. std::string filename; /// The line of the file emitting the status log. size_t line; /// The string-formatted status message. std::string message; /// The ASCII time stamp for when the status message was emitted std::string calendar_time; /// The UNIX time for when the status message was emitted size_t time; /** * @brief The host identifier at the time when logs are flushed. * * There is occasionally a delay between logging a status and decorating * with the host identifier. In most cases the identifier is static so this * does not matter. In some cases the host identifier causes database lookups. */ std::string identifier; }; /** * @brief Logger plugin feature bits for complicated loggers. * * Logger plugins may opt-in to additional features like explicitly handling * Glog status events or requesting event subscribers to forward each event * directly to the logger. This enumeration tracks, and corresponds to, each * of the feature methods defined in a logger plugin. * * A specific registry call action can be used to retrieve an overloaded Status * object containing all of the opt-in features. */ enum LoggerFeatures { LOGGER_FEATURE_BLANK = 0, LOGGER_FEATURE_LOGSTATUS = 1, LOGGER_FEATURE_LOGEVENT = 2, }; /** * @brief Helper logging macro for table-generated verbose log lines. * * Since logging in tables does not always mean a critical warning or error * but more likely a parsing or expected edge-case, we provide a TLOG. * * The tool user can set within config or via the CLI what level of logging * to tolerate. It's the table developer's job to assume consistency in logging. */ #define TLOG VLOG(1) /** * @brief Prepend a reference number to the log line. * * A reference number is an external-search helper for somewhat confusing or * seeminly-critical log lines. */ #define RLOG(n) "[Ref #" #n "] " /** * @brief Superclass for the pluggable logging facilities. * * In order to make the logging of osquery results and inline debug, warning, * error status easy to integrate into your environment, we take advantage of * a plugin interface which allows you to integrate osquery with your internal * large-scale logging infrastructure. * * You may use flume, splunk, syslog, scribe, etc. In order to use your * specific upstream logging systems, one simply needs to create a custom * subclass of LoggerPlugin. That subclass should at least implement the * LoggerPlugin::logString method. * * Consider the following example: * * @code{.cpp} * class TestLoggerPlugin : public LoggerPlugin { * public: * osquery::Status logString(const std::string& s) { * int i = 0; * internal::logStringToFlume(s, i); * std::string message; * if (i == 0) { * message = "OK"; * } else { * message = "Failed"; * } * return osquery::Status(i, message); * } * }; * * REGISTER(TestLoggerPlugin, "logger", "test"); * @endcode */ class LoggerPlugin : public Plugin { public: /// The LoggerPlugin PluginRequest action router. Status call(const PluginRequest& request, PluginResponse& response) override; /** * @brief A feature method to decide if Glog should stop handling statuses. * * Return true if this logger plugin's #logStatus method should handle Glog * statuses exclusively. If true then Glog will stop writing status lines * to the configured log path. * * @return false if this logger plugin should NOT handle Glog statuses. */ virtual bool usesLogStatus() { return false; } /** * @brief A feature method to decide if events should be forwarded. * * See the optional logEvent method. * * @return false if this logger plugin should NOT handle events directly. */ virtual bool usesLogEvent() { return false; } /** * @brief Set the process name. */ void setProcessName(const std::string& name) { process_name_ = name; } /** * @brief Get the process name. */ const std::string& name() const { return process_name_; } /** @brief Virtual method which should implement custom logging. * * LoggerPlugin::logString should be implemented by a subclass of * LoggerPlugin which needs to log a string in a custom way. * * @return an instance of osquery::Status which indicates the success or * failure of the operation. */ virtual Status logString(const std::string& s) = 0; /** * @brief See the usesLogStatus method, log a Glog status. * * @param log A vector of parsed Glog log lines. * @return Status non-op indicating success or failure. */ virtual Status logStatus(const std::vector& log) { (void)log; return Status(1, "Not enabled"); } /** * @brief Optionally handle snapshot query results separately from events. * * If a logger plugin wants to write snapshot query results (potentially * large amounts of data) to a specific sink it should implement logSnapshot. * Otherwise the serialized log item data will be forwarded to logString. * * @param s A special log item will complete results from a query. * @return log status */ virtual Status logSnapshot(const std::string& s) { return logString(s); } /** * @brief Optionally handle each published event via the logger. * * It is possible to skip the database representation of event subscribers * and instead forward each added event to the active logger plugin. */ virtual Status logEvent(const std::string& /*s*/) { return Status(1, "Not enabled"); } protected: /** * @brief Initialize the logger with the name of the binary and any status * logs generated between program launch and logger start. * * The logger initialization is called once CLI flags have been parsed, the * registry items are constructed, extension routes broadcasted and extension * plugins discovered (as a logger may be an extension plugin) and the config * has been loaded (which may include additional CLI flag-options). * * All of these actions may have generated VERBOSE, INFO, WARNING, or ERROR * logs. The internal logging facility, Glog, collects these intermediate * status logs and a customized log sink buffers them until the active * osquery logger's `init` method is called. * * @param binary_name The string name of the process (argv[0]). * @param log The set of status (INFO, WARNING, ERROR) logs generated before * the logger's `init` method was called. */ virtual void init(const std::string& binary_name, const std::vector& log) = 0; private: std::string process_name_; }; /// Set the verbose mode, changes Glog's sinking logic and will affect plugins. void setVerboseLevel(); /** * @brief Start status logging to a buffer until the logger plugin is online. * * This will also call google::InitGoogleLogging. Use the default init_glog * to control this in tests to protect against calling the API twice. */ void initStatusLogger(const std::string& name, bool init_glog = true); /** * @brief Initialize the osquery Logger facility by dumping the buffered status * logs and configurating status log forwarding. * * initLogger will disable the `BufferedLogSink` facility, dump any status logs * emitted between process start and this init call, then configure the new * logger facility to receive status logs. * * The `forward_all` control is used when buffering logs in extensions. * It is fine if the logger facility in the core app does not want to receive * status logs, but this is NOT an option in extensions/modules. All status * logs must be forwarded to the core. * * @param name The process name. */ void initLogger(const std::string& name); /** * @brief Log a string using the default logger receiver. * * Note that this method should only be used to log results. If you'd like to * log normal osquery operations, use Google Logging. * * @param message the string to log * @param category a category/metadata key * * @return Status indicating the success or failure of the operation */ Status logString(const std::string& message, const std::string& category); /** * @brief Log a string using a specific logger receiver. * * Note that this method should only be used to log results. If you'd like to * log normal osquery operations, use Google Logging. * * @param message the string to log * @param category a category/metadata key * @param receiver a string representing the log receiver to use * * @return Status indicating the success or failure of the operation */ Status logString(const std::string& message, const std::string& category, const std::string& receiver); /** * @brief Log results of scheduled queries to the default receiver * * @param item a struct representing the results of a scheduled query * * @return Status indicating the success or failure of the operation */ Status logQueryLogItem(const QueryLogItem& item); /** * @brief Log results of scheduled queries to a specified receiver * * @param item a struct representing the results of a scheduled query * @param receiver a string representing the log receiver to use * * @return Status indicating the success or failure of the operation */ Status logQueryLogItem(const QueryLogItem& item, const std::string& receiver); /** * @brief Log raw results from a query (or a snapshot scheduled query). * * @param item the unmangled results from the query planner. * * @return Status indicating the success or failure of the operation */ Status logSnapshotQuery(const QueryLogItem& item); /** * @brief Sink a set of buffered status logs. * * When the osquery daemon uses a watcher/worker set, the watcher's status logs * are accumulated in a buffered log sink. Well-performing workers should have * the set of watcher status logs relayed and sent to the configured logger * plugin. * * Status logs from extensions will be forwarded to the extension manager (core) * normally, but the watcher does not receive or send registry requests. * Extensions, the registry, configuration, and optional config/logger plugins * are all protected as a monitored worker. */ void relayStatusLogs(bool async = false); /// Inspect the number of internal-buffered status log lines. size_t queuedStatuses(); /// Inspect the number of active internal status log sender threads. size_t queuedSenders(); /** * @brief Write a log line to the OS system log. * * There are occasional needs to log independently of the osquery logging * facilities. This allows a feature (not a table) to bypass all osquery * configuration and log to the OS system log. * * Linux/Darwin: this uses syslog's LOG_NOTICE. * Windows: This will end up inside the Facebook/osquery in the Windows * Event Log. */ void systemLog(const std::string& line); } // namespace osquery