Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix esp-idf and IPv6 support by using plain UDP socket, drop Syslog library #17

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 9 additions & 14 deletions components/syslog/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,22 @@
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')

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),
Expand All @@ -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)

Expand Down
66 changes: 50 additions & 16 deletions components/syslog/syslog_component.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

#include "esphome/core/log.h"
#include "esphome/core/application.h"
#include "esphome/core/version.h"

#ifdef USE_LOGGER
#include "esphome/components/logger/logger.h"
#endif
/*
#include "esphome/core/helpers.h"
#include "esphome/core/defines.h"
#include "esphome/core/version.h"
*/

namespace esphome {
Expand All @@ -23,12 +23,51 @@ 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<sockaddr_in6 *>(&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<sockaddr_in *>(&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_LOGW(TAG, "Failed to parse server IP address '%s'", this->settings_.address.c_str());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO i would log error, mark component failed and then return from setup.

return;
}
this->socket_ = socket::socket(this->server.ss_family, SOCK_DGRAM, IPPROTO_UDP);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IPPROTO_UDP is not defined on arduino for esp8266.
I managed to fix this by adding

#ifndef IPPROTO_UDP
#include <lwip/ip.h>
#define IPPROTO_UDP IP_PROTO_UDP
#endif

Maybe there is a better way but this works for me. (Also tested with esp-idf.)

if (!this->socket_) {
ESP_LOGW(TAG, "Failed to create UDP socket");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, loge, mark failed.

return;
}

this->log(ESPHOME_LOG_LEVEL_INFO , "syslog", "Syslog started");
ESP_LOGI(TAG, "Started");

Expand All @@ -53,21 +92,16 @@ void SyslogComponent::loop() {
void SyslogComponent::log(uint8_t level, const std::string &tag, const std::string &payload) {
level = level > 7 ? 7 : level;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do something like:

if (this->is_failed()) {
  // maybe log with ESP_LOGV or ESP_LOGVV to reduce spam since component is marked failed 
  return;
}


// 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);
}
}
Expand Down
15 changes: 4 additions & 11 deletions components/syslog/syslog_component.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,7 @@
#include "esphome/core/defines.h"
#include "esphome/core/automation.h"
#include "esphome/core/log.h"
#include <Syslog.h>
#include <Udp.h>

#if defined ESP8266 || defined ARDUINO_ESP8266_ESP01
#include <ESP8266WiFi.h>
#else
#include <WiFi.h>
#endif

#include <WiFiUdp.h>
#include "esphome/components/socket/socket.h"

namespace esphome {
namespace syslog {
Expand Down Expand Up @@ -51,7 +42,9 @@ class SyslogComponent : public Component {
bool strip_colors;
bool enable_logger;
SYSLOGSettings settings_;
UDP *udp_ = NULL;
std::unique_ptr<socket::Socket> socket_ = nullptr;
struct sockaddr_storage server;
socklen_t server_socklen;
};

template<typename... Ts> class SyslogLogAction : public Action<Ts...> {
Expand Down