-
Notifications
You must be signed in to change notification settings - Fork 915
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add logging to libcudf #12637
Add logging to libcudf #12637
Changes from 37 commits
60f6157
a888ac8
5108371
b2cc531
d7ecd33
830ab00
a4c51c0
d029e1e
3d7c2ca
cbda9d0
87c5d3a
0df8e97
92f53f9
36004e5
0a90a8f
0e95828
498ac21
f0ae3e2
680f425
6ab194c
404fa1d
bc12b13
38da9b5
c736164
b0ec318
bae202c
df803a4
d6b8145
e813cb9
b1f0994
c1ceb73
30fcd28
af09276
588d6f5
0c24717
5f090e2
6fea0bf
c77426d
5581c8a
6378bf7
553a57b
49f3896
0b6b27c
a540928
dcd3b29
e49f007
fd2636f
c75ff33
a8c3e11
57f6d05
eb045f7
2280ecf
e7fe0cd
06893a3
1d1950c
855fcc0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -901,6 +901,42 @@ void trivial_types_only(T t){ | |
} | ||
``` | ||
|
||
# Logging | ||
|
||
libcudf includes logging utilities (built on top of [spdlog](https://github.com/gabime/spdlog) | ||
library), which should be used to log important events (e.g. user warnings). This utility can also | ||
be used to log debug information, as long as the correct logging level is used. There are six macros | ||
that should be used for logging at different levels: | ||
|
||
* `CUDF_LOG_TRACE` - verbose debug messages (targeted at developers) | ||
* `CUDF_LOG_DEBUG` - debug messages (targeted at developers) | ||
* `CUDF_LOG_INFO` - information about rare events (e.g. once per run) that occur during normal | ||
execution | ||
* `CUDF_LOG_WARN` - user warnings about potentially unexpected behavior or deprecations | ||
* `CUDF_LOG_ERROR` - recoverable errors | ||
* `CUDF_LOG_CRITICAL` - unrecoverable errors (e.g. memory corruption) | ||
|
||
By default, messages below the `INFO` level are excluded from the log. In public builds, the code | ||
vuule marked this conversation as resolved.
Show resolved
Hide resolved
|
||
that logs at these levels is compiled out. This prevents logging of potentially sensitive data that | ||
might be done for debug purposes. Also, this allows developers to include expensive computation in | ||
the trace/debug logs, as the overhead will not be present in the public builds. The minimum enabled | ||
logging level is `INFO`, and it can be modified in multiple ways: | ||
|
||
* CMake configuration variable `LIBCUDF_LOGGING_LEVEL` - controls the minimum level of logging that | ||
will be compiled in the build. | ||
Available levels are `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`, `CRITICAL`, and `OFF`. | ||
* Environment variable `LIBCUDF_LOGGING_LEVEL`, with the same values as the CMake variable. | ||
The difference is that disabled levels are present in the build, but the messages are excluded from | ||
the log. | ||
vuule marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* Global logger object exposed via `cudf::logger()`. This API should not be used within libcudf to | ||
vuule marked this conversation as resolved.
Show resolved
Hide resolved
|
||
manipulate logging, | ||
its purpose is to allow upstream users to configure libcudf logging to fit their application. | ||
|
||
The default log file is `cudf_log.txt` in the current working directory. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I saw the default value is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current default level and sink type are based on RMM defaults. Upon further inspection, it looks like RMM does not have any INFO level messages, so only errors get logged into the file. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated the default behavior to: log warnings and more critical levels to stdout There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Curious why choose stdout instead of stderr? stdout is more likely than stderr to be used for something application specific. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did not make the initial choice carefully. Read up a bit today and changed to stderr. |
||
The environment variable `LIBCUDF_DEBUG_LOG_FILE` can be set to specify the path and file name. | ||
Upstream users can also manipulate `cudf::logger().sinks()` to add sinks or divert the log to standard | ||
output or even a custom spdlog sink. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would recommend adding support to have different sinks for different levels. By doing so, we can leverage multiple output log streams for debugging/application monitoring etc. By default we can have all log levels written into one sink, but we can also config to separate sinks. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would argue that's not necessary. If someone wants to do that, they can add a sink that multiplexes out to the separate streams based not just on log level but any other custom logic they might want to use to determine which stream to use. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Example of multi-sinks for multi-levels from spdlog: https://github.com/gabime/spdlog/blob/3cab260814613b0a7e6889d2a030b2992c916d1a/example/example.cpp#L253 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh wait. We may support that implicitly via spdlog! For example, we support doing so (in the example below):
And the example from spdlog has a snippet to create a sink like this:
So we can achieve multi-sinks for multi-levels by just setting the corresponding level for each sink. Please help verify this and update this dev guide too. |
||
|
||
# Data Types | ||
|
||
Columns may contain data of a number of types (see `enum class type_id` in `include/cudf/types.hpp`) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
/* | ||
* Copyright (c) 2023, NVIDIA CORPORATION. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <cudf/utilities/logger.hpp> | ||
|
||
// The default is INFO, but it should be used sparingly, so that by default a log file is only | ||
vuule marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// output if there is important information, warnings, errors, and critical failures | ||
// Log messages that require computation should only be used at level TRACE and DEBUG | ||
#define CUDF_LOG_TRACE(...) SPDLOG_LOGGER_TRACE(&cudf::logger(), __VA_ARGS__) | ||
#define CUDF_LOG_DEBUG(...) SPDLOG_LOGGER_DEBUG(&cudf::logger(), __VA_ARGS__) | ||
#define CUDF_LOG_INFO(...) SPDLOG_LOGGER_INFO(&cudf::logger(), __VA_ARGS__) | ||
#define CUDF_LOG_WARN(...) SPDLOG_LOGGER_WARN(&cudf::logger(), __VA_ARGS__) | ||
#define CUDF_LOG_ERROR(...) SPDLOG_LOGGER_ERROR(&cudf::logger(), __VA_ARGS__) | ||
#define CUDF_LOG_CRITICAL(...) SPDLOG_LOGGER_CRITICAL(&cudf::logger(), __VA_ARGS__) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* | ||
* Copyright (c) 2023, NVIDIA CORPORATION. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <spdlog/spdlog.h> | ||
|
||
namespace cudf { | ||
|
||
/** | ||
* @brief Returns the global logger. | ||
* | ||
* This is a global instance of a spdlog logger. It can be used to configure logging behavior in | ||
* libcudf. | ||
* | ||
* Examples: | ||
* @code{.cpp} | ||
* // Turn off logging at runtime | ||
* cudf::logger().set_level(spdlog::level::off); | ||
* // Add a stdout sink to the logger | ||
* cudf::logger().sinks().push_back(std::make_shared<spdlog::sinks::stdout_sink_mt>()); | ||
* // Replace the default sink | ||
* cudf::logger().sinks() ={std::make_shared<spdlog::sinks::stderr_sink_mt>()}; | ||
* @endcode | ||
* | ||
* Note: Changes to the sinks are not thread safe and should only be done during global | ||
* initialization. | ||
* | ||
* @return spdlog::logger& The logger. | ||
*/ | ||
spdlog::logger& logger(); | ||
|
||
} // namespace cudf |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/* | ||
* Copyright (c) 2023, NVIDIA CORPORATION. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
#include <cudf/utilities/error.hpp> | ||
#include <cudf/utilities/logger.hpp> | ||
|
||
#include "spdlog/sinks/stdout_sinks.h" | ||
#include <spdlog/sinks/basic_file_sink.h> | ||
|
||
#include <string> | ||
|
||
namespace { | ||
|
||
/** | ||
* @brief TODO | ||
vuule marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*/ | ||
[[nodiscard]] spdlog::sink_ptr make_libcudf_sink() | ||
{ | ||
if (auto filename = std::getenv("LIBCUDF_DEBUG_LOG_FILE"); filename != nullptr) { | ||
return std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true); | ||
} else { | ||
return std::make_shared<spdlog::sinks::stdout_sink_mt>(); | ||
} | ||
vuule marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
/** | ||
* @brief Converts the level name into the `spdlog` level enum. | ||
*/ | ||
[[nodiscard]] spdlog::level::level_enum libcudf_log_level() | ||
{ | ||
auto const env_level = std::getenv("LIBCUDF_LOGGING_LEVEL"); | ||
if (env_level == nullptr) { return spdlog::level::warn; } | ||
vuule marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
auto const env_lvl_str = std::string(env_level); | ||
if (env_lvl_str == "TRACE") return spdlog::level::trace; | ||
if (env_lvl_str == "DEBUG") return spdlog::level::debug; | ||
if (env_lvl_str == "INFO") return spdlog::level::info; | ||
if (env_lvl_str == "WARN") return spdlog::level::warn; | ||
if (env_lvl_str == "ERROR") return spdlog::level::err; | ||
if (env_lvl_str == "CRITICAL") return spdlog::level::critical; | ||
if (env_lvl_str == "OFF") return spdlog::level::off; | ||
|
||
CUDF_FAIL("Invalid value for LIBCUDF_LOGGING_LEVEL environment variable"); | ||
} | ||
|
||
/** | ||
* @brief Simple wrapper around a spdlog::logger that performs cuDF-specific initialization. | ||
*/ | ||
struct logger_wrapper { | ||
vuule marked this conversation as resolved.
Show resolved
Hide resolved
|
||
spdlog::logger logger_; | ||
|
||
logger_wrapper() : logger_{"CUDF", make_libcudf_sink()} | ||
{ | ||
logger_.set_pattern("[%6t][%H:%M:%S:%f][%-6l] %v"); | ||
logger_.set_level(libcudf_log_level()); | ||
logger_.flush_on(spdlog::level::warn); | ||
} | ||
}; | ||
|
||
} // namespace | ||
|
||
spdlog::logger& cudf::logger() | ||
{ | ||
static logger_wrapper wrapped{}; | ||
return wrapped.logger_; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should logging levels of RMM and cuDF be independently controlled?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming they do - how do we handle setting
SPDLOG_ACTIVE_LEVEL
? RMM sets it based on RMM_LOGGING_LEVEL, and we need to set it based onLIBCUDF_LOGGING_LEVEL
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah. Good point. I don't have feelings on this, we can keep it all the same.
I think spdlog may have multiple ways to set the logging level, and compile-time options are only one of the methods. It would be hard to ensure that debug messages are compiled out while retaining runtime control over two different loggers. 😖
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the logging level can be set the runtime. This is what we use to set the level if
LIBCUDF_LOGGING_LEVEL
env var is set.The CMake config var can be seen as minimal logging level that the build includes, since it literally undefines logging macros for level below its value (which is great for security/perf). AFAIK, the logging level for both libcudf will stay at INFO (or whatever is the default) even if CMake config var is TRACE/DEBUG.
I'm wary of having different SPDLOG_ACTIVE_LEVEL values in RMM and libcudf as it feels like possible ODR violation 🤷
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you would run into any issues. RMM only sets this for compiled components i.e. its tests and benchmarks. The main rmm code is header-only so there are no compiled components where
SPDLOG_ACTIVE_LEVEL
would be set to conflict with whatever libcudf sets.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about this more we might need to make this PRIVATE.
If a downstream consumer uses cudf and rmm they can't specify an RMM logging level since we are specifying one already.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great point, yeah that makes sense. I believe RMM sets this PUBLIC, but again it's only done in tests and benchmarks so it probably doesn't matter.