diff --git a/components/syslog/__init__.py b/components/syslog/__init__.py deleted file mode 100644 index 3e392f6..0000000 --- a/components/syslog/__init__.py +++ /dev/null @@ -1,59 +0,0 @@ -import esphome.config_validation as cv -import esphome.codegen as cg -from esphome import automation -from esphome.const import CONF_ID, CONF_IP_ADDRESS, CONF_PORT, CONF_CLIENT_ID, CONF_LEVEL, CONF_PAYLOAD, CONF_TAG -from esphome.components.logger import LOG_LEVELS, is_log_level - -CONF_STRIP_COLOR_CODES = "strip_color_codes" -CONF_FORWARD_LOGGER = "forward_logger" -CONF_MIN_LOG_LEVEL = "min_log_level" - -DEPENDENCIES = ['logger', 'network'] - -syslog_ns = cg.esphome_ns.namespace('syslog') - -Syslog = syslog_ns.class_('Syslog', cg.Component) -SyslogLogAction = syslog_ns.class_('SyslogLogAction', automation.Action) - -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(Syslog), - cv.Optional(CONF_IP_ADDRESS, default="255.255.255.255"): cv.string_strict, - cv.Optional(CONF_PORT, default=514): cv.port, - cv.Optional(CONF_FORWARD_LOGGER, default=True): cv.boolean, - cv.Optional(CONF_STRIP_COLOR_CODES, default=True): cv.boolean, - cv.Optional(CONF_MIN_LOG_LEVEL, default="DEBUG"): is_log_level, - cv.Optional(CONF_CLIENT_ID): cv.string_strict, -}) - -SYSLOG_LOG_ACTION_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.use_id(Syslog), - cv.Required(CONF_LEVEL): cv.templatable(cv.int_range(min=0, max=7)), - cv.Required(CONF_TAG): cv.templatable(cv.string), - cv.Required(CONF_PAYLOAD): cv.templatable(cv.string), -}) - -def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) - yield cg.register_component(var, config) - - cg.add(var.set_forward_logger(config[CONF_FORWARD_LOGGER])) - cg.add(var.set_strip_color_codes(config[CONF_STRIP_COLOR_CODES])) - cg.add(var.set_server_ip_address(config[CONF_IP_ADDRESS])) - cg.add(var.set_server_port(config[CONF_PORT])) - cg.add(var.set_min_log_level(LOG_LEVELS[config[CONF_MIN_LOG_LEVEL]])) - if CONF_CLIENT_ID in config: - cg.add(var.set_hostname(config[CONF_CLIENT_ID])) - -@automation.register_action('syslog.log', SyslogLogAction, SYSLOG_LOG_ACTION_SCHEMA) -def syslog_log_action_to_code(config, action_id, template_arg, args): - parent = yield cg.get_variable(config[CONF_ID]) - var = cg.new_Pvariable(action_id, template_arg, parent) - - template_ = yield cg.templatable(config[CONF_LEVEL], args, cg.int) - cg.add(var.set_level(template_)) - template_ = yield cg.templatable(config[CONF_TAG], args, cg.std_string) - cg.add(var.set_tag(template_)) - template_ = yield cg.templatable(config[CONF_PAYLOAD], args, cg.std_string) - cg.add(var.set_payload(template_)) - - yield var diff --git a/components/syslog/syslog.cpp b/components/syslog/syslog.cpp deleted file mode 100644 index 8355f20..0000000 --- a/components/syslog/syslog.cpp +++ /dev/null @@ -1,173 +0,0 @@ -#include "syslog.h" -#ifdef USE_NETWORK - -#include - -#include "esphome/components/network/util.h" -#include "esphome/core/application.h" -#include "esphome/core/log.h" - -#ifdef USE_LOGGER -#include "esphome/components/logger/logger.h" -#endif - -namespace esphome { - -namespace syslog { - -std::string remove_ansi_colors(const std::string &input) { - std::string result; - bool escape_sequence = false; - - for (char c : input) { - if (c == '\033') { // Start of an escape sequence - escape_sequence = true; - } else if (escape_sequence && c == 'm') { // End of an escape sequence - escape_sequence = false; - } else if (!escape_sequence) { // Normal character - result += c; - } - } - - return result; -} - -enum Priority { - EMERG = 0, /* System is unusable */ - ALERT = 1, /* Action must be taken immediately */ - CRIT = 2, /* Critical conditions */ - ERR = 3, /* Error conditions */ - WARNING = 4, /* Warning conditions */ - NOTICE = 5, /* Normal but significant conditions */ - INFO = 6, /* Informational messages */ - DEBUG = 7, /* Debug-level messages */ -}; - -static const uint8_t esphome_to_syslog_log_levels[] = { - /* ESPHOME_LOG_LEVEL_NONE */ Priority::DEBUG, - /* ESPHOME_LOG_LEVEL_ERROR */ Priority::ERR, - /* ESPHOME_LOG_LEVEL_WARN */ Priority::WARNING, - /* ESPHOME_LOG_LEVEL_INFO */ Priority::INFO, - /* ESPHOME_LOG_LEVEL_CONFIG */ Priority::NOTICE, - /* ESPHOME_LOG_LEVEL_DEBUG */ Priority::DEBUG, - /* ESPHOME_LOG_LEVEL_VERBOSE */ Priority::DEBUG, - /* ESPHOME_LOG_LEVEL_VERY_VERBOSE */ Priority::DEBUG, -}; - -static const uint8_t ESPHOME_LOG_LEVELS = 8; - -static const char *TAG = "syslog"; - -Syslog::Syslog() { - this->hostname_ = ::esphome::network::get_use_address(); - this->errors_encountered_ = 0; -} - -void Syslog::on_log(uint8_t level, const char *tag, const char *raw_message, size_t raw_message_len) { - if (level > this->min_log_level_) return; - - std::string message = std::string(raw_message, raw_message_len); - if (this->strip_color_codes_) { - std::string clean_message = remove_ansi_colors(message); - this->log(level, tag, clean_message); - } else { - this->log(level, tag, message); - } -} - -void Syslog::setup() { -#ifdef USE_LOGGER - if (logger::global_logger != nullptr && this->forward_logger_) { - logger::global_logger->add_log_listener(this); - } -#endif - -#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || \ - defined(USE_SOCKET_IMPL_LWIP_SOCKETS) - this->socket_ = socket::socket(AF_INET, SOCK_DGRAM, PF_INET); - // We don't expect any reply from syslog server, so there's no need to block. - this->socket_->setblocking(false); - this->destination_len_ = socket::set_sockaddr( - reinterpret_cast(&this->destination_), - sizeof(this->destination_), this->server_ip_address_, this->server_port_); - - if (this->destination_len_ == 0) { - std::string error_message(strerror(errno)); - ESP_LOGE(TAG, - "Cannot use IP address '%s' or port %d for server connection: %s", - this->server_ip_address_.c_str(), this->server_port_, - error_message.c_str()); - } -#endif -} - -void Syslog::loop() { - if (!this->latest_error_message_.empty() && this->errors_encountered_ >= 10) { - ESP_LOGW(TAG, "Failed to send log: %s", latest_error_message_.c_str()); - this->errors_encountered_ = 0; - latest_error_message_.clear(); - } -} - -void Syslog::dump_config() { - ESP_LOGCONFIG(TAG, "Syslog:"); - ESP_LOGCONFIG(TAG, " Server IP address: %s", - this->server_ip_address_.c_str()); - ESP_LOGCONFIG(TAG, " Server port: %d", this->server_port_); - ESP_LOGCONFIG(TAG, " Client ID (syslog hostname): %s", - this->hostname_.c_str()); -#ifdef USE_LOGGER - ESP_LOGCONFIG(TAG, " Min log level: %d", this->min_log_level_); - ESP_LOGCONFIG(TAG, " Forward logger messages: %s", - this->forward_logger_ ? "yes" : "no"); - ESP_LOGCONFIG(TAG, " Strip color codes: %s", - this->strip_color_codes_ ? "yes" : "no"); -#endif -} - -void Syslog::log(int level, const std::string &tag, const std::string &msg) { - if (level >= ESPHOME_LOG_LEVELS) { - level = ESPHOME_LOG_LEVELS - 1; - } else if (level < 0) { - level = 0; - } - - std::stringstream payload_stream; - // rsyslog compatible protocol: - // VERSION SP TIMESTAMP SP HOSTNAME SP APP-NAME SP PROCID SP MSGID SP - // [SD-ID]s SP MSG - payload_stream << "<" << static_cast(esphome_to_syslog_log_levels[level]) - << ">1 - " << this->hostname_ << " " << App.get_name() - << " - - - " << msg; - - std::string payload = payload_stream.str(); - ssize_t bytes_sent = 0; -#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || \ - defined(USE_SOCKET_IMPL_LWIP_SOCKETS) - if (this->destination_len_ > 0) { - bytes_sent = - this->socket_->sendto(payload.c_str(), payload.length(), 0, - reinterpret_cast(&this->destination_), - this->destination_len_); - } -#else - if (this->udp_client_.beginPacket(this->server_ip_address_.c_str(), - this->server_port_)) { - bytes_sent = this->udp_client_.write(payload.c_str(), payload.length()); - if (!this->udp_client_.endPacket()) { - bytes_sent = 0; - } - } -#endif - - if (bytes_sent != payload.length()) { - // Can't log here as we could be within logger callback, but our loop() will - // pick it up. - this->latest_error_message_ = strerror(errno); - ++this->errors_encountered_; - } -} - -} // namespace syslog -} // namespace esphome -#endif diff --git a/components/syslog/syslog.h b/components/syslog/syslog.h deleted file mode 100644 index 85672a6..0000000 --- a/components/syslog/syslog.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once -#include "esphome/core/defines.h" -#ifdef USE_NETWORK - -/* Potential future improvements: - - - hostname instead of IP address - - custom mapping of log leves to syslog levels - - set custom facility - - protocol support: Structured Data, BSD, TCP - - allow specifying time component to get timestamp - - code optimization (e.g. marking methods as HOT or using fewer complex ops) - - autoretries (fixes esp8266 skipping logs at startup due to dump_config flood) - */ - -#include "esphome/core/automation.h" -#include "esphome/core/component.h" -#include "esphome/core/log.h" -#include "esphome/components/logger/logger.h" - -#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || \ - defined(USE_SOCKET_IMPL_LWIP_SOCKETS) -#include "esphome/components/socket/socket.h" -#else -#include "WiFiUdp.h" -#endif - -namespace esphome { - -namespace syslog { - -class Syslog : public Component, public logger::LogListener { - public: - explicit Syslog(); - - float get_setup_priority() const override { - return setup_priority::AFTER_WIFI; - } - - void setup() override; - void loop() override; - void dump_config() override; - - void set_server_ip_address(const std::string &address) { - this->server_ip_address_ = address; - } - void set_server_port(uint16_t port) { this->server_port_ = port; } - void set_hostname(const std::string &hostname) { this->hostname_ = hostname; } - void set_min_log_level(int log_level) { this->min_log_level_ = log_level; } - void set_forward_logger(bool forward) { this->forward_logger_ = forward; } - void set_strip_color_codes(bool strip) { this->strip_color_codes_ = strip; } - - void on_log(uint8_t, const char *, const char *, size_t) override; - void log(int level, const std::string &tag, const std::string &msg); - - protected: - std::string server_ip_address_; - uint16_t server_port_; - std::string hostname_; - int min_log_level_; - bool forward_logger_; - bool strip_color_codes_; - -#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || \ - defined(USE_SOCKET_IMPL_LWIP_SOCKETS) - std::unique_ptr socket_{}; - struct sockaddr_storage destination_; - socklen_t destination_len_; -#else - // The socket class doesn't implement UDP for ESP8266. - WiFiUDP udp_client_{}; -#endif - - int errors_encountered_; - std::string latest_error_message_; - - template - class SyslogLogAction : public Action { - public: - SyslogLogAction(Syslog *parent) : parent_(parent) {} - TEMPLATABLE_VALUE(int, level) - TEMPLATABLE_VALUE(std::string, tag) - TEMPLATABLE_VALUE(std::string, payload) - - void play(Ts... x) override { - this->parent_->log(this->level_.value(x...), this->tag_.value(x...), - this->payload_.value(x...)); - } - - protected: - Syslog *parent_; - }; -}; - -} // namespace syslog - -} // namespace esphome -#endif diff --git a/templates/syslog.yaml b/templates/syslog.yaml index 510a056..22f543c 100644 --- a/templates/syslog.yaml +++ b/templates/syslog.yaml @@ -1,10 +1,18 @@ -external_components: - # Path is relative to the main YAML file, not this device_base template! - - source: components +udp: + - id: syslog_server + addresses: !secret syslog_server + +time: + # https://github.com/esphome/esphome/issues/12591 + - id: syslog_fake_time + platform: sntp + timezone: UTC + servers: + - 127.0.0.1 -# https://github.com/esphome/esphome/issues/12591 syslog: - ip_address: !secret syslog_server + udp_id: syslog_server + time_id: syslog_fake_time mqtt: # Disable logging to save bandwidth.