1010#include < binlog/EventFilter.hpp>
1111
1212// Logging macros. These will call `logger()` to get a Logger instance, picking up any `logger`
13- // defined in the current scope. A default `codeql::logger()` is provided, otherwise domain-specific
14- // loggers can be added as class fields called `logger` (as `Logger::operator()()` returns itself).
15- // Domain specific loggers are set up with a name that appears in the log and can be used to filter
16- // debug levels (see `Logger`). If working in the global namespace, the default logger can be used
17- // by defining `auto& logger = codeql::logger();`
13+ // defined in the current scope. Domain-specific loggers can be added or used by either:
14+ // * providing a class field called `logger` (as `Logger::operator()()` returns itself)
15+ // * declaring a local `logger` variable (to be used for one-time execution like code in `main`)
16+ // * declaring a `Logger& logger()` function returning a reference to a static local variable
17+ // * passing a logger around using a `Logger& logger` function parameter
18+ // They are created with a name that appears in the logs and can be used to filter debug levels (see
19+ // `Logger`).
1820#define LOG_CRITICAL (...) LOG_IMPL(codeql::Log::Level::critical, __VA_ARGS__)
1921#define LOG_ERROR (...) LOG_IMPL(codeql::Log::Level::error, __VA_ARGS__)
2022#define LOG_WARNING (...) LOG_IMPL(codeql::Log::Level::warning, __VA_ARGS__)
@@ -68,7 +70,52 @@ class Log {
6870 public:
6971 using Level = binlog::Severity;
7072
73+ // Internal data required to build `Logger` instances
74+ struct LoggerConfiguration {
75+ binlog::Session& session;
76+ std::string fullyQualifiedName;
77+ Level level;
78+ };
79+
80+ // Configure logging. This consists in
81+ // * using environment variable `CODEQL_EXTRACTOR_SWIFT_LOG_DIR` to choose where to dump the log
82+ // file(s). Log files will go to a subdirectory thereof named after `root`
83+ // * using environment variable `CODEQL_EXTRACTOR_SWIFT_LOG_LEVELS` to configure levels for
84+ // loggers and outputs. This must have the form of a comma separated `spec:level` list, where
85+ // `spec` is either a glob pattern (made up of alphanumeric, `/`, `*` and `.` characters) for
86+ // matching logger names or one of `out:bin`, `out:text` or `out:console`.
87+ // Output default levels can be seen in the corresponding initializers below. By default, all
88+ // loggers are configured with the lowest output level
89+ static void configure (std::string_view root) { instance ().configureImpl (root); }
90+
91+ // Flush logs to the designated outputs
92+ static void flush () { instance ().flushImpl (); }
93+
94+ // create `Logger` configuration, used internally by `Logger`'s constructor
95+ static LoggerConfiguration getLoggerConfiguration (std::string_view name) {
96+ return instance ().getLoggerConfigurationImpl (name);
97+ }
98+
7199 private:
100+ static constexpr const char * format = " %u %S [%n] %m (%G:%L)\n " ;
101+
102+ Log () = default ;
103+
104+ static Log& instance () {
105+ static Log ret;
106+ return ret;
107+ }
108+
109+ static class Logger & logger ();
110+
111+ void configureImpl (std::string_view root);
112+ void flushImpl ();
113+ LoggerConfiguration getLoggerConfigurationImpl (std::string_view name);
114+
115+ // make `session.consume(*this)` work, which requires access to `write`
116+ friend binlog::Session;
117+ Log& write (const char * buffer, std::streamsize size);
118+
72119 // Output filtered according to a configured log level
73120 template <typename Output>
74121 struct FilteredOutput {
@@ -93,72 +140,39 @@ class Log {
93140 using LevelRule = std::pair<std::regex, Level>;
94141 using LevelRules = std::vector<LevelRule>;
95142
96- static constexpr const char * format = " %u %S [%n] %m (%G:%L)\n " ;
97-
98143 binlog::Session session;
99144 std::ofstream textFile;
100145 FilteredOutput<std::ofstream> binary{Level::no_logs};
101146 FilteredOutput<binlog::TextOutputStream> text{Level::info, textFile, format};
102147 FilteredOutput<binlog::TextOutputStream> console{Level::warning, std::cerr, format};
103148 LevelRules sourceRules;
104149 std::string rootName;
105-
106- Log () = default ;
107-
108- static Log& instance () {
109- static Log ret;
110- return ret;
111- }
112-
113- friend class Logger ;
114- friend binlog::Session;
115-
116- Level getLevelForSource (std::string_view name) const ;
117- Log& write (const char * buffer, std::streamsize size);
118- static class Logger & logger ();
119-
120- public:
121- // Configure logging. This consists in
122- // * setting up a default logger with `root` as name
123- // * using environment variable `CODEQL_EXTRACTOR_SWIFT_LOG_DIR` to choose where to dump the log
124- // file(s). Log files will go to a subdirectory thereof named after `root`
125- // * using environment variable `CODEQL_EXTRACTOR_SWIFT_LOG_LEVELS` to configure levels for
126- // loggers and outputs. This must have the form of a comma separated `spec:level` list, where
127- // `spec` is either a glob pattern (made up of alphanumeric, `/`, `*` and `.` characters) for
128- // matching logger names or one of `out:bin`, `out:text` or `out:console`.
129- // Output default levels can be seen in the corresponding initializers above. By default, all
130- // loggers are configured with the lowest output level
131- static void configure (std::string_view root);
132-
133- // Flush logs to the designated outputs
134- static void flush ();
135150};
136151
137152// This class represent a named domain-specific logger, responsible for pushing logs using the
138153// underlying `binlog::SessionWriter` class. This has a configured log level, so that logs on this
139154// `Logger` with a level lower than the configured one are no-ops.
140155class Logger {
141- binlog::SessionWriter w{Log::instance ().session };
142- Log::Level level_{Log::Level::no_logs};
143-
144- void setName (std::string name);
145-
146- friend Logger& logger ();
147- // constructor for the default `Logger`
148- explicit Logger () { setName (Log::instance ().rootName ); }
149-
150156 public:
151- explicit Logger (const std::string& name) { setName (Log::instance (). rootName + ' / ' + name); }
157+ explicit Logger (const std::string& name) : Logger (Log::getLoggerConfiguration( name)) { }
152158
153159 binlog::SessionWriter& writer () { return w; }
154160 Log::Level level () const { return level_; }
155161
156162 // make defining a `Logger logger` field be equivalent to providing a `Logger& logger()` function
157163 // in order to be picked up by logging macros
158164 Logger& operator ()() { return *this ; }
159- };
160165
161- // default logger
162- Logger& logger ();
166+ private:
167+ static constexpr size_t queueSize = 1 << 20 ; // default taken from binlog
168+
169+ explicit Logger (Log::LoggerConfiguration&& configuration)
170+ : w{configuration.session , queueSize, /* id */ 0 ,
171+ std::move (configuration.fullyQualifiedName )},
172+ level_{configuration.level } {}
173+
174+ binlog::SessionWriter w;
175+ Log::Level level_;
176+ };
163177
164178} // namespace codeql
0 commit comments