Merge pull request #2168 from meshtastic/rsyslog-client

Add RSYSLOG Support to TCP Firmware
This commit is contained in:
Thomas Göttgens 2023-02-07 01:58:45 +01:00 committed by GitHub
commit 23e1c0b7a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 438 additions and 22 deletions

@ -1 +1 @@
Subproject commit 384b664a759592d9393642ba98835f69bb8f2fb2
Subproject commit 2b391ff8a76bfc07662faa007bb4fd6e01691ba8

172
src/DebugConfiguration.cpp Normal file
View File

@ -0,0 +1,172 @@
/* based on https://github.com/arcao/Syslog
MIT License
Copyright (c) 2016 Martin Sloup
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.*/
#include "configuration.h"
#include "DebugConfiguration.h"
#if HAS_WIFI || HAS_ETHERNET
Syslog::Syslog(UDP &client)
{
this->_client = &client;
this->_server = NULL;
this->_port = 0;
this->_deviceHostname = SYSLOG_NILVALUE;
this->_appName = SYSLOG_NILVALUE;
this->_priDefault = LOGLEVEL_KERN;
}
Syslog &Syslog::server(const char *server, uint16_t port)
{
if (this->_ip.fromString(server)) {
this->_server = NULL;
} else {
this->_server = server;
}
this->_port = port;
return *this;
}
Syslog &Syslog::server(IPAddress ip, uint16_t port)
{
this->_ip = ip;
this->_server = NULL;
this->_port = port;
return *this;
}
Syslog &Syslog::deviceHostname(const char *deviceHostname)
{
this->_deviceHostname = (deviceHostname == NULL) ? SYSLOG_NILVALUE : deviceHostname;
return *this;
}
Syslog &Syslog::appName(const char *appName)
{
this->_appName = (appName == NULL) ? SYSLOG_NILVALUE : appName;
return *this;
}
Syslog &Syslog::defaultPriority(uint16_t pri)
{
this->_priDefault = pri;
return *this;
}
Syslog &Syslog::logMask(uint8_t priMask)
{
this->_priMask = priMask;
return *this;
}
void Syslog::enable()
{
this->_enabled = true;
}
void Syslog::disable()
{
this->_enabled = false;
}
bool Syslog::isEnabled()
{
return this->_enabled;
}
bool Syslog::vlogf(uint16_t pri, const char *fmt, va_list args)
{
return this->vlogf(pri, this->_appName, fmt, args);
}
bool Syslog::vlogf(uint16_t pri, const char *appName, const char *fmt, va_list args)
{
char *message;
size_t initialLen;
size_t len;
bool result;
initialLen = strlen(fmt);
message = new char[initialLen + 1];
len = vsnprintf(message, initialLen + 1, fmt, args);
if (len > initialLen) {
delete[] message;
message = new char[len + 1];
vsnprintf(message, len + 1, fmt, args);
}
result = this->_sendLog(pri, appName, message);
delete[] message;
return result;
}
inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *message)
{
int result;
if (!this->_enabled)
return false;
if ((this->_server == NULL && this->_ip == INADDR_NONE) || this->_port == 0)
return false;
// Check priority against priMask values.
if ((LOG_MASK(LOG_PRI(pri)) & this->_priMask) == 0)
return true;
// Set default facility if none specified.
if ((pri & LOG_FACMASK) == 0)
pri = LOG_MAKEPRI(LOG_FAC(this->_priDefault), pri);
if (this->_server != NULL) {
result = this->_client->beginPacket(this->_server, this->_port);
} else {
result = this->_client->beginPacket(this->_ip, this->_port);
}
if (result != 1)
return false;
this->_client->print('<');
this->_client->print(pri);
this->_client->print(F(">1 - "));
this->_client->print(this->_deviceHostname);
this->_client->print(' ');
this->_client->print(appName);
this->_client->print(F(" - - - \xEF\xBB\xBF"));
this->_client->print(F("["));
this->_client->print(int(millis() / 1000));
this->_client->print(F("]: "));
this->_client->print(message);
this->_client->endPacket();
return true;
}
#endif

