Use esphome own syslog component instead of custom

This commit is contained in:
2026-01-05 21:05:18 +00:00
parent d4855e2f1c
commit 96ddd324f8
4 changed files with 13 additions and 335 deletions
-59
View File
@@ -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
-173
View File
@@ -1,173 +0,0 @@
#include "syslog.h"
#ifdef USE_NETWORK
#include <sstream>
#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<sockaddr *>(&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:
// <PRI>VERSION SP TIMESTAMP SP HOSTNAME SP APP-NAME SP PROCID SP MSGID SP
// [SD-ID]s SP MSG
payload_stream << "<" << static_cast<int>(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<sockaddr *>(&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
-98
View File
@@ -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<esphome::socket::Socket> 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 <typename... Ts>
class SyslogLogAction : public Action<Ts...> {
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
+13 -5
View File
@@ -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.