Skip to content

Commit 088c05e

Browse files
authored
Merge pull request #644 from elbeno/flow-logging
✨ Fine-grained logging control for flows
2 parents a342e76 + ff5cce5 commit 088c05e

File tree

11 files changed

+379
-109
lines changed

11 files changed

+379
-109
lines changed

include/flow/graph_builder.hpp

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,13 @@
2626
namespace flow {
2727
namespace detail {
2828
template <typename T> using is_duplicated = std::bool_constant<(T::size() > 1)>;
29-
}
29+
30+
template <typename CTNode, typename Output>
31+
concept is_output_compatible = requires(CTNode n) {
32+
{ Output::create_node(n) } -> std::same_as<typename Output::node_t>;
33+
};
34+
} // namespace detail
35+
3036
template <typename T> using name_for = typename T::name_t;
3137

3238
[[nodiscard]] constexpr auto edge_size(auto const &nodes,
@@ -56,12 +62,14 @@ template <stdx::ct_string Name,
5662
template <stdx::ct_string, std::size_t> typename Impl>
5763
struct graph_builder {
5864
// NOLINTBEGIN(readability-function-cognitive-complexity)
59-
template <typename Node, std::size_t N, std::size_t E>
65+
template <typename Output, std::size_t N, std::size_t E>
6066
[[nodiscard]] constexpr static auto make_graph(auto const &nodes,
6167
auto const &edges) {
62-
using graph_t = stdx::cx_multimap<Node, Node, N, E>;
68+
using output_node_t = typename Output::node_t;
69+
using graph_t = stdx::cx_multimap<output_node_t, output_node_t, N, E>;
6370
graph_t g{};
64-
for_each([&](auto const &node) { g.put(node); }, nodes);
71+
for_each([&]<typename Node>(Node n) { g.put(Output::create_node(n)); },
72+
nodes);
6573

6674
auto const named_nodes = stdx::apply_indices<name_for>(nodes);
6775
for_each(
@@ -95,7 +103,7 @@ struct graph_builder {
95103
},
96104
node_ps);
97105

98-
g.put(lhs, rhs);
106+
g.put(Output::create_node(lhs), Output::create_node(rhs));
99107
},
100108
edges);
101109
return g;
@@ -212,18 +220,16 @@ struct graph_builder {
212220
constexpr auto edge_capacity = edge_size(node_set, edges);
213221

214222
using output_t = Impl<Graph::name, node_capacity>;
215-
using rt_node_t = typename output_t::node_t;
216-
217223
static_assert(
218224
all_of(
219225
[]<typename N>(N const &) {
220-
return std::is_convertible_v<N, rt_node_t>;
226+
return detail::is_output_compatible<N, output_t>;
221227
},
222228
node_set),
223229
"Output node type is not compatible with given input nodes");
224230

225-
auto g = make_graph<rt_node_t, node_capacity, edge_capacity>(node_set,
226-
edges);
231+
auto g =
232+
make_graph<output_t, node_capacity, edge_capacity>(node_set, edges);
227233
return topo_sort<output_t>(g);
228234
}
229235

include/flow/impl.hpp

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <flow/step.hpp>
55
#include <log/log.hpp>
66

7+
#include <stdx/ct_string.hpp>
78
#include <stdx/cx_vector.hpp>
89
#include <stdx/span.hpp>
910

@@ -13,6 +14,31 @@
1314
#include <type_traits>
1415

1516
namespace flow {
17+
namespace detail {
18+
template <typename CTNode> constexpr auto run_func() -> void {
19+
if (CTNode::condition) {
20+
typename CTNode::func_t{}();
21+
}
22+
}
23+
24+
template <typename Flow, typename CTNode> constexpr auto log_func() -> void {
25+
if (CTNode::condition) {
26+
using log_spec_t = decltype(get_log_spec<CTNode, Flow>());
27+
CIB_LOG(typename log_spec_t::flavor, log_spec_t::level, "flow.{}({})",
28+
typename CTNode::type_t{}, typename CTNode::name_t{});
29+
}
30+
}
31+
} // namespace detail
32+
33+
struct rt_node {
34+
FunctionPtr run{};
35+
FunctionPtr log_name{};
36+
37+
private:
38+
friend constexpr auto operator==(rt_node const &,
39+
rt_node const &) -> bool = default;
40+
};
41+
1642
/**
1743
* flow::impl is a constant representation of a series of Milestones and actions
1844
* to be executed in a specific order.
@@ -45,11 +71,18 @@ template <stdx::ct_string Name, std::size_t NumSteps> class impl {
4571
public:
4672
stdx::cx_vector<FunctionPtr, capacity> functionPtrs{};
4773

48-
using node_t = rt_node;
4974
constexpr static bool active = capacity > 0;
50-
5175
constexpr static auto name = Name;
5276

77+
using node_t = rt_node;
78+
79+
template <typename CTNode>
80+
constexpr static auto create_node(CTNode) -> node_t {
81+
constexpr auto rf = detail::run_func<CTNode>;
82+
constexpr auto lf = detail::log_func<log_spec_id_t<Name>, CTNode>;
83+
return node_t{rf, lf};
84+
}
85+
5386
/**
5487
* Create a new flow::impl of Milestones.
5588
*
@@ -60,24 +93,24 @@ template <stdx::ct_string Name, std::size_t NumSteps> class impl {
6093
*
6194
* @see flow::builder
6295
*/
63-
constexpr explicit(true)
64-
impl(stdx::span<node_t const, NumSteps> newMilestones) {
96+
constexpr explicit(true) impl(stdx::span<node_t const, NumSteps> steps) {
6597
if constexpr (loggingEnabled) {
66-
for (auto const &milestone : newMilestones) {
67-
functionPtrs.push_back(milestone.log_name);
68-
functionPtrs.push_back(milestone.run);
98+
for (auto const &step : steps) {
99+
functionPtrs.push_back(step.log_name);
100+
functionPtrs.push_back(step.run);
69101
}
70102
} else {
71-
std::transform(std::cbegin(newMilestones), std::cend(newMilestones),
103+
std::transform(std::cbegin(steps), std::cend(steps),
72104
std::back_inserter(functionPtrs),
73-
[](auto const &milestone) { return milestone.run; });
105+
[](auto const &step) { return step.run; });
74106
}
75107
}
76108
};
77109

78110
namespace detail {
79111
template <stdx::ct_string Name, auto... FuncPtrs> struct inlined_func_list {
80112
constexpr static auto active = sizeof...(FuncPtrs) > 0;
113+
constexpr static auto ct_name = Name;
81114

82115
__attribute__((flatten, always_inline)) auto operator()() const -> void {
83116
constexpr static bool loggingEnabled = not Name.empty();
@@ -86,13 +119,17 @@ template <stdx::ct_string Name, auto... FuncPtrs> struct inlined_func_list {
86119
stdx::ct_string_to_type<Name, sc::string_constant>();
87120

88121
if constexpr (loggingEnabled) {
89-
CIB_TRACE("flow.start({})", name);
122+
using log_spec_t = decltype(get_log_spec<inlined_func_list>());
123+
CIB_LOG(typename log_spec_t::flavor, log_spec_t::level,
124+
"flow.start({})", name);
90125
}
91126

92127
(FuncPtrs(), ...);
93128

94129
if constexpr (loggingEnabled) {
95-
CIB_TRACE("flow.end({})", name);
130+
using log_spec_t = decltype(get_log_spec<inlined_func_list>());
131+
CIB_LOG(typename log_spec_t::flavor, log_spec_t::level,
132+
"flow.end({})", name);
96133
}
97134
}
98135
};

include/flow/log.hpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#pragma once
2+
3+
#include <log/log.hpp>
4+
5+
#include <stdx/ct_string.hpp>
6+
7+
#include <type_traits>
8+
9+
namespace flow {
10+
struct default_log_spec {
11+
using flavor = logging::default_flavor_t;
12+
constexpr static auto level = logging::level::TRACE;
13+
};
14+
template <stdx::ct_string, typename...>
15+
constexpr auto log_spec = default_log_spec{};
16+
17+
template <stdx::ct_string Name> struct log_spec_id_t {
18+
constexpr static auto ct_name = Name;
19+
};
20+
21+
template <typename T, typename Fallback = log_spec_id_t<"default">,
22+
typename... DummyArgs>
23+
requires(sizeof...(DummyArgs) == 0)
24+
constexpr static auto get_log_spec() {
25+
using log_spec_t = decltype(log_spec<T::ct_name, DummyArgs...>);
26+
if constexpr (std::is_same_v<log_spec_t, default_log_spec const>) {
27+
if constexpr (Fallback::ct_name == stdx::ct_string{"default"}) {
28+
return log_spec<Fallback::ct_name, DummyArgs...>;
29+
} else {
30+
return get_log_spec<Fallback>();
31+
}
32+
} else {
33+
return log_spec_t{};
34+
}
35+
}
36+
} // namespace flow

include/flow/step.hpp

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
#include <cib/detail/runtime_conditional.hpp>
44
#include <cib/func_decl.hpp>
55
#include <flow/common.hpp>
6+
#include <flow/log.hpp>
67
#include <flow/subgraph_identity.hpp>
7-
#include <log/log.hpp>
88
#include <sc/string_constant.hpp>
99

1010
#include <stdx/compiler.hpp>
@@ -14,45 +14,20 @@
1414
#include <type_traits>
1515

1616
namespace flow {
17-
struct rt_node {
18-
FunctionPtr run;
19-
FunctionPtr log_name;
20-
21-
private:
22-
friend constexpr auto operator==(rt_node const &,
23-
rt_node const &) -> bool = default;
24-
};
25-
2617
template <stdx::ct_string Type, stdx::ct_string Name,
2718
subgraph_identity Identity, typename Cond, typename F>
28-
struct ct_node : rt_node {
19+
struct ct_node {
2920
using is_subgraph = void;
3021
using type_t =
3122
decltype(stdx::ct_string_to_type<Type, sc::string_constant>());
32-
3323
using name_t =
3424
decltype(stdx::ct_string_to_type<Name, sc::string_constant>());
25+
using func_t = F;
3526

3627
constexpr static auto ct_name = Name;
37-
3828
constexpr static auto identity = Identity;
39-
4029
constexpr static auto condition = Cond{};
4130

42-
constexpr static auto run_func = [] {
43-
if (condition) {
44-
F{}();
45-
}
46-
};
47-
48-
constexpr static auto log_func = [] {
49-
if (condition) {
50-
CIB_TRACE("flow.{}({})", type_t{}, name_t{});
51-
}
52-
};
53-
54-
constexpr ct_node() : rt_node{run_func, log_func} {}
55-
5631
constexpr auto operator*() const {
5732
if constexpr (Identity == subgraph_identity::REFERENCE) {
5833
return ct_node<Type, Name, subgraph_identity::VALUE, Cond, F>{};

include/seq/impl.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ template <stdx::ct_string, std::size_t NumSteps> struct impl {
2424

2525
using node_t = rt_step;
2626

27+
template <typename CTNode>
28+
constexpr static auto create_node(CTNode n) -> node_t {
29+
return n;
30+
}
31+
2732
constexpr explicit(true) impl(stdx::span<node_t const, NumSteps> steps) {
2833
for (auto const &step : steps) {
2934
_forward_steps.push_back(step.forward_ptr);

test/flow/CMakeLists.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
add_unit_test(
2-
"flow_flow_test"
2+
"flow_separate_actions_test"
33
CATCH2
44
FILES
5-
"flow.cpp"
65
"flow_cib_func.cpp"
6+
"separate_actions.cpp"
77
LIBRARIES
88
warnings
99
cib)
1010

11-
add_tests(graph graph_builder)
11+
add_tests(flow graph graph_builder logging log_levels custom_log_levels)
1212

1313
add_subdirectory(fail)

test/flow/custom_log_levels.cpp

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#include <cib/cib.hpp>
2+
#include <flow/flow.hpp>
3+
4+
#include <catch2/catch_test_macros.hpp>
5+
6+
#include <string>
7+
#include <vector>
8+
9+
namespace {
10+
using namespace flow::literals;
11+
12+
template <auto... Cs> struct wrapper {
13+
struct inner {
14+
constexpr static auto config = cib::config(Cs...);
15+
};
16+
constexpr static auto n = cib::nexus<inner>{};
17+
18+
wrapper() { n.init(); }
19+
20+
template <typename S> static auto run() -> void { n.template service<S>(); }
21+
};
22+
23+
std::vector<logging::level> log_calls{};
24+
25+
template <typename S, auto... Cs>
26+
constexpr auto run_flow = []() -> void {
27+
log_calls.clear();
28+
wrapper<Cs...>::template run<S>();
29+
};
30+
31+
struct TestFlowA : public flow::service<"A"> {};
32+
struct TestFlowB : public flow::service<"B"> {};
33+
struct TestFlowC : public flow::service<"C"> {};
34+
35+
constexpr auto msA = flow::milestone<"msA">();
36+
constexpr auto msB = flow::milestone<"msB">();
37+
38+
struct log_config {
39+
struct log_handler {
40+
template <logging::level Level, typename ModuleId,
41+
typename FilenameStringType, typename LineNumberType,
42+
typename MsgType>
43+
auto log(FilenameStringType, LineNumberType, MsgType const &) -> void {
44+
log_calls.push_back(Level);
45+
}
46+
};
47+
log_handler logger;
48+
};
49+
} // namespace
50+
51+
template <> inline auto logging::config<> = log_config{};
52+
53+
struct user1_log_spec : flow::default_log_spec {
54+
constexpr static auto level = logging::level::USER1;
55+
};
56+
struct user2_log_spec : flow::default_log_spec {
57+
constexpr static auto level = logging::level::USER2;
58+
};
59+
struct info_log_spec : flow::default_log_spec {
60+
constexpr static auto level = logging::level::INFO;
61+
};
62+
template <> constexpr auto flow::log_spec<"default"> = info_log_spec{};
63+
64+
TEST_CASE("override default log level", "[flow_custom_log_levels]") {
65+
run_flow<TestFlowA, cib::exports<TestFlowA>,
66+
cib::extend<TestFlowA>(*msA)>();
67+
68+
REQUIRE(not log_calls.empty());
69+
std::for_each(std::begin(log_calls), std::end(log_calls),
70+
[](auto level) { CHECK(level == logging::level::INFO); });
71+
}
72+
73+
template <> constexpr auto flow::log_spec<"B"> = user1_log_spec{};
74+
template <> constexpr auto flow::log_spec<"msB"> = user2_log_spec{};
75+
76+
TEST_CASE("override log level by name", "[flow_custom_log_levels]") {
77+
run_flow<TestFlowB, cib::exports<TestFlowB>,
78+
cib::extend<TestFlowB>(*msB)>();
79+
80+
REQUIRE(log_calls.size() == 3);
81+
CHECK(log_calls[0] == logging::level::USER1);
82+
CHECK(log_calls[1] == logging::level::USER2);
83+
CHECK(log_calls[2] == logging::level::USER1);
84+
}
85+
86+
template <> constexpr auto flow::log_spec<"C"> = user1_log_spec{};
87+
88+
TEST_CASE("default log spec for step will use overridden log spec for flow",
89+
"[flow_custom_log_levels]") {
90+
run_flow<TestFlowC, cib::exports<TestFlowC>,
91+
cib::extend<TestFlowC>(*msA)>();
92+
93+
REQUIRE(log_calls.size() == 3);
94+
std::for_each(std::begin(log_calls), std::end(log_calls),
95+
[](auto level) { CHECK(level == logging::level::USER1); });
96+
}

0 commit comments

Comments
 (0)