View File

@ -1,3 +1,6 @@
#ifndef SYSLOG_H
#define SYSLOG_H
// DEBUG LED
#ifndef LED_INVERTED
#define LED_INVERTED 0 // define as 1 if LED is active low (on)
@ -17,6 +20,7 @@
#define MESHTASTIC_LOG_LEVEL_INFO "INFO "
#define MESHTASTIC_LOG_LEVEL_WARN "WARN "
#define MESHTASTIC_LOG_LEVEL_ERROR "ERROR"
#define MESHTASTIC_LOG_LEVEL_CRIT "CRIT "
#define MESHTASTIC_LOG_LEVEL_TRACE "TRACE"
#include "SerialConsole.h"
@ -28,21 +32,72 @@
#define LOG_INFO(...) SEGGER_RTT_printf(0, __VA_ARGS__)
#define LOG_WARN(...) SEGGER_RTT_printf(0, __VA_ARGS__)
#define LOG_ERROR(...) SEGGER_RTT_printf(0, __VA_ARGS__)
#define LOG_CRIT(...) SEGGER_RTT_printf(0, __VA_ARGS__)
#define LOG_TRACE(...) SEGGER_RTT_printf(0, __VA_ARGS__)
#else
#ifdef DEBUG_PORT
#define LOG_DEBUG(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_DEBUG, __VA_ARGS__)
#define LOG_INFO(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_INFO, __VA_ARGS__)
#define LOG_WARN(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_WARN, __VA_ARGS__)
#define LOG_ERROR(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_ERROR, __VA_ARGS__)
#define LOG_TRACE(...) DEBUG_PORT.log(MESHTASTIC_LOG_TRACE, __VA_ARGS__)
#define LOG_CRIT(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_CRIT, __VA_ARGS__)
#define LOG_TRACE(...) DEBUG_PORT.log(MESHTASTIC_LOG_LEVEL_TRACE, __VA_ARGS__)
#else
#define LOG_DEBUG(...)
#define LOG_INFO(...)
#define LOG_WARN(...)
#define LOG_ERROR(...)
#define LOG_CRIT(...)
#define LOG_TRACE(...)
#endif
#endif
#define SYSLOG_NILVALUE "-"
#define SYSLOG_CRIT 2 /* critical conditions */
#define SYSLOG_ERR 3 /* error conditions */
#define SYSLOG_WARN 4 /* warning conditions */
#define SYSLOG_INFO 6 /* informational */
#define SYSLOG_DEBUG 7 /* debug-level messages */
// trace does not go out to syslog (yet?)
#define LOG_PRIMASK 0x07 /* mask to extract priority part (internal) */
/* extract priority */
#define LOG_PRI(p) ((p)&LOG_PRIMASK)
#define LOG_MAKEPRI(fac, pri) (((fac) << 3) | (pri))
/* facility codes */
#define LOGLEVEL_KERN (0 << 3) /* kernel messages */
#define LOGLEVEL_USER (1 << 3) /* random user-level messages */
#define LOGLEVEL_MAIL (2 << 3) /* mail system */
#define LOGLEVEL_DAEMON (3 << 3) /* system daemons */
#define LOGLEVEL_AUTH (4 << 3) /* security/authorization messages */
#define LOGLEVEL_SYSLOG (5 << 3) /* messages generated internally by syslogd */
#define LOGLEVEL_LPR (6 << 3) /* line printer subsystem */
#define LOGLEVEL_NEWS (7 << 3) /* network news subsystem */
#define LOGLEVEL_UUCP (8 << 3) /* UUCP subsystem */
#define LOGLEVEL_CRON (9 << 3) /* clock daemon */
#define LOGLEVEL_AUTHPRIV (10 << 3) /* security/authorization messages (private) */
#define LOGLEVEL_FTP (11 << 3) /* ftp daemon */
/* other codes through 15 reserved for system use */
#define LOGLEVEL_LOCAL0 (16 << 3) /* reserved for local use */
#define LOGLEVEL_LOCAL1 (17 << 3) /* reserved for local use */
#define LOGLEVEL_LOCAL2 (18 << 3) /* reserved for local use */
#define LOGLEVEL_LOCAL3 (19 << 3) /* reserved for local use */
#define LOGLEVEL_LOCAL4 (20 << 3) /* reserved for local use */
#define LOGLEVEL_LOCAL5 (21 << 3) /* reserved for local use */
#define LOGLEVEL_LOCAL6 (22 << 3) /* reserved for local use */
#define LOGLEVEL_LOCAL7 (23 << 3) /* reserved for local use */
#define LOG_NFACILITIES 24 /* current number of facilities */
#define LOG_FACMASK 0x03f8 /* mask to extract facility part */
/* facility of pri */
#define LOG_FAC(p) (((p)&LOG_FACMASK) >> 3)
#define LOG_MASK(pri) (1 << (pri)) /* mask for one priority */
#define LOG_UPTO(pri) ((1 << ((pri) + 1)) - 1) /* all priorities through pri */
// -----------------------------------------------------------------------------
// AXP192 (Rev1-specific options)
// -----------------------------------------------------------------------------
@ -52,3 +107,50 @@
// Default Bluetooth PIN
#define defaultBLEPin 123456
#if HAS_ETHERNET
#include <RAK13800_W5100S.h>
#endif // HAS_ETHERNET
#if HAS_WIFI
#include <WiFi.h>
#endif // HAS_WIFI
#if HAS_WIFI || HAS_ETHERNET
class Syslog
{
private:
UDP *_client;
IPAddress _ip;
const char *_server;
uint16_t _port;
const char *_deviceHostname;
const char *_appName;
uint16_t _priDefault;
uint8_t _priMask = 0xff;
bool _enabled = false;
bool _sendLog(uint16_t pri, const char *appName, const char *message);
public:
explicit Syslog(UDP &client);
Syslog &server(const char *server, uint16_t port);
Syslog &server(IPAddress ip, uint16_t port);
Syslog &deviceHostname(const char *deviceHostname);
Syslog &appName(const char *appName);
Syslog &defaultPriority(uint16_t pri = LOGLEVEL_KERN);
Syslog &logMask(uint8_t priMask);
void enable();
void disable();
bool isEnabled();
bool vlogf(uint16_t pri, const char *fmt, va_list args) __attribute__((format(printf, 3, 0)));
bool vlogf(uint16_t pri, const char *appName, const char *fmt, va_list args) __attribute__((format(printf, 3, 0)));
};
#endif // HAS_ETHERNET || HAS_WIFI
#endif // SYSLOG_H

