alpaka
Abstraction Library for Parallel Kernel Acceleration
Loading...
Searching...
No Matches
logger.hpp
Go to the documentation of this file.
1/* Copyright 2025 René Widera
2 * SPDX-License-Identifier: MPL-2.0
3 */
4
5#pragma once
6
9#include "alpaka/unused.hpp"
10
11#include <atomic>
12#include <chrono>
13#include <functional>
14#include <iostream>
15#include <ostream>
16#include <source_location>
17#include <string>
18#include <string_view>
19
20namespace alpaka::onHost::logger::internal
21{
22 /** Write all output to std::cerr
23 *
24 * The output is not buffered and will be written immediately, it is **NOT** threadsafe.
25 *
26 * @todo seperate the indention level from the writer
27 * @todo write additional logger, std::cout and thread save logger
28 */
29 struct StdErr
30 {
31 static StdErr& get()
32 {
33 static StdErr inst = StdErr{};
34 return inst;
35 }
36
37 std::ostream& operator<<(auto const& input) const
38 {
39 return std::cerr << input;
40 }
41
42 /** increase the indention level
43 *
44 * @return the indention level for the current message
45 */
46 int enter()
47 {
48 return indentLvl++;
49 }
50
51 /** decrease the indention level
52 *
53 * @return the indention level for the current message
54 */
55 int leave()
56 {
57 return --indentLvl;
58 }
59
60 /** current indention level
61 *
62 * @return the indention level for the current message
63 */
64 int current()
65 {
66 return indentLvl.load();
67 }
68
69 private:
70 std::atomic<int> indentLvl = 1;
71 };
72
73 /** Indent the message if needed and forward it to the output writer
74 *
75 * If input is indented depends on the preprocessor define ALPAKA_LOG_INDENT
76 */
77 inline void indent(auto& writer, [[maybe_unused]] int indentLvl)
78 {
79#if defined(ALPAKA_LOG_INDENT)
80 for(int i = 0; i < indentLvl; ++i)
81 i == 0 ? (writer << "|-") : (writer << "--");
82 if(indentLvl)
83#endif
84 writer << " ";
85 }
86
87 /** Adjust the length of a string to a minimum length
88 *
89 * @param str input string
90 * @param n minimum number of characters, if the string is shorter than this number, it will be padded with a
91 * padding character
92 * @return new string with a minimum number of characters
93 */
94 inline std::string adjStringLength(std::string str, size_t n, char const paddingCharacter = ' ')
95 {
96 if(str.length() >= n)
97 {
98 return str;
99 }
100 str.resize(n, paddingCharacter);
101 return str;
102 }
103
104 /** shortening the function signatures to become human-readable
105 *
106 * If the name is simplified depends on the preprocessor define ALPAKA_LOG_DETAIL_SHORT
107 */
108 inline std::string adjDetails(std::string const& str)
109 {
110#if defined(ALPAKA_LOG_DETAIL_SHORT)
112#else
113 return str;
114#endif
115 }
116
117 /** Log the entry and exit of a scope */
118 template<logger::concepts::Level T_LogLvl, typename T_Writer = StdErr>
119 struct Scoped
120 {
121 public:
122 Scoped(T_LogLvl logLvl, std::source_location const& location)
123 : m_functionName{adjDetails(location.function_name())}
124 , m_prefix{std::string("[") + adjStringLength(logLvl.getName(), 6) + "]"}
125 , m_startTime{std::chrono::high_resolution_clock::now()}
126 , m_writer{T_Writer::get()}
127 {
128 m_writer << m_prefix << "[+]";
129 indent(m_writer, m_writer.enter());
130 m_writer << m_functionName << std::endl;
131 }
132
133 Scoped(T_LogLvl logLvl) : m_writer{T_Writer::get()}, m_enableOutput{false}
134 {
135 alpaka::unused(logLvl);
136 }
137
138 Scoped(Scoped const&) = delete;
139 Scoped(Scoped&&) = delete;
140 Scoped& operator=(Scoped const&) = delete;
141 Scoped& operator=(Scoped&&) = delete;
142
143 ~Scoped()
144 {
145 if(m_enableOutput)
146 {
147 auto const endTime = std::chrono::high_resolution_clock::now();
148 double durationInSeconds = std::chrono::duration<double, std::milli>(endTime - m_startTime).count();
149
150 m_writer << m_prefix << "[-]";
151 indent(m_writer, m_writer.leave());
152 m_writer << m_functionName << " " << durationInSeconds << " ms" << std::endl;
153 }
154 }
155
156 private:
157 std::string m_functionName;
158 std::string m_prefix;
159 decltype(std::chrono::high_resolution_clock::now()) m_startTime;
160 T_Writer& m_writer;
161 bool m_enableOutput = true;
162 };
163
164 /** Write a meta data message to the output
165 *
166 * @tparam T_Callable callable without arguments which provides a string which should be written to the output
167 */
168 template<logger::concepts::Level T_LogLvl, typename T_Callable, typename T_Writer = StdErr>
169 requires(std::is_invocable_r_v<std::string, T_Callable>)
170 struct Info
171 {
172 public:
173 Info(T_LogLvl logLvl, T_Callable const& callable, std::source_location const& location)
174 {
175 auto fullPrefix = std::string("[") + adjStringLength(logLvl.getName(), 6) + "]";
176
177 auto& writer = T_Writer::get();
178 std::stringstream ss;
179 ss << " ";
180 writer << fullPrefix << ss.str();
181 indent(writer, writer.current());
182 writer << callable() << " " << adjDetails(location.function_name()) << " " << location.file_name() << ":"
183 << location.line() << std::endl;
184 }
185
186 Info(Info const&) = delete;
187 Info(Info&&) = delete;
188 Info& operator=(Info const&) = delete;
189 Info& operator=(Info&&) = delete;
190
191 ~Info() = default;
192 };
193} // namespace alpaka::onHost::logger::internal
use source_location to derive the demangled type name based on: https://www.reddit....
std::string simplifyFunctionSignature(std::string const &deName)
Simplify the C++ signature of a function.
std::ostream & operator<<(std::ostream &s, DeviceProperties const &p)
constexpr decltype(auto) get(concepts::SpecializationOf< Dict > auto &t) noexcept
Definition Dict.hpp:151