Skip to content

Commit 6a1f1eb

Browse files
committed
Add syslog logger implementation
This implements RFC5424 version of the protocol. See #1819
1 parent 74e6f05 commit 6a1f1eb

File tree

5 files changed

+183
-0
lines changed

5 files changed

+183
-0
lines changed

include/SyslogLogger.h

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
#pragma once
3+
#include <WiFiUdp.h>
4+
#include <TaskSchedulerDeclarations.h>
5+
#include <mutex>
6+
7+
class SyslogLogger {
8+
public:
9+
SyslogLogger();
10+
void init(Scheduler& scheduler);
11+
void updateSettings(const String&& hostname);
12+
void write(const uint8_t *buffer, size_t size);
13+
14+
private:
15+
void loop();
16+
void disable();
17+
void enable();
18+
bool resolveAndStart();
19+
bool isResolved() const {
20+
return _address != INADDR_NONE;
21+
}
22+
23+
Task _loopTask;
24+
std::mutex _mutex;
25+
WiFiUDP _udp;
26+
IPAddress _address;
27+
String _syslog_hostname;
28+
String _my_hostname;
29+
String _proc_id;
30+
String _header;
31+
int _port;
32+
bool _enabled;
33+
};
34+
35+
extern SyslogLogger Syslog;

include/defaults.h

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#define SYSLOG_ENABLED false
2626
#define SYSLOG_PORT 514
27+
#define SYSLOG_APPNAME "OpenDTU"
2728

2829
#define NTP_SERVER_OLD "pool.ntp.org"
2930
#define NTP_SERVER "opendtu.pool.ntp.org"

src/MessageOutput.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Copyright (C) 2022-2024 Thomas Basler and others
44
*/
55
#include "MessageOutput.h"
6+
#include "SyslogLogger.h"
67

78
#include <Arduino.h>
89

@@ -26,6 +27,8 @@ void MessageOutputClass::register_ws_output(AsyncWebSocket* output)
2627