View File

@ -7,6 +7,12 @@
#include "sleep.h"
#include "utils.h"
#ifdef DEBUG_HEAP_MQTT
#include "mqtt/MQTT.h"
#include "target_specific.h"
#include <WiFi.h>
#endif
#ifdef HAS_PMU
#include "XPowersAXP192.tpp"
#include "XPowersAXP2101.tpp"
@ -311,6 +317,25 @@ void Power::readPowerStatus()
ESP.getFreeHeap() - lastheap, running, concurrency::mainController.size(false));
lastheap = ESP.getFreeHeap();
}
#ifdef DEBUG_HEAP_MQTT
if (mqtt) {
// send MQTT-Packet with Heap-Size
uint8_t dmac[6];
getMacAddr(dmac); // Get our hardware ID
char mac[18];
sprintf(mac, "!%02x%02x%02x%02x", dmac[2], dmac[3], dmac[4], dmac[5]);
auto newHeap = ESP.getFreeHeap();
std::string heapTopic = "msh/2/heap/" + std::string(mac);
std::string heapString = std::to_string(newHeap);
mqtt->pubSub.publish(heapTopic.c_str(), heapString.c_str(), false);
// auto fragHeap = ESP.getHeapFragmentation();
auto wifiRSSI = WiFi.RSSI();
heapTopic = "msh/2/wifi/" + std::string(mac);
std::string wifiString = std::to_string(wifiRSSI);
mqtt->pubSub.publish(heapTopic.c_str(), wifiString.c_str(), false);
}
#endif
#endif
// If we have a battery at all and it is less than 10% full, force deep sleep if we have more than 3 low readings in a row

