Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 4 additions & 1 deletion wled00/bus_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ make_unique(Args&&... args)
#endif

// enable additional debug output
#if defined(WLED_DEBUG_HOST)
#if defined(WLED_ENABLE_SYSLOG)
#include "syslog.h"
#define DEBUGOUT Syslog
#elif defined(WLED_DEBUG_HOST)
#include "net_debug.h"
#define DEBUGOUT NetDebug
#else
Expand Down
20 changes: 20 additions & 0 deletions wled00/cfg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,16 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(hueIP[i], if_hue_ip[i]);
#endif

#ifdef WLED_ENABLE_SYSLOG
JsonObject if_syslog = interfaces["syslog"];
CJSON(syslogEnabled, if_syslog["en"]);
getStringFromJson(syslogHost, if_syslog[F("host")], 33);
CJSON(syslogPort, if_syslog["port"]);
CJSON(syslogProtocol, if_syslog["proto"]);
CJSON(syslogFacility, if_syslog["fac"]);
CJSON(syslogSeverity, if_syslog["sev"]);
#endif

JsonObject if_ntp = interfaces[F("ntp")];
CJSON(ntpEnabled, if_ntp["en"]);
getStringFromJson(ntpServerName, if_ntp[F("host")], 33); // "1.wled.pool.ntp.org"
Expand Down Expand Up @@ -1051,6 +1061,16 @@ void serializeConfig(JsonObject root) {
}
#endif

#ifdef WLED_ENABLE_SYSLOG
JsonObject if_syslog = interfaces.createNestedObject("syslog");
if_syslog["en"] = syslogEnabled;
if_syslog["host"] = syslogHost;
if_syslog["port"] = syslogPort;
if_syslog["proto"] = syslogProtocol;
if_syslog["fac"] = syslogFacility;
if_syslog["sev"] = syslogSeverity;
#endif

JsonObject if_ntp = interfaces.createNestedObject("ntp");
if_ntp["en"] = ntpEnabled;
if_ntp[F("host")] = ntpServerName;
Expand Down
53 changes: 53 additions & 0 deletions wled00/data/settings_sync.htm
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,59 @@ <h3>Serial</h3>
</select><br>
<i>Keep at 115200 to use Improv. Some boards may not support high rates.</i>
</div>
<h3>Syslog</h3>
Copy link
Collaborator

Choose a reason for hiding this comment

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

this will add size to all builds, not acceptable for a debug function IMHO

Copy link
Member

@netmindz netmindz Apr 26, 2025

Choose a reason for hiding this comment

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

Yeah I would agree that allowing the user to select the facility and level is overkill

Copy link
Author

Choose a reason for hiding this comment

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

this should solve that size problem when not using syslog:

#4664 (comment)

Copy link
Member

Choose a reason for hiding this comment

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

While that is an interesting approach to the problem, I still see no point in allowing the user to define nonsensical values. For example , WLED is not a printer, so what is the point of the user being able to select LPR as the facility.

Copy link
Author

Choose a reason for hiding this comment

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

Hi i just added the full list i found online. So which should i remove/keep? i read somewhere that SYSLOG_LOCAL4 (LOCAL4) is normaly used for user output, thats why i also set is as default.

// Syslog facility codes
#define SYSLOG_KERN     	0  // kernel messages
#define SYSLOG_USER     	1  // user-level messages
#define SYSLOG_MAIL     	2  // mail system
#define SYSLOG_DAEMON   	3  // system daemons
#define SYSLOG_AUTH     	4  // security/authorization messages
#define SYSLOG_SYSLOG   	5  // messages generated internally by syslogd
#define SYSLOG_LPR      	6  // line printer subsystem
#define SYSLOG_NEWS     	7  // network news subsystem
#define SYSLOG_UUCP     	8  // UUCP subsystem
#define SYSLOG_CRON     	9  // clock daemon
#define SYSLOG_AUTHPRIV 	10 // security/authorization messages (private)
#define SYSLOG_FTP      	11 // FTP daemon
#define SYSLOG_NTP      	12 // NTP subsystem (used in some systems)
#define SYSLOG_LOG_AUDIT 	13 // log audit (used in some systems like Linux auditd)
#define SYSLOG_LOG_ALERT 	14 // log alert
#define SYSLOG_CLOCK_DAEMON 15 // clock daemon (alternate)
#define SYSLOG_LOCAL0   	16 // local use 0
#define SYSLOG_LOCAL1   	17 // local use 1
#define SYSLOG_LOCAL2   	18 // local use 2
#define SYSLOG_LOCAL3   	19 // local use 3
#define SYSLOG_LOCAL4   	20 // local use 4
#define SYSLOG_LOCAL5   	21 // local use 5
#define SYSLOG_LOCAL6   	22 // local use 6
#define SYSLOG_LOCAL7   	23 // local use 7

