1212#include < binlog/adapt_stdoptional.hpp>
1313#include < binlog/adapt_stdvariant.hpp>
1414
15+ #include " swift/log/SwiftDiagnostics.h"
16+
1517// Logging macros. These will call `logger()` to get a Logger instance, picking up any `logger`
1618// defined in the current scope. Domain-specific loggers can be added or used by either:
1719// * providing a class field called `logger` (as `Logger::operator()()` returns itself)
2022// * passing a logger around using a `Logger& logger` function parameter
2123// They are created with a name that appears in the logs and can be used to filter debug levels (see
2224// `Logger`).
23- #define LOG_CRITICAL (...) LOG_WITH_LEVEL(codeql::Log::Level:: critical, __VA_ARGS__)
24- #define LOG_ERROR (...) LOG_WITH_LEVEL(codeql::Log::Level:: error, __VA_ARGS__)
25- #define LOG_WARNING (...) LOG_WITH_LEVEL(codeql::Log::Level:: warning, __VA_ARGS__)
26- #define LOG_INFO (...) LOG_WITH_LEVEL(codeql::Log::Level:: info, __VA_ARGS__)
27- #define LOG_DEBUG (...) LOG_WITH_LEVEL(codeql::Log::Level:: debug, __VA_ARGS__)
28- #define LOG_TRACE (...) LOG_WITH_LEVEL(codeql::Log::Level:: trace, __VA_ARGS__)
25+ #define LOG_CRITICAL (...) LOG_WITH_LEVEL(critical, __VA_ARGS__)
26+ #define LOG_ERROR (...) LOG_WITH_LEVEL(error, __VA_ARGS__)
27+ #define LOG_WARNING (...) LOG_WITH_LEVEL(warning, __VA_ARGS__)
28+ #define LOG_INFO (...) LOG_WITH_LEVEL(info, __VA_ARGS__)
29+ #define LOG_DEBUG (...) LOG_WITH_LEVEL(debug, __VA_ARGS__)
30+ #define LOG_TRACE (...) LOG_WITH_LEVEL(trace, __VA_ARGS__)
2931
3032// only do the actual logging if the picked up `Logger` instance is configured to handle the
3133// provided log level. `LEVEL` must be a compile-time constant. `logger()` is evaluated once
32- #define LOG_WITH_LEVEL (LEVEL, ...) \
33- do { \
34- constexpr codeql::Log::Level _level = LEVEL; \
35- codeql::Logger& _logger = logger (); \
36- if (_level >= _logger.level ()) { \
37- BINLOG_CREATE_SOURCE_AND_EVENT (_logger.writer (), _level, /* category */ , binlog::clockNow (), \
38- __VA_ARGS__); \
39- } \
34+ #define LOG_WITH_LEVEL_AND_CATEGORY (LEVEL, CATEGORY, ...) \
35+ do { \
36+ constexpr codeql::Log::Level _level = codeql::Log::Level::LEVEL; \
37+ codeql::Logger& _logger = logger (); \
38+ if (_level >= _logger.level ()) { \
39+ BINLOG_CREATE_SOURCE_AND_EVENT (_logger.writer (), _level, CATEGORY, binlog::clockNow (), \
40+ __VA_ARGS__); \
41+ } \
42+ } while (false )
43+
44+ #define LOG_WITH_LEVEL (LEVEL, ...) LOG_WITH_LEVEL_AND_CATEGORY(LEVEL, , __VA_ARGS__)
45+
46+ // Emit errors with a specified diagnostics ID. This must be the name of a function in the
47+ // codeql::diagnostics namespace, which must call SwiftDiagnosticSource::create with ID as first
48+ // argument. This function will be called at most once during the program execution.
49+ // See codeql::diagnostics::internal_error below as an example.
50+ #define DIAGNOSE_CRITICAL (ID, ...) DIAGNOSE_WITH_LEVEL(critical, ID, __VA_ARGS__)
51+ #define DIAGNOSE_ERROR (ID, ...) DIAGNOSE_WITH_LEVEL(error, ID, __VA_ARGS__)
52+
53+ #define DIAGNOSE_WITH_LEVEL (LEVEL, ID, ...) \
54+ do { \
55+ static int _ignore = (codeql::diagnostics::ID (), 0 ); \
56+ std::ignore = _ignore; \
57+ LOG_WITH_LEVEL_AND_CATEGORY (LEVEL, ID, __VA_ARGS__); \
4058 } while (false )
4159
4260// avoid calling into binlog's original macros
6886namespace codeql {
6987
7088// tools should define this to tweak the root name of all loggers
71- extern const std::string_view logRootName ;
89+ extern const std::string_view programName ;
7290
7391// This class is responsible for the global log state (outputs, log level rules, flushing)
7492// State is stored in the singleton `Log::instance()`.
7593// Before using logging, `Log::configure("<name>")` should be used (e.g.
7694// `Log::configure("extractor")`). Then, `Log::flush()` should be regularly called.
7795// Logging is configured upon first usage. This consists in
7896// * using environment variable `CODEQL_EXTRACTOR_SWIFT_LOG_DIR` to choose where to dump the log
79- // file(s). Log files will go to a subdirectory thereof named after `logRootName `
97+ // file(s). Log files will go to a subdirectory thereof named after `programName `
8098// * using environment variable `CODEQL_EXTRACTOR_SWIFT_LOG_LEVELS` to configure levels for
8199// loggers and outputs. This must have the form of a comma separated `spec:level` list, where
82100// `spec` is either a glob pattern (made up of alphanumeric, `/`, `*` and `.` characters) for
@@ -122,23 +140,40 @@ class Log {
122140 friend binlog::Session;
123141 Log& write (const char * buffer, std::streamsize size);
124142
143+ struct OnlyWithCategory {};
144+
125145 // Output filtered according to a configured log level
126146 template <typename Output>
127147 struct FilteredOutput {
128148 binlog::Severity level;
129149 Output output;
130- binlog::EventFilter filter{
131- [this ](const binlog::EventSource& src) { return src.severity >= level; }};
150+ binlog::EventFilter filter;
132151
133152 template <typename ... Args>
134153 FilteredOutput (Level level, Args&&... args)
135- : level{level}, output{std::forward<Args>(args)...} {}
154+ : level{level}, output{std::forward<Args>(args)...}, filter{filterOnLevel ()} {}
155+
156+ template <typename ... Args>
157+ FilteredOutput (OnlyWithCategory, Level level, Args&&... args)
158+ : level{level},
159+ output{std::forward<Args>(args)...},
160+ filter{filterOnLevelAndNonEmptyCategory ()} {}
136161
137162 FilteredOutput& write (const char * buffer, std::streamsize size) {
138163 filter.writeAllowed (buffer, size, output);
139164 return *this ;
140165 }
141166
167+ binlog::EventFilter::Predicate filterOnLevel () const {
168+ return [this ](const binlog::EventSource& src) { return src.severity >= level; };
169+ }
170+
171+ binlog::EventFilter::Predicate filterOnLevelAndNonEmptyCategory () const {
172+ return [this ](const binlog::EventSource& src) {
173+ return !src.category .empty () && src.severity >= level;
174+ };
175+ }
176+
142177 // if configured as `no_logs`, the output is effectively disabled
143178 explicit operator bool () const { return level < Level::no_logs; }
144179 };
@@ -151,14 +186,15 @@ class Log {
151186 FilteredOutput<std::ofstream> binary{Level::no_logs};
152187 FilteredOutput<binlog::TextOutputStream> text{Level::info, textFile, format};
153188 FilteredOutput<binlog::TextOutputStream> console{Level::warning, std::cerr, format};
189+ FilteredOutput<SwiftDiagnosticsDumper> diagnostics{OnlyWithCategory{}, Level::error};
154190 LevelRules sourceRules;
155- std::vector<std::string> collectSeverityRulesAndReturnProblems (const char * envVar);
191+ std::vector<std::string> collectLevelRulesAndReturnProblems (const char * envVar);
156192};
157193
158194// This class represent a named domain-specific logger, responsible for pushing logs using the
159195// underlying `binlog::SessionWriter` class. This has a configured log level, so that logs on this
160196// `Logger` with a level lower than the configured one are no-ops. The level is configured based
161- // on rules matching `<logRootName >/<name>` in `CODEQL_EXTRACTOR_SWIFT_LOG_LEVELS` (see above).
197+ // on rules matching `<programName >/<name>` in `CODEQL_EXTRACTOR_SWIFT_LOG_LEVELS` (see above).
162198// `<name>` is provided in the constructor. If no rule matches the name, the log level defaults to
163199// the minimum level of all outputs.
164200class Logger {
0 commit comments