View File

@ -5,6 +5,8 @@
#include "configuration.h"
#include <assert.h>
#include <cstring>
#include <memory>
#include <stdexcept>
#include <sys/time.h>
#include <time.h>
@ -13,6 +15,10 @@
*/
NoopPrint noopPrint;
#if HAS_WIFI || HAS_ETHERNET
extern Syslog syslog;
#endif
void RedirectablePrint::setDestination(Print *_dest)
{
assert(_dest);
@ -96,6 +102,39 @@ size_t RedirectablePrint::log(const char *logLevel, const char *format, ...)
}
}
r += vprintf(format, arg);
#if HAS_WIFI || HAS_ETHERNET
// if syslog is in use, collect the log messages and send them to syslog
if (syslog.isEnabled()) {
int ll = 0;
switch (logLevel[0]) {
case 'D':
ll = SYSLOG_DEBUG;
break;
case 'I':
ll = SYSLOG_INFO;
break;
case 'W':
ll = SYSLOG_WARN;
break;
case 'E':
ll = SYSLOG_ERR;
break;
case 'C':
ll = SYSLOG_CRIT;
break;
default:
ll = 0;
}
auto thread = concurrency::OSThread::currentThread;
if (thread) {
syslog.vlogf(ll, thread->ThreadName.c_str(), format, arg);
} else {
syslog.vlogf(ll, format, arg);
}
}
#endif
va_end(arg);
isContinuationMessage = !hasNewline;
@ -136,3 +175,22 @@ void RedirectablePrint::hexDump(const char *logLevel, unsigned char *buf, uint16
}
log(logLevel, " +------------------------------------------------+ +----------------+\n");
}
std::string RedirectablePrint::mt_sprintf(const std::string fmt_str, ...)
{
int n = ((int)fmt_str.size()) * 2; /* Reserve two times as much as the length of the fmt_str */
std::unique_ptr<char[]> formatted;
va_list ap;
while (1) {
formatted.reset(new char[n]); /* Wrap the plain char array into the unique_ptr */
strcpy(&formatted[0], fmt_str.c_str());
va_start(ap, fmt_str);
int final_n = vsnprintf(&formatted[0], n, fmt_str.c_str(), ap);
va_end(ap);
if (final_n < 0 || final_n >= n)
n += abs(final_n - n + 1);
else
break;
}
return std::string(formatted.get());
}

View File

