OpenShot Library | libopenshot 1.1.1
Loading...
Searching...
No Matches
Logger.cpp
Go to the documentation of this file.
1// Copyright (c) 2008-2026 OpenShot Studios, LLC
2// SPDX-License-Identifier: LGPL-3.0-or-later
3#include "Logger.h"
4#include "Settings.h"
5#if USE_RESVG == 1
6#include "ResvgQt.h"
7#endif
8#include <algorithm>
9#include <cctype>
10#include <cstdlib>
11#include <ctime>
12#ifdef _WIN32
13#include <filesystem>
14#endif
15#include <iomanip>
16#include <iostream>
17#include <sstream>
18#include <stdexcept>
19#include <thread>
20
21using namespace openshot;
22namespace {
23int ParseLevel(std::string value) {
24 std::transform(value.begin(), value.end(), value.begin(),
25 [](unsigned char c) { return std::toupper(c); });
26 if (value == "DEBUG") return Logger::LevelDebug;
27 if (value == "INFO") return Logger::LevelInfo;
28 if (value == "WARNING") return Logger::LevelWarning;
29 if (value == "ERROR") return Logger::LevelError;
30 if (value == "CRITICAL") return Logger::LevelCritical;
31 if (value == "OFF") return Logger::LevelOff;
32 throw std::invalid_argument("Invalid logging level: " + value);
33}
34const char* LevelName(Logger::Level level) {
35 switch (level) {
36 case Logger::LevelDebug: return "DEBUG";
37 case Logger::LevelInfo: return "INFO";
38 case Logger::LevelWarning: return "WARNING";
39 case Logger::LevelError: return "ERROR";
40 case Logger::LevelCritical: return "CRITICAL";
41 default: return "OFF";
42 }
43}
44std::tm LocalTime(std::time_t now) {
45 std::tm result{};
46#ifdef _WIN32
47 localtime_s(&result, &now);
48#else
49 localtime_r(&now, &result);
50#endif
51 return result;
52}
53}
54
56 // Retain the logger through static destruction, including crash reporting.
57 static Logger* instance = new Logger;
58 return instance;
59}
60
61Logger::Logger() : legacy_settings(Settings::Instance()) {
62 // Parse each variable once; malformed values do not hide valid fallbacks.
63 for (const char* key : {"OPENSHOT_LOG_LEVEL", "OPENSHOT_LOG_FILE_LEVEL",
64 "OPENSHOT_LOG_CONSOLE_LEVEL", "LIBOPENSHOT_LOG_LEVEL",
65 "LIBOPENSHOT_LOG_FILE_LEVEL", "LIBOPENSHOT_LOG_CONSOLE_LEVEL"}) {
66 const char* value = std::getenv(key);
67 if (!value) continue;
68 try {
69 int level = ParseLevel(value);
70 std::string name(key);
71 if (name.find("CONSOLE") == std::string::npos) file_level = level;
72 if (name.find("FILE") == std::string::npos) {
73 console_level = level;
74 console_configured = true;
75 }
76 } catch (const std::invalid_argument&) {
77 std::cerr << "libopenshot: ignoring invalid " << key << "=" << value << '\n';
78 }
79 }
80#ifdef _WIN32
81 // Windows environment paths are UTF-16; avoid the locale-dependent narrow getenv.
82 if (const wchar_t* path = _wgetenv(L"LIBOPENSHOT_LOG_FILE")) {
83 try { Path(std::filesystem::path(path).u8string()); }
84 catch (const std::exception&) {
85 std::cerr << "libopenshot: invalid LIBOPENSHOT_LOG_FILE path\n";
86 }
87 }
88#else
89 if (const char* path = std::getenv("LIBOPENSHOT_LOG_FILE")) Path(path);
90#endif
91#if USE_RESVG == 1
92 ResvgRenderer::initLog();
93#endif
94}
95
96int Logger::ConsoleLevel() const {
97 // Preserve the old explicit Settings API and presence-based environment alias.
98 if (!console_configured && legacy_settings->DEBUG_TO_STDERR) return LevelDebug;
99 return console_level;
100}
101bool Logger::ShouldLog(Level level) const {
102 return level != LevelOff && ((file_open && level >= file_level) || level >= ConsoleLevel());
103}
104void Logger::SetFileLevel(std::string level) { file_level = ParseLevel(level); }
105void Logger::SetConsoleLevel(std::string level) {
106 console_level = ParseLevel(level);
107 console_configured = true;
108}
109void Logger::Enable(bool enabled) { file_level = enabled ? LevelDebug : LevelOff; }
110
111void Logger::Path(std::string path) {
112 const std::lock_guard<std::recursive_mutex> lock(loggerMutex);
113 if (path == file_path && file_open) return;
114 file_open = false;
115 if (log_file.is_open()) log_file.close();
116 log_file.clear();
117 file_path = path;
118 if (path.empty()) return;
119 try {
120#ifdef _WIN32
121 log_file.open(std::filesystem::u8path(path), std::ios::out | std::ios::app);
122#else
123 // POSIX paths already use UTF-8 bytes. Avoid std::filesystem, which
124 // requires macOS 10.15 even when compiling with C++17 enabled.
125 log_file.open(path, std::ios::out | std::ios::app);
126#endif
127 } catch (const std::exception&) {
128 std::cerr << "libopenshot: invalid log file path: " << path << '\n';
129 return;
130 }
131 file_open = log_file.is_open();
132 if (!file_open) {
133 std::cerr << "libopenshot: unable to open log file: " << path << '\n';
134 return;
135 }
136 auto now = LocalTime(std::time(nullptr));
137 // Keep these markers intact for openshot-qt's existing crash recovery scanner.
138 log_file << "------------------------------------------\n"
139 << "libopenshot logging: " << std::put_time(&now, "%a %b %d %H:%M:%S %Y") << '\n'
140 << "------------------------------------------" << std::endl;
141}
142void Logger::LogToFile(std::string message) {
143 // Deliberately retain the existing crash-write path (no level filter or new lock).
144 if (log_file.is_open()) log_file << message << std::flush;
145}
146void Logger::Log(std::string message, Level level) {
147 if (!ShouldLog(level)) return;
148 const std::lock_guard<std::recursive_mutex> lock(loggerMutex);
149 auto now = LocalTime(std::time(nullptr));
150 std::ostringstream record;
151 record << std::put_time(&now, "%Y-%m-%d %H:%M:%S") << ' '
152 << LevelName(level) << " [" << std::this_thread::get_id() << "] " << message;
153 if (message.empty() || message.back() != '\n') record << '\n';
154 const auto text = record.str();
155 if (level >= file_level) LogToFile(text);
156 if (level >= ConsoleLevel()) std::clog << text << std::flush;
157}
159 const std::lock_guard<std::recursive_mutex> lock(loggerMutex);
160 file_level = LevelOff;
161 console_level = LevelOff;
162 console_configured = true;
163 file_open = false;
164 if (log_file.is_open()) log_file.close();
165}
166
167// Append debug information
168void Logger::AppendDebugMethod(std::string method_name,
169 std::string arg1_name, float arg1_value,
170 std::string arg2_name, float arg2_value,
171 std::string arg3_name, float arg3_value,
172 std::string arg4_name, float arg4_value,
173 std::string arg5_name, float arg5_value,
174 std::string arg6_name, float arg6_value)
175{
176 if (!ShouldLog(LevelDebug))
177 // Don't do anything
178 return;
179
180 {
181 // Create a scoped lock, allowing only a single thread to run the following code at one time
182 const std::lock_guard<std::recursive_mutex> lock(loggerMutex);
183
184 std::stringstream message;
185 message << std::fixed << std::setprecision(4);
186
187 // Construct message
188 message << method_name << " (";
189
190 if (arg1_name.length() > 0)
191 message << arg1_name << "=" << arg1_value;
192
193 if (arg2_name.length() > 0)
194 message << ", " << arg2_name << "=" << arg2_value;
195
196 if (arg3_name.length() > 0)
197 message << ", " << arg3_name << "=" << arg3_value;
198
199 if (arg4_name.length() > 0)
200 message << ", " << arg4_name << "=" << arg4_value;
201
202 if (arg5_name.length() > 0)
203 message << ", " << arg5_name << "=" << arg5_value;
204
205 if (arg6_name.length() > 0)
206 message << ", " << arg6_name << "=" << arg6_value;
207
208 message << ")" << std::endl;
209
210 Log(message.str(), LevelDebug);
211 }
212}
Header file for global Settings class.
Independent file and stderr logging. No networking or worker thread.
Definition Logger.h:14
void AppendDebugMethod(std::string method_name, std::string arg1_name="", float arg1_value=-1.0, std::string arg2_name="", float arg2_value=-1.0, std::string arg3_name="", float arg3_value=-1.0, std::string arg4_name="", float arg4_value=-1.0, std::string arg5_name="", float arg5_value=-1.0, std::string arg6_name="", float arg6_value=-1.0)
Definition Logger.cpp:168
void SetFileLevel(std::string level)
Explicit configuration overrides environment defaults. Invalid levels throw.
Definition Logger.cpp:104
void SetConsoleLevel(std::string level)
Definition Logger.cpp:105
void Path(std::string path)
Definition Logger.cpp:111
void Enable(bool enabled)
Compatibility: enable debug file output, or disable ordinary file output.
Definition Logger.cpp:109
bool ShouldLog(Level level=LevelDebug) const
Definition Logger.cpp:101
void LogToFile(std::string message)
Unfiltered raw output, retained for the existing crash handler.
Definition Logger.cpp:142
void Log(std::string message, Level level=LevelDebug)
Definition Logger.cpp:146
static Logger * Instance()
Definition Logger.cpp:55
This class is contains settings used by libopenshot (and can be safely toggled at any point)
Definition Settings.h:26
bool DEBUG_TO_STDERR
Legacy native debug-to-stderr switch (explicit Logger levels take precedence)
Definition Settings.h:126
This namespace is the default namespace for all code in the openshot library.