Skip to content

Commit d9cd223

Browse files
committed
Integrate logger with library
Initialize logger with a silent mode and output set to stderr. Allow for setting loggers for loader and adapters separately with options passed as environment variables UR_LOG_* (append adapter name or null for null adapter): UR_LOG_LOADER="level:info;flush:warning;output:stdout" UR_LOG_NULL="level:debug;output:file,<file_path>" Where: - `level` is the level determining minimum log level to be printed, possible values: debug, info, warning, error (default: no messages) - `flush` is the log level at which all the messages will be printed immediately (default: error) - `output` is the stream to which messages will be printed, possible values: stdout, stderr, file (default: stderr)
1 parent 6b16f91 commit d9cd223

File tree

8 files changed

+316
-71
lines changed

8 files changed

+316
-71
lines changed

scripts/core/INTRO.rst

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,11 +195,58 @@ Unified Runtime loader implements tracing support through the `XPTI framework <h
195195
| **user_data**: A pointer to `function_with_args_t` object, that includes function ID, name, arguments, and return value.
196196
- None
197197

198+
Logging
199+
---------------------
200+
201+
Logging in UR is handled by loggers which can be set for each library separately. There are several levels of logging: *debug*, *info*, *warning*, and *error*.
202+
The level of logging determines what messages will be printed, ie. the level set to *warning* means all messages at levels *warning* and *error* will be printed.
203+
By default, no messages are printed.
204+
205+
By default, there is a guarantee that *error* messages are flushed immediately. One can change this behavior to flush on lower-level messages.
206+
207+
Loggers redirect messages to *stdout*, *stderr*, or a file (default: *stderr*).
208+
209+
All of these logging options can be set with **UR_LOG_LOADER** and **UR_LOG_NULL** environment variables described in the **Environment Variables** section below.
210+
Both of these environment variables have the same syntax for setting logger options::
211+
212+
"[level:debug|info|warning|error];[flush:<debug|info|warning|error>];[output:stdout|stderr|file,<path>]"
213+
214+
* level - a log level, meaning that only messages from this level and above are printed,
215+
possible values, from the lowest level to the highest one: *debug*, *info*, *warning*, *error*,
216+
* flush - a flush level, meaning that messages at this level and above are guaranteed to be flushed immediately,
217+
possible values are the same as above,
218+
* output - indicates where messages should be printed,
219+
possible values are: *stdout*, *stderr* and *file*,
220+
when providing a *file* output option, a *<path>* is required
221+
222+
.. note::
223+
For output to file, a path to the file have to be provided after a comma, like in the example above. The path has to exist, file will be created if not existing.
224+
All these three logger options are optional. The defaults are set when options are not provided in the environment variable.
225+
Options have to be separated with `;`, option names, and their values with `:`. Additionally, when providing *file* output, the keyword *file* and a path to a file
226+
have to be separated by `,`.
227+
228+
An example of an environment variable for setting up the loader library logger with logging level set to *info*, flush level set to *warning*, and output set to
229+
the `out.log` file::
230+
231+
UR_LOG_LOADER="level:info;flush:warning;output:file,out.log"
232+
233+
An example of an environment variable for setting up the null adapter library with logging level set to *warning* and output set to stdout::
234+
235+
UR_LOG_NULL="level:warning;output:stdout"
236+
198237
Environment Variables
199238
---------------------
200239

201240
Specific environment variables can be set to control the behavior of unified runtime or enable certain features.
202241

242+
.. envvar:: UR_LOG_LOADER
243+
244+
Holds parameters for setting Unified Runtime loader logging. The syntax is described in the **Logging** section above.
245+
246+
.. envvar:: UR_LOG_NULL
247+
248+
Holds parameters for setting Unified Runtime null adapter logging. The syntax is described in the **Logging** section above.
249+
203250
.. envvar:: UR_ADAPTERS_FORCE_LOAD
204251

205252
Holds a comma-separated list of library names used by the loader for adapter discovery. By setting this value you can

source/common/logger/ur_level.hpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
#ifndef UR_LEVEL_HPP
55
#define UR_LEVEL_HPP 1
66

7+
#include <stdexcept>
8+
#include <string>
9+
710
namespace logger {
811

912
enum class Level { DEBUG,
@@ -27,6 +30,29 @@ inline constexpr auto level_to_str(Level level) {
2730
}
2831
}
2932

33+
inline auto str_to_level(std::string name) {
34+
struct lvl_name {
35+
std::string name;
36+
Level lvl;
37+
};
38+
39+
const lvl_name lvl_names[] = {{"debug", Level::DEBUG},
40+
{"info", Level::INFO},
41+
{"warning", Level::WARN},
42+
{"error", Level::WARN}};
43+
44+
for (auto const &item : lvl_names) {
45+
if (item.name.compare(name) == 0) {
46+
return item.lvl;
47+
}
48+
}
49+
throw std::invalid_argument(
50+
std::string("Parsing error: no valid log level for string '") + name +
51+
std::string("'.") +
52+
std::string(
53+
"\nValid log level names are: debug, info, warning and error"));
54+
}
55+
3056
} // namespace logger
3157

3258
#endif /* UR_LEVEL_HPP */

source/common/logger/ur_logger.hpp

Lines changed: 87 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,70 +4,104 @@
44
#ifndef UR_LOGGER_HPP
55
#define UR_LOGGER_HPP 1
66

7-
#include "ur_level.hpp"
8-
#include "ur_sinks.hpp"
7+
#include <algorithm>
8+
#include <memory>
9+
10+
#include "ur_logger_details.hpp"
11+
#include "ur_util.hpp"
912

1013
namespace logger {
14+
extern Logger logger;
15+
16+
template <typename... Args>
17+
inline void debug(const char *format, Args &&...args) {
18+
logger.log(logger::Level::DEBUG, format, std::forward<Args>(args)...);
19+
}
20+
21+
template <typename... Args>
22+
inline void info(const char *format, Args &&...args) {
23+
logger.log(logger::Level::INFO, format, std::forward<Args>(args)...);
24+
}
25+
26+
template <typename... Args>
27+
inline void warning(const char *format, Args &&...args) {
28+
logger.log(logger::Level::WARN, format, std::forward<Args>(args)...);
29+
}
30+
31+
template <typename... Args>
32+
inline void error(const char *format, Args &&...args) {
33+
logger.log(logger::Level::ERR, format, std::forward<Args>(args)...);
34+
}
35+
36+
inline void setLevel(logger::Level level) { logger.setLevel(level); }
37+
38+
inline void setFlushLevel(logger::Level level) { logger.setFlushLevel(level); }
39+
40+
/// @brief Create an instance of the logger with parameters obtained from the respective
41+
/// environment variable or with default configuration if the env var is empty,
42+
/// not set, or has the wrong format.
43+
/// Logger env vars are in the format: UR_LOG_*, ie.:
44+
/// - UR_LOG_LOADER (logger for loader library),
45+
/// - UR_LOG_NULL (logger for null adapter).
46+
/// Example of env var for setting up a loader library logger with logging
47+
/// level set to `info`, flush level set to `warning`, and output set to
48+
/// the `out.log` file:
49+
/// UR_LOG_LOADER="level:info;flush:warning;output:file,out.log"
50+
/// @param logger_name name that should be appended to the `UR_LOG_` prefix to
51+
/// get the proper environment variable, ie. "loader"
52+
/// @return an instance of a logger::Logger. In case of failure in the parsing of
53+
/// the environment variable, returns a default logger with the following
54+
/// options:
55+
/// - log level: quiet, meaning no messages are printed
56+
/// - flush level: error, meaning that only error messages are guaranteed
57+
/// to be printed immediately as they occur
58+
/// - output: stderr
59+
inline auto create_logger(std::string logger_name) {
60+
std::transform(logger_name.begin(), logger_name.end(), logger_name.begin(),
61+
::toupper);
62+
std::stringstream env_var_name;
63+
const auto default_level = logger::Level::QUIET;
64+
const auto default_flush_level = logger::Level::ERR;
65+
const std::string default_output = "stderr";
66+
auto level = default_level;
67+
auto flush_level = default_flush_level;
68+
std::unique_ptr<logger::Sink> sink;
1169

12-
class Logger {
13-
public:
14-
Logger(std::unique_ptr<logger::Sink> sink) : sink(std::move(sink)) {
15-
if (!this->sink) {
16-
throw std::invalid_argument(
17-
"Can't create logger with nullptr Sink!");
18-
}
19-
this->level = logger::Level::QUIET;
70+
env_var_name << "UR_LOG_" << logger_name;
71+
auto map = getenv_to_map(env_var_name.str().c_str());
72+
if (!map.has_value()) {
73+
return Logger(std::make_unique<logger::StderrSink>());
2074
}
2175

22-
Logger(logger::Level level, std::unique_ptr<logger::Sink> sink)
23-
: level(level), sink(std::move(sink)) {
24-
if (!this->sink) {
25-
throw std::invalid_argument(
26-
"Can't create logger with nullptr Sink!");
76+
try {
77+
auto kv = map->find("level");
78+
if (kv != map->end()) {
79+
auto value = kv->second.front();
80+
level = str_to_level(value);
2781
}
28-
}
29-
30-
~Logger() = default;
31-
32-
void setLevel(logger::Level level) { this->level = level; }
33-
34-
void setFlushLevel(logger::Level level) {
35-
this->sink->setFlushLevel(level);
36-
}
37-
38-
template <typename... Args>
39-
void debug(const char *format, Args &&...args) {
40-
log(logger::Level::DEBUG, format, std::forward<Args>(args)...);
41-
}
4282

43-
template <typename... Args>
44-
void info(const char *format, Args &&...args) {
45-
log(logger::Level::INFO, format, std::forward<Args>(args)...);
46-
}
47-
48-
template <typename... Args>
49-
void warning(const char *format, Args &&...args) {
50-
log(logger::Level::WARN, format, std::forward<Args>(args)...);
51-
}
52-
53-
template <typename... Args>
54-
void error(const char *format, Args &&...args) {
55-
log(logger::Level::ERR, format, std::forward<Args>(args)...);
56-
}
57-
58-
private:
59-
logger::Level level;
60-
std::unique_ptr<logger::Sink> sink;
83+
kv = map->find("flush");
84+
if (kv != map->end()) {
85+
auto value = kv->second.front();
86+
flush_level = str_to_level(value);
87+
}
6188

62-
template <typename... Args>
63-
void log(logger::Level level, const char *format, Args &&...args) {
64-
if (level < this->level) {
65-
return;
89+
std::vector<std::string> values = {default_output};
90+
kv = map->find("output");
91+
if (kv != map->end()) {
92+
values = kv->second;
6693
}
6794

68-
sink->log(level, format, std::forward<Args>(args)...);
95+
sink = values.size() == 2 ? sink_from_str(values[0], values[1])
96+
: sink_from_str(values[0]);
97+
} catch (const std::invalid_argument &e) {
98+
std::cerr << "Error when creating a logger instance from environment variable" << e.what();
99+
return Logger(std::make_unique<logger::StderrSink>());
69100
}
70-
};
101+
sink->setFlushLevel(flush_level);
102+
103+
return Logger(level, std::move(sink));
104+
}
71105

72106
} // namespace logger
73107

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright (C) 2023 Intel Corporation
2+
// SPDX-License-Identifier: MIT
3+
4+
#ifndef UR_LOGGER_DETAILS_HPP
5+
#define UR_LOGGER_DETAILS_HPP 1
6+
7+
#include "ur_level.hpp"
8+
#include "ur_sinks.hpp"
9+
10+
namespace logger {
11+
12+
class Logger {
13+
public:
14+
Logger(std::unique_ptr<logger::Sink> sink) : sink(std::move(sink)) {
15+
if (!this->sink) {
16+
throw std::invalid_argument(
17+
"Can't create logger with nullptr Sink!");
18+
}
19+
this->level = logger::Level::QUIET;
20+
}
21+
22+
Logger(logger::Level level, std::unique_ptr<logger::Sink> sink)
23+
: level(level), sink(std::move(sink)) {
24+
if (!this->sink) {
25+
throw std::invalid_argument(
26+
"Can't create logger with nullptr Sink!");
27+
}
28+
}
29+
30+
Logger(const Logger &other) : level(other.level), sink(std::move(sink)) {}
31+
32+
Logger &operator=(Logger other) {
33+
std::swap(level, other.level);
34+
sink.swap(other.sink);
35+
return *this;
36+
}
37+
38+
~Logger() = default;
39+
40+
void setLevel(logger::Level level) { this->level = level; }
41+
42+
void setFlushLevel(logger::Level level) {
43+
this->sink->setFlushLevel(level);
44+
}
45+
46+
template <typename... Args>
47+
void debug(const char *format, Args &&...args) {
48+
log(logger::Level::DEBUG, format, std::forward<Args>(args)...);
49+
}
50+
51+
template <typename... Args>
52+
void info(const char *format, Args &&...args) {
53+
log(logger::Level::INFO, format, std::forward<Args>(args)...);
54+
}
55+
56+
template <typename... Args>
57+
void warning(const char *format, Args &&...args) {
58+
log(logger::Level::WARN, format, std::forward<Args>(args)...);
59+
}
60+
61+
template <typename... Args>
62+
void error(const char *format, Args &&...args) {
63+
log(logger::Level::ERR, format, std::forward<Args>(args)...);
64+
}
65+
66+
template <typename... Args>
67+
void log(logger::Level level, const char *format, Args &&...args) {
68+
if (level < this->level) {
69+
return;
70+
}
71+
72+
sink->log(level, format, std::forward<Args>(args)...);
73+
}
74+
75+
private:
76+
logger::Level level;
77+
std::unique_ptr<logger::Sink> sink;
78+
};
79+
80+
} // namespace logger
81+
82+
#endif /* UR_LOGGER_DETAILS_HPP */

source/common/logger/ur_sinks.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,21 @@ class FileSink : public Sink {
152152
std::ofstream ofstream;
153153
};
154154

155+
inline std::unique_ptr<Sink> sink_from_str(std::string name, std::string file_path = "") {
156+
if (name == "stdout") {
157+
return std::make_unique<logger::StdoutSink>();
158+
} else if (name == "stderr") {
159+
return std::make_unique<logger::StderrSink>();
160+
} else if (name == "file" && !file_path.empty()) {
161+
return std::make_unique<logger::FileSink>(file_path.c_str());
162+
}
163+
164+
throw std::invalid_argument(
165+
std::string("Parsing error: no valid sink for string '") + name +
166+
std::string("' with path '") + file_path + std::string("'.") +
167+
std::string("\nValid sink names are: stdout, stderr, file"));
168+
}
169+
155170
} // namespace logger
156171

157172
#endif /* UR_SINKS_HPP */

source/drivers/null/ur_null.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
*
99
*/
1010
#include "ur_null.hpp"
11+
#include "logger/ur_logger.hpp"
12+
13+
namespace logger {
14+
Logger logger = create_logger("null");
15+
}
1116

1217
namespace driver {
1318
//////////////////////////////////////////////////////////////////////////

source/loader/ur_lib.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*
99
*/
1010
#include "ur_lib.hpp"
11+
#include "logger/ur_logger.hpp"
1112
#include "ur_loader.hpp"
1213
#include "ur_proxy_layer.hpp"
1314
#include "validation/ur_validation_layer.hpp"
@@ -16,6 +17,10 @@
1617
#include "tracing/ur_tracing_layer.hpp"
1718
#endif
1819

20+
namespace logger {
21+
Logger logger = create_logger("loader");
22+
}
23+
1924
namespace ur_lib {
2025
///////////////////////////////////////////////////////////////////////////////
2126
context_t *context;

0 commit comments

Comments
 (0)