2728
size_t MessageOutputClass::write(uint8_t c)
2829
{
30+
Syslog.write(&c, 1);
31+
2932
if (_buff_pos < BUFFER_SIZE) {
3033
std::lock_guard<std::mutex> lock(_msgLock);
3134
_buffer[_buff_pos] = c;
@@ -39,6 +42,8 @@ size_t MessageOutputClass::write(uint8_t c)
3942

4043
size_t MessageOutputClass::write(const uint8_t* buffer, size_t size)
4144
{
45+
Syslog.write(buffer, size);
46+
4247
std::lock_guard<std::mutex> lock(_msgLock);
4348
if (_buff_pos + size < BUFFER_SIZE) {
4449
memcpy(&_buffer[_buff_pos], buffer, size);

src/NetworkSettings.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "NetworkSettings.h"
66
#include "Configuration.h"
77
#include "MessageOutput.h"
8+
#include "SyslogLogger.h"
89
#include "PinMapping.h"
910
#include "Utils.h"
1011
#include "defaults.h"
@@ -34,6 +35,8 @@ void NetworkSettingsClass::init(Scheduler& scheduler)
3435

3536
scheduler.addTask(_loopTask);
3637
_loopTask.enable();
38+
39+
Syslog.init(scheduler);
3740
}
3841

3942
void NetworkSettingsClass::NetworkEvent(const WiFiEvent_t event)
@@ -279,6 +282,8 @@ void NetworkSettingsClass::applyConfig()
279282
}
280283
MessageOutput.println("done");
281284
setStaticIp();
285+
286+
Syslog.updateSettings(getHostname());
282287
}
283288

284289
void NetworkSettingsClass::setHostname()

src/SyslogLogger.cpp

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Copyright (C) 2022-2024 Thomas Basler and others
4+
*/
5+
#include <HardwareSerial.h>
6+
#include <ESPmDNS.h>
7+
#include "defaults.h"
8+
#include "SyslogLogger.h"
9+
#include "Configuration.h"
10+
#include "MessageOutput.h"
11+
#include "NetworkSettings.h"
12+
13+
SyslogLogger::SyslogLogger()
14+
: _loopTask(TASK_IMMEDIATE, TASK_FOREVER, std::bind(&SyslogLogger::loop, this))
15+
{
16+
}
17+
18+
void SyslogLogger::init(Scheduler& scheduler)
19+
{
20+
// PROCID change indicates a restart.
21+
_proc_id = String(esp_random(), HEX);
22+
23+
scheduler.addTask(_loopTask);
24+
_loopTask.enable();
25+
}
26+
27+
void SyslogLogger::updateSettings(const String&& hostname)
28+
{
29+
auto config = Configuration.get().Syslog;
30+
31+
// Disable logger while it is reconfigured.
32+
disable();
33+
34+
if (!config.Enabled) {
35+
MessageOutput.println("[SyslogLogger] Syslog not enabled");
36+
return;
37+
}
38+
39+
_port = config.Port;
40+
_my_hostname = hostname;
41+
_syslog_hostname = config.Hostname;
42+
if (_syslog_hostname.isEmpty()) {
43+
MessageOutput.println("[SyslogLogger] Hostname not configured");
44+
return;
45+
}
46+
47+
MessageOutput.printf("[SyslogLogger] Logging to %s!\r\n", _syslog_hostname.c_str());
48+
49+
_header = "<7>1 - "; // RFC5424: Facility KERNEL, severity DEBUG, version 1, NIL timestamp.
50+
_header += _my_hostname;
51+
_header += " " SYSLOG_APPNAME " ";
52+
_header += _proc_id;
53+
// NIL values for message id and structured // data; utf-8 BOM.
54+
_header += " - - \xEF\xBB\xBF";
55+
56+
// Enable logger.
57+
enable();
58+
}
59+
60+
void SyslogLogger::write(const uint8_t *buffer, size_t size)
61+
{
62+
std::lock_guard<std::mutex> lock(_mutex);
63+
if (!_enabled || !isResolved()) {
64+
return;
65+
}
66+
for (int i = 0; i < size; i++) {
67+
uint8_t c = buffer[i];
68+
bool overflow = false;
69+
if (c >= 0x20) {
70+
overflow = !_udp.write(c);
71+
}
72+
if (c == '\n' || overflow) {
73+
_udp.endPacket();
74+
_udp.beginPacket(_address, _port);
75+
_udp.print(_header);
76+
}
77+
}
78+
}
79+
80+
void SyslogLogger::disable()
81+
{
82+
std::lock_guard<std::mutex> lock(_mutex);
83+
if (_enabled) {
84+
_enabled = false;
85+
_address = INADDR_NONE;
86+
_udp.stop();
87+
}
88+
}
89+
90+
void SyslogLogger::enable()
91+
{
92+
// Bind random source port.
93+
if (!_udp.begin(0)) {
94+
MessageOutput.println("[SyslogLogger] No sockets available");
95+
return;
96+
}
97+
98+
std::lock_guard<std::mutex> lock(_mutex);
99+
_enabled = true;
100+
}
101+
102+
bool SyslogLogger::resolveAndStart()
103+
{
104+
if (Configuration.get().Mdns.Enabled) {
105+
_address = MDNS.queryHost(_syslog_hostname); // INADDR_NONE if failed
106+
}
107+
if (_address != INADDR_NONE) {
108+
if (!_udp.beginPacket(_address, _port)) {
109+
return false;
110+
}
111+
} else {
112+
if (!_udp.beginPacket(_syslog_hostname.c_str(), _port)) {
113+
return false;
114+
}
115+
_address = _udp.remoteIP(); // Store resolved address.
116+
}
117+
_udp.print(_header);
118+
_udp.print("[SyslogLogger] Logging to ");
119+
_udp.print(_syslog_hostname);
120+
_udp.endPacket();
121+
_udp.beginPacket(_address, _port);
122+
_udp.print(_header);
123+
return true;
124+
}
125+
126+
void SyslogLogger::loop()
127+
{
128+
std::lock_guard<std::mutex> lock(_mutex);
129+
if (!_enabled || !NetworkSettings.isConnected() || isResolved()) {
130+
return;
131+
}
132+
if (!resolveAndStart()) {
133+
_enabled = false;
134+
}
135+
}
136+
137+
SyslogLogger Syslog;

0 commit comments

Comments
 (0)