i would think of least those ?

#define SYSLOG_USER     	1  // user-level messages
#define SYSLOG_DAEMON   	3  // system daemons
#define SYSLOG_LOCAL0   	16 // local use 0
#define SYSLOG_LOCAL1   	17 // local use 1
#define SYSLOG_LOCAL2   	18 // local use 2
#define SYSLOG_LOCAL3   	19 // local use 3
#define SYSLOG_LOCAL4   	20 // local use 4
#define SYSLOG_LOCAL5   	21 // local use 5
#define SYSLOG_LOCAL6   	22 // local use 6
#define SYSLOG_LOCAL7   	23 // local use 7

and maybe change th edefault to:

LOCAL0 is reserved for custom applications (i.e. it will never collide with kernel, mail, auth, etc.)

Copy link
Author

Choose a reason for hiding this comment

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

i removed the one i mentioned

<div id="NoSyslog" class="hide">
<em class="warn">This firmware build does not support Syslog interface.<br></em>
</div>
<div id="Syslog">
Enable Syslog: <input type="checkbox" name="SL_en"><br>
Host: <input type="text" name="SL_host" maxlength="32"><br>
Port: <input type="number" name="SL_port" min="1" max="65535" value="%SL_port%"><br>
Protocol:
<select name=SL_proto>
<option value="0">BSD (RFC3164)</option>
<option value="1">RFC5424</option>
<option value="2">Raw</option>
</select><br>
Facility:
<select name=SL_fac>
<option value="0">KERN</option>
<option value="1">USER</option>
<option value="2">MAIL</option>
<option value="3">DAEMON</option>
<option value="4">AUTH</option>
<option value="5">SYSLOG</option>
<option value="6">LPR</option>
<option value="7">NEWS</option>
<option value="8">UUCP</option>
<option value="9">CRON</option>
<option value="10">AUTHPRIV</option>
<option value="11">FTP</option>
<option value="12">NTP</option>
<option value="13">LOG_AUDIT</option>
<option value="14">LOG_ALERT</option>
<option value="15">CLOCK_DAEMON</option>
<option value="16">LOCAL0</option>
<option value="17">LOCAL1</option>
<option value="18">LOCAL2</option>
<option value="19">LOCAL3</option>
<option value="20">LOCAL4</option>
<option value="21">LOCAL5</option>
<option value="22">LOCAL6</option>
<option value="23">LOCAL7</option>
</select><br>
Severity:
<select name=SL_sev>
<option value="0">EMERG</option>
<option value="1">ALERT</option>
<option value="2">CRIT</option>
<option value="3">ERR</option>
<option value="4">WARNING</option>
<option value="5">NOTICE</option>
<option value="6">INFO</option>
<option value="7">DEBUG</option>
</select><br>
</div>
<hr>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
</form>
Expand Down
8 changes: 7 additions & 1 deletion wled00/json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,10 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
bool stateResponse = root[F("v")] | false;

#if defined(WLED_DEBUG) && defined(WLED_DEBUG_HOST)
netDebugEnabled = root[F("debug")] | netDebugEnabled;
netDebugEnabled = root[F("debug")] | netDebugEnabled;
#elif defined(WLED_DEBUG) && defined(WLED_ENABLE_SYSLOG)
syslogEnabled = root[F("debug")] | syslogEnabled;
DEBUG_PRINTF_P(PSTR("Syslog: %s\n"), syslogEnabled ? PSTR("ENABLED") : PSTR("DISABLED") );
#endif

