Feature: Add syslog logger
This implements RFC5424 version of the protocol. Doesn't use https://github.com/arcao/Syslog since the protocol itself is trivial and most of the libraries functionality is not needed here. The library also doesn't support setting the PROCID field, which is set to a random id to indicate a reboot here. Adds UI for syslog configuration to network admin view.
This commit is contained in:
parent
dc588dce07
commit
03010027eb
@ -81,7 +81,7 @@ struct CONFIG_T {
|
|||||||
struct {
|
struct {
|
||||||
bool Enabled;
|
bool Enabled;
|
||||||
char Hostname[SYSLOG_MAX_HOSTNAME_STRLEN + 1];
|
char Hostname[SYSLOG_MAX_HOSTNAME_STRLEN + 1];
|
||||||
uint32_t Port;
|
uint16_t Port;
|
||||||
} Syslog;
|
} Syslog;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|||||||
34
include/SyslogLogger.h
Normal file
34
include/SyslogLogger.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
#include <WiFiUdp.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
class SyslogLogger {
|
||||||
|
public:
|
||||||
|
SyslogLogger();
|
||||||
|
void init(Scheduler& scheduler);
|
||||||
|
void updateSettings(const String&& hostname);
|
||||||
|
void write(const uint8_t *buffer, size_t size);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void loop();
|
||||||
|
void disable();
|
||||||
|
void enable();
|
||||||
|
bool resolveAndStart();
|
||||||
|
bool isResolved() const {
|
||||||
|
return _address != INADDR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Task _loopTask;
|
||||||
|
std::mutex _mutex;
|
||||||
|
WiFiUDP _udp;
|
||||||
|
IPAddress _address;
|
||||||
|
String _syslog_hostname;
|
||||||
|
String _proc_id;
|
||||||
|
String _header;
|
||||||
|
uint16_t _port;
|
||||||
|
bool _enabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern SyslogLogger Syslog;
|
||||||
@ -3,6 +3,7 @@
|
|||||||
* Copyright (C) 2022-2024 Thomas Basler and others
|
* Copyright (C) 2022-2024 Thomas Basler and others
|
||||||
*/
|
*/
|
||||||
#include "MessageOutput.h"
|
#include "MessageOutput.h"
|
||||||
|
#include "SyslogLogger.h"
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
@ -26,6 +27,8 @@ void MessageOutputClass::register_ws_output(AsyncWebSocket* output)
|
|||||||
|
|
||||||
size_t MessageOutputClass::write(uint8_t c)
|
size_t MessageOutputClass::write(uint8_t c)
|
||||||
{
|
{
|
||||||
|
Syslog.write(&c, 1);
|
||||||
|
|
||||||
if (_buff_pos < BUFFER_SIZE) {
|
if (_buff_pos < BUFFER_SIZE) {
|
||||||
std::lock_guard<std::mutex> lock(_msgLock);
|
std::lock_guard<std::mutex> lock(_msgLock);
|
||||||
_buffer[_buff_pos] = c;
|
_buffer[_buff_pos] = c;
|
||||||
@ -39,6 +42,8 @@ size_t MessageOutputClass::write(uint8_t c)
|
|||||||
|
|
||||||
size_t MessageOutputClass::write(const uint8_t* buffer, size_t size)
|
size_t MessageOutputClass::write(const uint8_t* buffer, size_t size)
|
||||||
{
|
{
|
||||||
|
Syslog.write(buffer, size);
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(_msgLock);
|
std::lock_guard<std::mutex> lock(_msgLock);
|
||||||
if (_buff_pos + size < BUFFER_SIZE) {
|
if (_buff_pos + size < BUFFER_SIZE) {
|
||||||
memcpy(&_buffer[_buff_pos], buffer, size);
|
memcpy(&_buffer[_buff_pos], buffer, size);
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
#include "NetworkSettings.h"
|
#include "NetworkSettings.h"
|
||||||
#include "Configuration.h"
|
#include "Configuration.h"
|
||||||
#include "MessageOutput.h"
|
#include "MessageOutput.h"
|
||||||
|
#include "SyslogLogger.h"
|
||||||
#include "PinMapping.h"
|
#include "PinMapping.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "__compiled_constants.h"
|
#include "__compiled_constants.h"
|
||||||
@ -52,6 +53,8 @@ void NetworkSettingsClass::init(Scheduler& scheduler)
|
|||||||
|
|
||||||
scheduler.addTask(_loopTask);
|
scheduler.addTask(_loopTask);
|
||||||
_loopTask.enable();
|
_loopTask.enable();
|
||||||
|
|
||||||
|
Syslog.init(scheduler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkSettingsClass::NetworkEvent(const WiFiEvent_t event, WiFiEventInfo_t info)
|
void NetworkSettingsClass::NetworkEvent(const WiFiEvent_t event, WiFiEventInfo_t info)
|
||||||
@ -292,6 +295,8 @@ void NetworkSettingsClass::applyConfig()
|
|||||||
}
|
}
|
||||||
MessageOutput.println("done");
|
MessageOutput.println("done");
|
||||||
setStaticIp();
|
setStaticIp();
|
||||||
|
|
||||||
|
Syslog.updateSettings(getHostname());
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkSettingsClass::setHostname()
|
void NetworkSettingsClass::setHostname()
|
||||||
|
|||||||
138
src/SyslogLogger.cpp
Normal file
138
src/SyslogLogger.cpp
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022-2024 Thomas Basler and others
|
||||||
|
*/
|
||||||
|
#include <HardwareSerial.h>
|
||||||
|
#include <ESPmDNS.h>
|
||||||
|
#include "defaults.h"
|
||||||
|
#include "SyslogLogger.h"
|
||||||
|
#include "Configuration.h"
|
||||||
|
#include "MessageOutput.h"
|
||||||
|
#include "NetworkSettings.h"
|
||||||
|
|
||||||
|
SyslogLogger::SyslogLogger()
|
||||||
|
: _loopTask(TASK_IMMEDIATE, TASK_FOREVER, std::bind(&SyslogLogger::loop, this))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyslogLogger::init(Scheduler& scheduler)
|
||||||
|
{
|
||||||
|
// PROCID change indicates a restart.
|
||||||
|
_proc_id = String(esp_random(), HEX);
|
||||||
|
|
||||||
|
scheduler.addTask(_loopTask);
|
||||||
|
_loopTask.enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyslogLogger::updateSettings(const String&& hostname)
|
||||||
|
{
|
||||||
|
auto& config = Configuration.get().Syslog;
|
||||||
|
|
||||||
|
// Disable logger while it is reconfigured.
|
||||||
|
disable();
|
||||||
|
|
||||||
|
if (!config.Enabled) {
|
||||||
|
MessageOutput.println("[SyslogLogger] Syslog not enabled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_port = config.Port;
|
||||||
|
_syslog_hostname = config.Hostname;
|
||||||
|
if (_syslog_hostname.isEmpty()) {
|
||||||
|
MessageOutput.println("[SyslogLogger] Hostname not configured");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageOutput.printf("[SyslogLogger] Logging to %s!\r\n", _syslog_hostname.c_str());
|
||||||
|
|
||||||
|
_header = "<14>1 - "; // RFC5424: Facility USER, severity INFO, version 1, NIL timestamp.
|
||||||
|
_header += hostname;
|
||||||
|
_header += " OpenDTU ";
|
||||||
|
_header += _proc_id;
|
||||||
|
// NIL values for message id and structured data
|
||||||
|
_header += " - - ";
|
||||||
|
|
||||||
|
// Enable logger.
|
||||||
|
enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyslogLogger::write(const uint8_t *buffer, size_t size)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
if (!_enabled || !isResolved()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
uint8_t c = buffer[i];
|
||||||
|
bool overflow = false;
|
||||||
|
if (c != '\r' && c != '\n') {
|
||||||
|
// Replace control and non-ASCII characters with '?'.
|
||||||
|
overflow = !_udp.write(c >= 0x20 && c < 0x7f ? c : '?');
|
||||||
|
}
|
||||||
|
if (c == '\n' || overflow) {
|
||||||
|
_udp.endPacket();
|
||||||
|
_udp.beginPacket(_address, _port);
|
||||||
|
_udp.print(_header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyslogLogger::disable()
|
||||||
|
{
|
||||||
|
MessageOutput.println("[SyslogLogger] Disable");
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
if (_enabled) {
|
||||||
|
_enabled = false;
|
||||||
|
_address = INADDR_NONE;
|
||||||
|
_udp.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyslogLogger::enable()
|
||||||
|
{
|
||||||
|
// Bind random source port.
|
||||||
|
if (!_udp.begin(0)) {
|
||||||
|
MessageOutput.println("[SyslogLogger] No sockets available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
_enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SyslogLogger::resolveAndStart()
|
||||||
|
{
|
||||||
|
if (Configuration.get().Mdns.Enabled) {
|
||||||
|
_address = MDNS.queryHost(_syslog_hostname); // INADDR_NONE if failed
|
||||||
|
}
|
||||||
|
if (_address != INADDR_NONE) {
|
||||||
|
if (!_udp.beginPacket(_address, _port)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!_udp.beginPacket(_syslog_hostname.c_str(), _port)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_address = _udp.remoteIP(); // Store resolved address.
|
||||||
|
}
|
||||||
|
_udp.print(_header);
|
||||||
|
_udp.print("[SyslogLogger] Logging to ");
|
||||||
|
_udp.print(_syslog_hostname);
|
||||||
|
_udp.endPacket();
|
||||||
|
_udp.beginPacket(_address, _port);
|
||||||
|
_udp.print(_header);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SyslogLogger::loop()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
if (!_enabled || !NetworkSettings.isConnected() || isResolved()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!resolveAndStart()) {
|
||||||
|
_enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SyslogLogger Syslog;
|
||||||
Loading…
Reference in New Issue
Block a user