diff --git a/README.md b/README.md index 5ac68d0..1115b8d 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ A simple syslog component for esphome. The component is designed to auto attach itself to the logger core module (like the MQTT component does with the `log_topic`) -This component uses the https://github.com/arcao/Syslog library version 2.0 at its core - ## How to ### Manually diff --git a/components/syslog/__init__.py b/components/syslog/__init__.py index af8d58b..637d0ed 100644 --- a/components/syslog/__init__.py +++ b/components/syslog/__init__.py @@ -8,7 +8,7 @@ CONF_ENABLE_LOGGER_MESSAGES = "enable_logger" CONF_MIN_LEVEL = "min_level" -DEPENDENCIES = ['logger','network'] +DEPENDENCIES = ['logger','network','socket'] debug_ns = cg.esphome_ns.namespace('debug') syslog_ns = cg.esphome_ns.namespace('syslog') @@ -16,17 +16,14 @@ SyslogComponent = syslog_ns.class_('SyslogComponent', cg.Component) SyslogLogAction = syslog_ns.class_('SyslogLogAction', automation.Action) -CONFIG_SCHEMA = cv.All( - cv.Schema({ - cv.GenerateID(): cv.declare_id(SyslogComponent), - cv.Optional(CONF_IP_ADDRESS, default="255.255.255.255"): cv.string_strict, - cv.Optional(CONF_PORT, default=514): cv.port, - cv.Optional(CONF_ENABLE_LOGGER_MESSAGES, default=True): cv.boolean, - cv.Optional(CONF_STRIP_COLORS, default=True): cv.boolean, - cv.Optional(CONF_MIN_LEVEL, default="DEBUG"): is_log_level, - }), - cv.only_with_arduino, -) +CONFIG_SCHEMA = cv.Schema({ + cv.GenerateID(): cv.declare_id(SyslogComponent), + cv.Optional(CONF_IP_ADDRESS, default="255.255.255.255"): cv.string_strict, + cv.Optional(CONF_PORT, default=514): cv.port, + cv.Optional(CONF_ENABLE_LOGGER_MESSAGES, default=True): cv.boolean, + cv.Optional(CONF_STRIP_COLORS, default=True): cv.boolean, + cv.Optional(CONF_MIN_LEVEL, default="DEBUG"): is_log_level, +}) SYSLOG_LOG_ACTION_SCHEMA = cv.Schema({ cv.GenerateID(): cv.use_id(SyslogComponent), @@ -36,8 +33,6 @@ }) def to_code(config): - cg.add_library('Syslog', '2.0.0') - var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) diff --git a/components/syslog/syslog_component.cpp b/components/syslog/syslog_component.cpp index ea80947..c785e4f 100644 --- a/components/syslog/syslog_component.cpp +++ b/components/syslog/syslog_component.cpp @@ -2,6 +2,7 @@ #include "esphome/core/log.h" #include "esphome/core/application.h" +#include "esphome/core/version.h" #ifdef USE_LOGGER #include "esphome/components/logger/logger.h" @@ -9,9 +10,13 @@ /* #include "esphome/core/helpers.h" #include "esphome/core/defines.h" -#include "esphome/core/version.h" */ +#ifdef USE_SOCKET_IMPL_LWIP_TCP +#include +#define IPPROTO_UDP IP_PROTO_UDP +#endif + namespace esphome { namespace syslog { @@ -23,12 +28,53 @@ static const uint8_t esphome_to_syslog_log_levels[] = {0, 3, 4, 6, 5, 7, 7, 7}; SyslogComponent::SyslogComponent() { this->settings_.client_id = App.get_name(); - // Get the WifiUDP client here instead of getting it in setup() to make sure we always have a client when calling log() - // Calling log() without the device connected should not be an issue since there is a wifi connected check and WifiUDP fails "silently" and doesn't generate an exception anyways - this->udp_ = new WiFiUDP(); } void SyslogComponent::setup() { + + /* + * Older versions of socket::set_sockaddr() return bogus results + * if trying to log to a Legacy IP address when IPv6 is enabled. + * Fixed by https://github.com/esphome/esphome/pull/7196 + */ + this->server_socklen = 0; + if (ESPHOME_VERSION_CODE >= VERSION_CODE(2024, 8, 0)) { + this->server_socklen = socket::set_sockaddr((struct sockaddr *)&this->server, sizeof(this->server), + this->settings_.address, this->settings_.port); + } +#if USE_NETWORK_IPV6 + else if (this->settings_.address.find(':') != std::string::npos) { + auto *server6 = reinterpret_cast(&this->server); + memset(server6, 0, sizeof(*server6)); + server6->sin6_family = AF_INET6; + server6->sin6_port = htons(this->settings_.port); + + ip6_addr_t ip6; + inet6_aton(this->settings_.address.c_str(), &ip6); + memcpy(server6->sin6_addr.un.u32_addr, ip6.addr, sizeof(ip6.addr)); + this->server_socklen = sizeof(*server6); + } +#endif /* USE_NETWORK_IPV6 */ + else { + auto *server4 = reinterpret_cast(&this->server); + memset(server4, 0, sizeof(*server4)); + server4->sin_family = AF_INET; + server4->sin_addr.s_addr = inet_addr(this->settings_.address.c_str()); + server4->sin_port = htons(this->settings_.port); + this->server_socklen = sizeof(*server4); + } + if (!this->server_socklen) { + ESP_LOGE(TAG, "Failed to parse server IP address '%s'", this->settings_.address.c_str()); + this->mark_failed(); + return; + } + this->socket_ = socket::socket(this->server.ss_family, SOCK_DGRAM, IPPROTO_UDP); + if (!this->socket_) { + ESP_LOGE(TAG, "Failed to create UDP socket"); + this->mark_failed(); + return; + } + this->log(ESPHOME_LOG_LEVEL_INFO , "syslog", "Syslog started"); ESP_LOGI(TAG, "Started"); @@ -51,23 +97,22 @@ void SyslogComponent::loop() { } void SyslogComponent::log(uint8_t level, const std::string &tag, const std::string &payload) { + + if (this->is_failed()) + return; + level = level > 7 ? 7 : level; - // Simple check to make sure that there is connectivity, if not, log the issue and return - if(WiFi.status() != WL_CONNECTED) { - ESP_LOGW(TAG, "Tried to send \"%s\"@\"%s\" with level %d but Wifi isn't connected yet", tag.c_str(), payload.c_str(), level); + if (!this->socket_) { + ESP_LOGW(TAG, "Tried to send \"%s\"@\"%s\" with level %d but socket isn't connected", tag.c_str(), payload.c_str(), level); return; } - Syslog syslog( - *this->udp_, - this->settings_.address.c_str(), - this->settings_.port, - this->settings_.client_id.c_str(), - tag.c_str(), - LOG_KERN - ); - if(!syslog.log(esphome_to_syslog_log_levels[level], payload.c_str())) { + int pri = esphome_to_syslog_log_levels[level]; + std::string buf = str_sprintf("<%d>1 - %s %s - - - \xEF\xBB\xBF%s", + pri, this->settings_.client_id.c_str(), + tag.c_str(), payload.c_str()); + if (this->socket_->sendto(buf.c_str(), buf.length(), 0, (struct sockaddr *)&this->server, this->server_socklen) < 0) { ESP_LOGW(TAG, "Tried to send \"%s\"@\"%s\" with level %d but but failed for an unknown reason", tag.c_str(), payload.c_str(), level); } } diff --git a/components/syslog/syslog_component.h b/components/syslog/syslog_component.h index b1cd173..1cb9465 100644 --- a/components/syslog/syslog_component.h +++ b/components/syslog/syslog_component.h @@ -6,16 +6,7 @@ #include "esphome/core/defines.h" #include "esphome/core/automation.h" #include "esphome/core/log.h" -#include -#include - -#if defined ESP8266 || defined ARDUINO_ESP8266_ESP01 - #include -#else - #include -#endif - -#include +#include "esphome/components/socket/socket.h" namespace esphome { namespace syslog { @@ -51,7 +42,9 @@ class SyslogComponent : public Component { bool strip_colors; bool enable_logger; SYSLOGSettings settings_; - UDP *udp_ = NULL; + std::unique_ptr socket_ = nullptr; + struct sockaddr_storage server; + socklen_t server_socklen; }; template class SyslogLogAction : public Action {