1+ #include " wled.h"
2+ #ifdef WLED_ENABLE_SYSLOG
3+
4+ #include " syslog.h"
5+
6+ static const __FlashStringHelper* protoNames[] = { F (" BSD" ), F (" RFC5424" ), F (" RAW" ) };
7+ static const char * facilityNames[] = {
8+ " KERN" , " USER" , " MAIL" , " DAEM" ,
9+ " AUTH" , " SYSL" , " LPR" , " NEWS" ,
10+ " UUCP" , " CRON" , " APRV" , " FTP" ,
11+ " NTP" , " AUDT" , " ALRT" , " CLCK" ,
12+ " LCL0" , " LCL1" , " LCL2" , " LCL3" ,
13+ " LCL4" , " LCL5" , " LCL6" , " LCL7"
14+ };
15+
16+ static const char * severityNames[] = {
17+ " EMERG" ," ALERT" ," CRIT" ," ERR" ," WARNING" ," NOTICE" ," INFO" ," DEBUG"
18+ };
19+
20+ SyslogPrinter::SyslogPrinter () :
21+ _facility(SYSLOG_LOCAL4),
22+ _severity(SYSLOG_DEBUG),
23+ _protocol(SYSLOG_PROTO_BSD),
24+ _appName(" WLED" ),
25+ _bufferIndex(0 ) {}
26+
27+ void SyslogPrinter::begin (const char * host, uint16_t port,
28+ uint8_t facility, uint8_t severity, uint8_t protocol) {
29+
30+ DEBUG_PRINTF_P (PSTR (" ===== WLED SYSLOG CONFIGURATION =====\n " ));
31+ DEBUG_PRINTF_P (PSTR (" Hostname: %s\n " ), syslogHost);
32+ DEBUG_PRINTF_P (PSTR (" Cached IP: %s\n " ), syslogHostIP.toString ().c_str ());
33+ DEBUG_PRINTF_P (PSTR (" Port: %u\n " ), (unsigned )syslogPort);
34+ DEBUG_PRINTF_P (PSTR (" Protocol: %u (%s)\n " ),
35+ protocol,
36+ protocol <= 2 ? (const char *)protoNames[protocol] : " UNKNOWN"
37+ );
38+ DEBUG_PRINTF_P (PSTR (" Facility: %u (%s)\n " ),
39+ (unsigned )facility,
40+ (facility < sizeof (facilityNames)/sizeof (facilityNames[0 ]))
41+ ? facilityNames[facility]
42+ : PSTR (" UNKNOWN" ));
43+ DEBUG_PRINTF_P (PSTR (" Severity: %u (%s)\n " ),
44+ (unsigned )severity,
45+ (severity < sizeof (severityNames)/sizeof (severityNames[0 ]))
46+ ? severityNames[severity]
47+ : PSTR (" UNKNOWN" ));
48+ DEBUG_PRINTF_P (PSTR (" ======================================\n " ));
49+
50+ strlcpy (syslogHost, host, sizeof (syslogHost));
51+ syslogPort = port;
52+ _facility = facility;
53+ _severity = severity;
54+ _protocol = protocol;
55+
56+ // clear any cached IP so resolveHostname() will run next write()
57+ syslogHostIP = IPAddress (0 ,0 ,0 ,0 );
58+ }
59+
60+ void SyslogPrinter::setAppName (const String &appName) {
61+ _appName = appName;
62+ }
63+
64+ bool SyslogPrinter::resolveHostname () {
65+ if (!WLED_CONNECTED || !syslogEnabled) return false ;
66+
67+ // If we already have an IP or can parse the hostname as an IP, use that
68+ if (syslogHostIP || syslogHostIP.fromString (syslogHost)) {
69+ return true ;
70+ }
71+
72+ // Otherwise resolve the hostname
73+ #ifdef ESP8266
74+ WiFi.hostByName (syslogHost, syslogHostIP, 750 );
75+ #else
76+ #ifdef WLED_USE_ETHERNET
77+ ETH.hostByName (syslogHost, syslogHostIP);
78+ #else
79+ WiFi.hostByName (syslogHost, syslogHostIP);
80+ #endif
81+ #endif
82+
83+ return syslogHostIP != IPAddress (0 , 0 , 0 , 0 );
84+ }
85+
86+ void SyslogPrinter::flushBuffer () {
87+ if (_bufferIndex == 0 ) return ;
88+
89+ // Trim any trailing CR so syslog server won’t show “#015”
90+ if (_bufferIndex > 0 && _buffer[_bufferIndex-1 ] == ' \r ' ) _bufferIndex--;
91+
92+ // Null-terminate
93+ _buffer[_bufferIndex] = ' \0 ' ;
94+
95+ // Send the buffer with default severity
96+ write ((const uint8_t *)_buffer, _bufferIndex, _severity);
97+
98+ // Reset buffer index
99+ _bufferIndex = 0 ;
100+ }
101+
102+ size_t SyslogPrinter::write (uint8_t c) {
103+ // Store in buffer regardless of connection status
104+ if (_bufferIndex < sizeof (_buffer) - 1 ) {
105+ _buffer[_bufferIndex++] = c;
106+ }
107+
108+ // If newline or buffer full, flush
109+ if (c == ' \n ' || _bufferIndex >= sizeof (_buffer) - 1 ) {
110+ flushBuffer ();
111+ }
112+
113+ return 1 ;
114+ }
115+
116+ size_t SyslogPrinter::write (const uint8_t *buf, size_t size) {
117+ return write (buf, size, _severity);
118+ }
119+
120+ size_t SyslogPrinter::write (const uint8_t *buf, size_t size, uint8_t severity) {
121+ if (!WLED_CONNECTED || buf == nullptr || !syslogEnabled) return 0 ;
122+ if (!resolveHostname ()) return 0 ;
123+
124+ syslogUdp.beginPacket (syslogHostIP, syslogPort);
125+
126+ // Calculate priority value
127+ uint8_t pri = (_facility << 3 ) | severity;
128+
129+ // Handle different syslog protocol formats
130+ switch (_protocol) {
131+ case SYSLOG_PROTO_BSD:
132+ // RFC 3164 format: <PRI>TIMESTAMP HOSTNAME APP-NAME: MESSAGE
133+ syslogUdp.printf (" <%d>" , pri);
134+
135+ if (ntpEnabled && ntpConnected) {
136+ // Month abbreviation
137+ const char * months[] = {" Jan" , " Feb" , " Mar" , " Apr" , " May" , " Jun" ,
138+ " Jul" , " Aug" , " Sep" , " Oct" , " Nov" , " Dec" };
139+
140+ syslogUdp.printf (" %s %2d %02d:%02d:%02d " ,
141+ months[month (localTime) - 1 ],
142+ day (localTime),
143+ hour (localTime),
144+ minute (localTime),
145+ second (localTime));
146+ } else {
147+ // No valid time available
148+ syslogUdp.print (" Jan 01 00:00:00 " );
149+ }
150+
151+ // Add hostname and app name
152+ syslogUdp.print (serverDescription);
153+ syslogUdp.print (" " );
154+ syslogUdp.print (_appName);
155+ syslogUdp.print (" : " );
156+
157+ // Add message content
158+ size = syslogUdp.write (buf, size);
159+ break ;
160+
161+ case SYSLOG_PROTO_RFC5424:
162+ // RFC 5424 format: <PRI>VERSION TIMESTAMP HOSTNAME APP-NAME PROCID MSGID STRUCTURED-DATA MSG
163+ syslogUdp.printf (" <%d>1 " , pri); // Version is always 1
164+
165+ if (ntpEnabled && ntpConnected) {
166+ syslogUdp.printf (" %04d-%02d-%02dT%02d:%02d:%02dZ " ,
167+ year (localTime),
168+ month (localTime),
169+ day (localTime),
170+ hour (localTime),
171+ minute (localTime),
172+ second (localTime));
173+ } else {
174+ // No valid time available
175+ syslogUdp.print (" 1970-01-01T00:00:00Z " );
176+ }
177+
178+ // Add hostname, app name, and other fields (using - for empty fields)
179+ syslogUdp.print (serverDescription);
180+ syslogUdp.print (" " );
181+ syslogUdp.print (_appName);
182+ syslogUdp.print (" - - - " ); // PROCID, MSGID, and STRUCTURED-DATA are empty
183+
184+ // Add message content
185+ size = syslogUdp.write (buf, size);
186+ break ;
187+
188+ case SYSLOG_PROTO_RAW:
189+ default :
190+ // Just send the raw message (like original NetDebug)
191+ size = syslogUdp.write (buf, size);
192+ break ;
193+ }
194+
195+ syslogUdp.endPacket ();
196+ return size;
197+ }
198+
199+ SyslogPrinter Syslog;
200+ #endif // WLED_ENABLE_SYSLOG
0 commit comments