@ -2,6 +2,7 @@
#include <Print.h>
#include <stdarg.h>
#include <string>
/**
* A Printable that can be switched to squirt its bytes to a different sink.
@ -40,6 +41,8 @@ class RedirectablePrint : public Print
size_t vprintf(const char *format, va_list arg);
void hexDump(const char *logLevel, unsigned char *buf, uint16_t len);
std::string mt_sprintf(const std::string fmt_str, ...);
};
class NoopPrint : public Print

View File

@ -241,47 +241,48 @@ uint32_t RadioInterface::getTxDelayMsecWeighted(float snr)
void printPacket(const char *prefix, const meshtastic_MeshPacket *p)
{
LOG_DEBUG("%s (id=0x%08x fr=0x%02x to=0x%02x, WantAck=%d, HopLim=%d Ch=0x%x", prefix, p->id, p->from & 0xff, p->to & 0xff,
p->want_ack, p->hop_limit, p->channel);
std::string out = DEBUG_PORT.mt_sprintf("%s (id=0x%08x fr=0x%02x to=0x%02x, WantAck=%d, HopLim=%d Ch=0x%x", prefix, p->id,
p->from & 0xff, p->to & 0xff, p->want_ack, p->hop_limit, p->channel);
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
auto &s = p->decoded;
LOG_DEBUG(" Portnum=%d", s.portnum);
out += DEBUG_PORT.mt_sprintf(" Portnum=%d", s.portnum);
if (s.want_response)
LOG_DEBUG(" WANTRESP");
out += DEBUG_PORT.mt_sprintf(" WANTRESP");
if (s.source != 0)
LOG_DEBUG(" source=%08x", s.source);
out += DEBUG_PORT.mt_sprintf(" source=%08x", s.source);
if (s.dest != 0)
LOG_DEBUG(" dest=%08x", s.dest);
out += DEBUG_PORT.mt_sprintf(" dest=%08x", s.dest);
if (s.request_id)
LOG_DEBUG(" requestId=%0x", s.request_id);
out += DEBUG_PORT.mt_sprintf(" requestId=%0x", s.request_id);
/* now inside Data and therefore kinda opaque
if (s.which_ackVariant == SubPacket_success_id_tag)
LOG_DEBUG(" successId=%08x", s.ackVariant.success_id);
out += DEBUG_PORT.mt_sprintf(" successId=%08x", s.ackVariant.success_id);
else if (s.which_ackVariant == SubPacket_fail_id_tag)
LOG_DEBUG(" failId=%08x", s.ackVariant.fail_id); */
out += DEBUG_PORT.mt_sprintf(" failId=%08x", s.ackVariant.fail_id); */
} else {
LOG_DEBUG(" encrypted");
out += " encrypted";
}
if (p->rx_time != 0) {
LOG_DEBUG(" rxtime=%u", p->rx_time);
out += DEBUG_PORT.mt_sprintf(" rxtime=%u", p->rx_time);
}
if (p->rx_snr != 0.0) {
LOG_DEBUG(" rxSNR=%g", p->rx_snr);
out += DEBUG_PORT.mt_sprintf(" rxSNR=%g", p->rx_snr);
}
if (p->rx_rssi != 0) {
LOG_DEBUG(" rxRSSI=%i", p->rx_rssi);
out += DEBUG_PORT.mt_sprintf(" rxRSSI=%i", p->rx_rssi);
}
if (p->priority != 0)
LOG_DEBUG(" priority=%d", p->priority);
out += DEBUG_PORT.mt_sprintf(" priority=%d", p->priority);
LOG_DEBUG(")\n");
out + ")\n";
LOG_DEBUG("%s", out.c_str());
}
RadioInterface::RadioInterface()

View File