bool onBefore = bri;
Expand Down Expand Up @@ -781,6 +784,9 @@ void serializeInfo(JsonObject root)
#ifdef WLED_DEBUG_HOST
os |= 0x0100;
if (!netDebugEnabled) os &= ~0x0080;
#elif defined(WLED_ENABLE_SYSLOG)
os |= 0x0100;
if (!syslogEnabled) os &= ~0x0080;
#endif
#endif
#ifndef WLED_DISABLE_ALEXA
Expand Down
22 changes: 22 additions & 0 deletions wled00/set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,28 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
t = request->arg(F("BD")).toInt();
if (t >= 96 && t <= 15000) serialBaud = t;
updateBaudRate(serialBaud *100);

#ifdef WLED_ENABLE_SYSLOG
syslogEnabled = request->hasArg(F("SL_en"));
strlcpy(syslogHost, request->arg(F("SL_host")).c_str(), sizeof(syslogHost));

t = request->arg(F("SL_port")).toInt();
if (t > 0) syslogPort = t;

t = request->arg(F("SL_proto")).toInt();
if (t >= SYSLOG_PROTO_BSD && t <= SYSLOG_PROTO_RAW) syslogProtocol = t;

t = request->arg(F("SL_fac")).toInt();
if (t >= SYSLOG_KERN && t <= SYSLOG_LOCAL7) syslogFacility = t;

t = request->arg(F("SL_sev")).toInt();
if (t >= SYSLOG_EMERG && t <= SYSLOG_DEBUG) syslogSeverity = t;

Syslog.begin(syslogHost, syslogPort,
syslogFacility, syslogSeverity, syslogProtocol);

Syslog.setAppName("WLED");
#endif
}

//TIME
Expand Down
200 changes: 200 additions & 0 deletions wled00/syslog.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
#include "wled.h"
#ifdef WLED_ENABLE_SYSLOG

#include "syslog.h"

static const __FlashStringHelper* protoNames[] = { F("BSD"), F("RFC5424"), F("RAW") };
static const char* facilityNames[] = {
"KERN", "USER", "MAIL", "DAEM",
"AUTH", "SYSL", "LPR", "NEWS",
"UUCP", "CRON", "APRV", "FTP",
"NTP", "AUDT", "ALRT", "CLCK",
"LCL0", "LCL1", "LCL2", "LCL3",
"LCL4", "LCL5", "LCL6", "LCL7"
};

static const char* severityNames[] = {
"EMERG","ALERT","CRIT","ERR","WARNING","NOTICE","INFO","DEBUG"
};

SyslogPrinter::SyslogPrinter() :
_facility(SYSLOG_LOCAL4),
_severity(SYSLOG_DEBUG),
_protocol(SYSLOG_PROTO_BSD),
_appName("WLED"),
_bufferIndex(0) {}

void SyslogPrinter::begin(const char* host, uint16_t port,
uint8_t facility, uint8_t severity, uint8_t protocol) {

DEBUG_PRINTF_P(PSTR("===== WLED SYSLOG CONFIGURATION =====\n"));
DEBUG_PRINTF_P(PSTR(" Hostname: %s\n"), syslogHost);
DEBUG_PRINTF_P(PSTR(" Cached IP: %s\n"), syslogHostIP.toString().c_str());
DEBUG_PRINTF_P(PSTR(" Port: %u\n"), (unsigned)syslogPort);
DEBUG_PRINTF_P(PSTR(" Protocol: %u (%s)\n"),
protocol,
protocol <= 2 ? (const char*)protoNames[protocol] : "UNKNOWN"
);
DEBUG_PRINTF_P(PSTR(" Facility: %u (%s)\n"),
(unsigned)facility,
(facility < sizeof(facilityNames)/sizeof(facilityNames[0]))
? facilityNames[facility]
: PSTR("UNKNOWN"));
DEBUG_PRINTF_P(PSTR(" Severity: %u (%s)\n"),
(unsigned)severity,
(severity < sizeof(severityNames)/sizeof(severityNames[0]))
? severityNames[severity]
: PSTR("UNKNOWN"));
DEBUG_PRINTF_P(PSTR("======================================\n"));

strlcpy(syslogHost, host, sizeof(syslogHost));
syslogPort = port;
_facility = facility;
_severity = severity;
_protocol = protocol;

// clear any cached IP so resolveHostname() will run next write()
syslogHostIP = IPAddress(0,0,0,0);
}

