| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | /* | ||
| 3 | * This file is part of the Falcon Player (FPP) and is Copyright (C) | ||
| 4 | * 2013-2022 by the Falcon Player Developers. | ||
| 5 | * | ||
| 6 | * The Falcon Player (FPP) is free software, and is covered under | ||
| 7 | * multiple Open Source licenses. Please see the included 'LICENSES' | ||
| 8 | * file for descriptions of what files are covered by each license. | ||
| 9 | * | ||
| 10 | * This source file is covered under the LGPL v2.1 as described in the | ||
| 11 | * included LICENSE.LGPL file. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <string> | ||
| 15 | #include <vector> | ||
| 16 | |||
| 17 | /* | ||
| 18 | * Define the allowed levels of Logging | ||
| 19 | */ | ||
| 20 | typedef enum { | ||
| 21 | LOG_ERR = 1, | ||
| 22 | LOG_WARN, | ||
| 23 | LOG_INFO, | ||
| 24 | LOG_DEBUG, | ||
| 25 | LOG_EXCESSIVE | ||
| 26 | } LogLevel; | ||
| 27 | |||
| 28 | /* | ||
| 29 | * A Logger Instance. | ||
| 30 | * In the future, this could be adjusted to allow different Instances | ||
| 31 | * to go to different files | ||
| 32 | */ | ||
| 33 | |||
| 34 | // LAYOUT RULE: this class is plugin ABI, even though no plugin ever names it. | ||
| 35 | // The VB_* macros below expand to a member of the single FPPLogger::INSTANCE, so | ||
| 36 | // a plugin bakes in "INSTANCE + N * sizeof(FPPLoggerInstance)" at compile time | ||
| 37 | // and passes that address to libfpp's _LogWrite() as an FPPLoggerInstance&. | ||
| 38 | // Change sizeof(FPPLoggerInstance) -- or insert/remove a facility in FPPLogger -- | ||
| 39 | // and every already-built plugin points at the middle of some other facility. | ||
| 40 | // What comes back is not a std::string, so facility.name.c_str() hands strlen() a | ||
| 41 | // pointer made of raw character bytes and fppd dies on the plugin's first log | ||
| 42 | // line. Adding the crashRingCapture bool below did exactly that: it grew this | ||
| 43 | // class from 28 to 32 bytes on 32-bit ARM, and plugins built against the older | ||
| 44 | // header resolved VB_PLUGIN 52 bytes short, landing inside the SSO buffer of | ||
| 45 | // Control's name. Plugin.h publishes fingerprints for this layout (they live | ||
| 46 | // there, not here, because that is the header every plugin includes textually | ||
| 47 | // rather than through the PCH) and the loader compares them, so a slip is | ||
| 48 | // refused at dlopen -- but only for plugins built since those fingerprints | ||
| 49 | // existed. If a member is genuinely needed here, treat it as an ABI change and | ||
| 50 | // weigh a FPP_PLUGIN_API_VERSION bump, which is the only thing that reaches | ||
| 51 | // binaries already in the field. | ||
| 52 | class FPPLoggerInstance { | ||
| 53 | public: | ||
| 54 | 39296 | FPPLoggerInstance(std::string name, bool crashRingCapture = true) { | |
| 55 |
1/2✓ Branch 0 taken 39296 times.
✗ Branch 1 not taken.
|
39296 | this->name = name; |
| 56 | 39296 | this->crashRingCapture = crashRingCapture; | |
| 57 | 39296 | } | |
| 58 | LogLevel level = LOG_INFO; | ||
| 59 | std::string name; | ||
| 60 | |||
| 61 | // Whether this facility's lines are kept in the crash-time ring (see | ||
| 62 | // CrashLogRingDump). Off for the per-frame data facilities: HexDump() | ||
| 63 | // emits at LogInfo, and DebugOutput::SendData calls it ungated on every | ||
| 64 | // frame, so a single such output would both burn CPU formatting into the | ||
| 65 | // ring and evict the entire history within a frame or two. The ring is | ||
| 66 | // for reconstructing state changes, and channel data is not one. | ||
| 67 | bool crashRingCapture = true; | ||
| 68 | }; | ||
| 69 | |||
| 70 | /* | ||
| 71 | * Class to hold all the Instances | ||
| 72 | */ | ||
| 73 | class FPPLogger { | ||
| 74 | public: | ||
| 75 | static FPPLogger INSTANCE; | ||
| 76 | 35 | const std::vector<FPPLoggerInstance*>& allInstances() { return all; } | |
| 77 | void Init(); | ||
| 78 | bool SetLevel(std::string name, LogLevel level); | ||
| 79 | bool SetLevel(const char* name, const char* level); | ||
| 80 | void SetAllLevel(LogLevel level); | ||
| 81 | int MinimumLogLevel(); | ||
| 82 | std::string GetLogLevelString(); | ||
| 83 | FPPLoggerInstance General = FPPLoggerInstance("General"); | ||
| 84 | FPPLoggerInstance ChannelOut = FPPLoggerInstance("ChannelOut", false); | ||
| 85 | FPPLoggerInstance ChannelData = FPPLoggerInstance("ChannelData", false); | ||
| 86 | FPPLoggerInstance Command = FPPLoggerInstance("Command"); | ||
| 87 | FPPLoggerInstance E131Bridge = FPPLoggerInstance("E131Bridge"); | ||
| 88 | FPPLoggerInstance Effect = FPPLoggerInstance("Effect"); | ||
| 89 | FPPLoggerInstance MediaOut = FPPLoggerInstance("MediaOut"); | ||
| 90 | FPPLoggerInstance Playlist = FPPLoggerInstance("Playlist"); | ||
| 91 | FPPLoggerInstance Schedule = FPPLoggerInstance("Schedule"); | ||
| 92 | FPPLoggerInstance Sequence = FPPLoggerInstance("Sequence"); | ||
| 93 | FPPLoggerInstance Settings = FPPLoggerInstance("Settings"); | ||
| 94 | FPPLoggerInstance Control = FPPLoggerInstance("Control"); | ||
| 95 | FPPLoggerInstance Sync = FPPLoggerInstance("Sync"); | ||
| 96 | ; | ||
| 97 | FPPLoggerInstance Plugin = FPPLoggerInstance("Plugin"); | ||
| 98 | FPPLoggerInstance GPIO = FPPLoggerInstance("GPIO"); | ||
| 99 | FPPLoggerInstance HTTP = FPPLoggerInstance("HTTP"); | ||
| 100 | |||
| 101 | private: | ||
| 102 | std::vector<FPPLoggerInstance*> all; | ||
| 103 | }; | ||
| 104 | |||
| 105 | #define VB_GENERAL FPPLogger::INSTANCE.General | ||
| 106 | #define VB_ALL FPPLogger::INSTANCE.General /* Over Load */ | ||
| 107 | #define VB_CHANNELOUT FPPLogger::INSTANCE.ChannelOut | ||
| 108 | #define VB_CHANNELDATA FPPLogger::INSTANCE.ChannelData | ||
| 109 | #define VB_COMMAND FPPLogger::INSTANCE.Command | ||
| 110 | #define VB_E131BRIDGE FPPLogger::INSTANCE.E131Bridge | ||
| 111 | #define VB_EFFECT FPPLogger::INSTANCE.Effect | ||
| 112 | #define VB_MEDIAOUT FPPLogger::INSTANCE.MediaOut | ||
| 113 | #define VB_PLAYLIST FPPLogger::INSTANCE.Playlist | ||
| 114 | #define VB_SCHEDULE FPPLogger::INSTANCE.Schedule | ||
| 115 | #define VB_SEQUENCE FPPLogger::INSTANCE.Sequence | ||
| 116 | #define VB_SETTING FPPLogger::INSTANCE.Settings | ||
| 117 | #define VB_CONTROL FPPLogger::INSTANCE.Control | ||
| 118 | #define VB_SYNC FPPLogger::INSTANCE.Sync | ||
| 119 | #define VB_PLUGIN FPPLogger::INSTANCE.Plugin | ||
| 120 | #define VB_GPIO FPPLogger::INSTANCE.GPIO | ||
| 121 | #define VB_HTTP FPPLogger::INSTANCE.HTTP | ||
| 122 | |||
| 123 | #define EXCESSIVE_LOG_LEVEL_WARNING "A Log Level is set to Excessive" | ||
| 124 | #define DEBUG_LOG_LEVEL_WARNING "A Log Level is set to Debug" | ||
| 125 | |||
| 126 | //extern int logLevel; | ||
| 127 | //extern char logLevelStr[16]; | ||
| 128 | //extern int logMask; | ||
| 129 | //extern char logMaskStr[1024]; | ||
| 130 | |||
| 131 | #define LogErr(facility, format, args...) _LogWrite(__FILE__, __LINE__, LOG_ERR, facility, format, ##args) | ||
| 132 | #define LogInfo(facility, format, args...) _LogWrite(__FILE__, __LINE__, LOG_INFO, facility, format, ##args) | ||
| 133 | #define LogWarn(facility, format, args...) _LogWrite(__FILE__, __LINE__, LOG_WARN, facility, format, ##args) | ||
| 134 | #define LogDebug(facility, format, args...) _LogWrite(__FILE__, __LINE__, LOG_DEBUG, facility, format, ##args) | ||
| 135 | #define LogExcess(facility, format, args...) _LogWrite(__FILE__, __LINE__, LOG_EXCESSIVE, facility, format, ##args) | ||
| 136 | |||
| 137 | void _LogWrite(const char* file, int line, int level, FPPLoggerInstance& facility, const char* format, ...); | ||
| 138 | void _LogWrite(const char* file, int line, int level, FPPLoggerInstance& facility, const std::string &str, ...); | ||
| 139 | bool WillLog(int level, FPPLoggerInstance& facility); | ||
| 140 | |||
| 141 | // Every log call reads a facility's level, on whatever thread it runs, while | ||
| 142 | // the API and settings change it on others: read and write it through these. | ||
| 143 | LogLevel FacilityLevel(FPPLoggerInstance& facility); | ||
| 144 | void SetFacilityLevel(FPPLoggerInstance& facility, LogLevel level); | ||
| 145 | |||
| 146 | // True when a line at this level/facility is retained in the crash-time ring | ||
| 147 | // even though the configured log level would otherwise discard it. | ||
| 148 | bool CrashLogRingWillCapture(int level, FPPLoggerInstance& facility); | ||
| 149 | |||
| 150 | // Stop accepting new lines. Called at the top of the crash handler so its own | ||
| 151 | // logging does not evict the pre-crash history. | ||
| 152 | void CrashLogRingFreeze(); | ||
| 153 | |||
| 154 | // Write the retained lines to fd, oldest first. Async-signal-safe: no locks, | ||
| 155 | // no allocation, write() only -- so it is usable from a crash handler that may | ||
| 156 | // hold the very locks the normal log path needs. | ||
| 157 | // | ||
| 158 | // PRIVACY: these are log lines, and FPP's logs carry host names, LAN addresses | ||
| 159 | // and discovered-device identifiers. Treat the output as log content -- it | ||
| 160 | // belongs in a crash report only at the share level that already ships | ||
| 161 | // logs/fppd.log, never at "stack traces only". | ||
| 162 | void CrashLogRingDump(int fd); | ||
| 163 | |||
| 164 | // Truncates a value before it goes into a log line - use for anything whose | ||
| 165 | // length isn't bounded by code (a Variable's value, a condition/command-list | ||
| 166 | // JSON blob, raw command output), as opposed to things like names/paths that | ||
| 167 | // are inherently short. Without this, e.g. an If condition checking a | ||
| 168 | // Variable that happens to hold a fetched web page can turn a single log | ||
| 169 | // line into hundreds of KB (found in practice: a 450KB Variable value logged | ||
| 170 | // in full from Condition.cpp's per-leaf trace). Appends a size marker when | ||
| 171 | // truncated so it's obvious data was cut, not silently dropped. | ||
| 172 | std::string TruncateForLog(const std::string& s, size_t maxLen = 200); | ||
| 173 | |||
| 174 | void SetLogFile(const char* filename, bool toStdOut = true); | ||
| 175 | |||
| 176 | // Drop the stdout copy of any line that made it into the log file. Only fppd | ||
| 177 | // wants this: it runs as `fppd -f` under systemd, so every line it logs would | ||
| 178 | // otherwise also be captured by journald, doubling the volume for a copy that | ||
| 179 | // dies with the boot. Leave it off (the default) in the small services -- their | ||
| 180 | // output is what `journalctl -u <service>` is expected to show. An interactive | ||
| 181 | // run (stdout is a TTY) echoes to the console regardless. | ||
| 182 | void SetSuppressDuplicateStdOut(bool suppress); | ||
| 183 | int loggingToFile(void); | ||
| 184 | void logVersionInfo(void); | ||
| 185 | |||
| 186 | bool SetLogLevel(const char* newLevel); | ||
| 187 | bool SetLogLevelComplex(std::string& input); /*parse debug:schedule,player;excess:mqtt*/ | ||
| 188 | bool SetLogLevelComplex(const char* input); /*parse debug:schedule,player;excess:mqtt*/ | ||
| 189 | std::string LogLevelToString(LogLevel level); /* Convert Level to String Equivilanent */ | ||
| 190 |