@ -2,6 +2,7 @@
#include "NodeDB.h"
#include "RTC.h"
#include "concurrency/Periodic.h"
#include "main.h"
#include "mesh/api/ethServerAPI.h"
#include "mqtt/MQTT.h"
#include "target_specific.h"
@ -17,6 +18,9 @@ NTPClient timeClient(ntpUDP, config.network.ntp_server);
uint32_t ntp_renew = 0;
#endif
EthernetUDP syslogClient;
Syslog syslog(syslogClient);
bool ethStartupComplete = 0;
using namespace concurrency;
@ -36,6 +40,27 @@ static int32_t reconnectETH()
timeClient.begin();
timeClient.setUpdateInterval(60 * 60); // Update once an hour
#endif
if (config.network.rsyslog_server[0]) {
LOG_INFO("Starting Syslog client\n");
// Defaults
int serverPort = 514;
const char *serverAddr = config.network.rsyslog_server;
String server = String(serverAddr);
int delimIndex = server.indexOf(':');
if (delimIndex > 0) {
String port = server.substring(delimIndex + 1, server.length());
server[delimIndex] = 0;
serverPort = port.toInt();
serverAddr = server.c_str();
}
syslog.server(serverAddr, serverPort);
syslog.deviceHostname(getDeviceName());
syslog.appName("Meshtastic");
syslog.defaultPriority(LOGLEVEL_USER);
syslog.enable();
}
// initWebServer();
initApiServer();
@ -143,10 +168,13 @@ bool isEthernetAvailable()
{
if (!config.network.eth_enabled) {
syslog.disable();
return false;
} else if (Ethernet.hardwareStatus() == EthernetNoHardware) {
syslog.disable();
return false;
} else if (Ethernet.linkStatus() == LinkOFF) {
syslog.disable();
return false;
} else {
return true;

View File

@ -39,6 +39,9 @@ unsigned long lastrun_ntp = 0;
bool needReconnect = true; // If we create our reconnector, run it once at the beginning
WiFiUDP syslogClient;
Syslog syslog(syslogClient);
Periodic *wifiReconnect;
static int32_t reconnectWiFi()
@ -135,6 +138,26 @@ static void onNetworkConnected()
timeClient.setUpdateInterval(60 * 60); // Update once an hour
#endif
if (config.network.rsyslog_server[0]) {
LOG_INFO("Starting Syslog client\n");
// Defaults
int serverPort = 514;
const char *serverAddr = config.network.rsyslog_server;
String server = String(serverAddr);
int delimIndex = server.indexOf(':');
if (delimIndex > 0) {
String port = server.substring(delimIndex + 1, server.length());
server[delimIndex] = 0;
serverPort = port.toInt();
serverAddr = server.c_str();
}
syslog.server(serverAddr, serverPort);
syslog.deviceHostname(getDeviceName());
syslog.appName("Meshtastic");
syslog.defaultPriority(LOGLEVEL_USER);
syslog.enable();
}
initWebServer();
initApiServer();
@ -223,6 +246,7 @@ static void WiFiEvent(WiFiEvent_t event)
break;
case ARDUINO_EVENT_WIFI_STA_STOP:
LOG_INFO("WiFi station stopped\n");
syslog.disable();
break;
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
LOG_INFO("Connected to access point\n");
@ -230,6 +254,7 @@ static void WiFiEvent(WiFiEvent_t event)
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
LOG_INFO("Disconnected from WiFi access point\n");
WiFi.disconnect(false, true);
syslog.disable();
needReconnect = true;
wifiReconnect->setIntervalFromNow(1000);
break;
@ -246,6 +271,7 @@ static void WiFiEvent(WiFiEvent_t event)
case ARDUINO_EVENT_WIFI_STA_LOST_IP:
LOG_INFO("Lost IP address and IP address is reset to 0\n");
WiFi.disconnect(false, true);
syslog.disable();
needReconnect = true;
wifiReconnect->setIntervalFromNow(1000);
break;

View File

@ -15,6 +15,8 @@
#include "mqtt/JSON.h"
#include <assert.h>
const int reconnectMax = 5;
MQTT *mqtt;
std::string statusTopic = "msh/2/stat/";
@ -218,15 +220,13 @@ void MQTT::reconnect()
sendSubscriptions();
} else {
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
LOG_ERROR("Failed to contact MQTT server (%d/5)...\n", reconnectCount + 1);
if (reconnectCount >= 4) {
reconnectCount++;
LOG_ERROR("Failed to contact MQTT server (%d/%d)...\n", reconnectCount, reconnectMax);
if (reconnectCount >= reconnectMax) {
needReconnect = true;
wifiReconnect->setIntervalFromNow(0);
reconnectCount = 0;
} else {
reconnectCount++;
}
#endif
}
}

View File

@ -43,3 +43,4 @@ postfixOperator:*/mqtt/*
missingOverride
virtualCallInConstructor
passedByValue:*/RedirectablePrint.h