void SyslogPrinter::setAppName(const String &appName) {
_appName = appName;
}

bool SyslogPrinter::resolveHostname() {
if (!WLED_CONNECTED || !syslogEnabled) return false;

// If we already have an IP or can parse the hostname as an IP, use that
if (syslogHostIP || syslogHostIP.fromString(syslogHost)) {
return true;
}

// Otherwise resolve the hostname
#ifdef ESP8266
WiFi.hostByName(syslogHost, syslogHostIP, 750);
#else
#ifdef WLED_USE_ETHERNET
ETH.hostByName(syslogHost, syslogHostIP);
#else
WiFi.hostByName(syslogHost, syslogHostIP);
#endif
#endif

return syslogHostIP != IPAddress(0, 0, 0, 0);
}

void SyslogPrinter::flushBuffer() {
if (_bufferIndex == 0) return;

// Trim any trailing CR so syslog server won’t show “#015”
if (_bufferIndex > 0 && _buffer[_bufferIndex-1] == '\r') _bufferIndex--;

// Null-terminate
_buffer[_bufferIndex] = '\0';

// Send the buffer with default severity
write((const uint8_t*)_buffer, _bufferIndex, _severity);

// Reset buffer index
_bufferIndex = 0;
}

size_t SyslogPrinter::write(uint8_t c) {
// Store in buffer regardless of connection status
if (_bufferIndex < sizeof(_buffer) - 1) {
_buffer[_bufferIndex++] = c;
}

// If newline or buffer full, flush
if (c == '\n' || _bufferIndex >= sizeof(_buffer) - 1) {
flushBuffer();
}

return 1;
}

size_t SyslogPrinter::write(const uint8_t *buf, size_t size) {
return write(buf, size, _severity);
}

size_t SyslogPrinter::write(const uint8_t *buf, size_t size, uint8_t severity) {
if (!WLED_CONNECTED || buf == nullptr || !syslogEnabled) return 0;
if (!resolveHostname()) return 0;

syslogUdp.beginPacket(syslogHostIP, syslogPort);

// Calculate priority value
uint8_t pri = (_facility << 3) | severity;

// Handle different syslog protocol formats
switch (_protocol) {
case SYSLOG_PROTO_BSD:
// RFC 3164 format: <PRI>TIMESTAMP HOSTNAME APP-NAME: MESSAGE
syslogUdp.printf("<%d>", pri);

if (ntpEnabled && ntpConnected) {
// Month abbreviation
const char* months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

syslogUdp.printf("%s %2d %02d:%02d:%02d ",
months[month(localTime) - 1],
day(localTime),
hour(localTime),
minute(localTime),
second(localTime));
} else {
// No valid time available
syslogUdp.print("Jan 01 00:00:00 ");
}

// Add hostname and app name
syslogUdp.print(serverDescription);
syslogUdp.print(" ");
syslogUdp.print(_appName);
syslogUdp.print(": ");

// Add message content
size = syslogUdp.write(buf, size);
break;

case SYSLOG_PROTO_RFC5424:
// RFC 5424 format: <PRI>VERSION TIMESTAMP HOSTNAME APP-NAME PROCID MSGID STRUCTURED-DATA MSG
syslogUdp.printf("<%d>1 ", pri); // Version is always 1

if (ntpEnabled && ntpConnected) {
syslogUdp.printf("%04d-%02d-%02dT%02d:%02d:%02dZ ",
year(localTime),
month(localTime),
day(localTime),
hour(localTime),
minute(localTime),
second(localTime));
} else {
// No valid time available
syslogUdp.print("1970-01-01T00:00:00Z ");
}

// Add hostname, app name, and other fields (using - for empty fields)
syslogUdp.print(serverDescription);
syslogUdp.print(" ");
syslogUdp.print(_appName);
syslogUdp.print(" - - - "); // PROCID, MSGID, and STRUCTURED-DATA are empty

// Add message content
size = syslogUdp.write(buf, size);
break;

case SYSLOG_PROTO_RAW:
default:
// Just send the raw message (like original NetDebug)
size = syslogUdp.write(buf, size);
break;
}

syslogUdp.endPacket();
return size;
}

SyslogPrinter Syslog;
#endif // WLED_ENABLE_SYSLOG
Loading