- Provide a fast path for logging pre-formatted strings that are small and fit in 64 bytes without any allocations.

- Use this new feature in the tracer.
master
faluco 4 years ago committed by faluco
parent 9601770196
commit e43f555fc0

@ -18,6 +18,9 @@
namespace srslog { namespace srslog {
/// This type is used to store small strings without doing any memory allocation.
using small_str_buffer = fmt::basic_memory_buffer<char, 64>;
namespace detail { namespace detail {
/// This structure gives the user a way to log generic information as a context. /// This structure gives the user a way to log generic information as a context.
@ -31,13 +34,14 @@ struct log_context {
/// Metadata fields carried for each log entry. /// Metadata fields carried for each log entry.
struct log_entry_metadata { struct log_entry_metadata {
std::chrono::high_resolution_clock::time_point tp; std::chrono::high_resolution_clock::time_point tp;
log_context context; log_context context;
const char* fmtstring; const char* fmtstring;
fmt::dynamic_format_arg_store<fmt::printf_context> store; fmt::dynamic_format_arg_store<fmt::printf_context> store;
std::string log_name; std::string log_name;
char log_tag; char log_tag;
std::vector<uint8_t> hex_dump; small_str_buffer small_str;
std::vector<uint8_t> hex_dump;
}; };
} // namespace detail } // namespace detail

@ -111,7 +111,7 @@ public:
// Did we wake up on timeout? // Did we wake up on timeout?
if (timedout && queue.empty()) { if (timedout && queue.empty()) {
cond_var.unlock(); cond_var.unlock();
return {false, T{}}; return {false, T()};
} }
// Here we have been woken up normally. // Here we have been woken up normally.

@ -113,7 +113,34 @@ public:
fmtstr, fmtstr,
std::move(store), std::move(store),
log_name, log_name,
log_tag}}; log_tag,
small_str_buffer()}};
backend.push(std::move(entry));
}
/// Builds the provided log entry and passes it to the backend. When the
/// channel is disabled the log entry will be discarded.
void operator()(small_str_buffer &&str)
{
if (!enabled()) {
return;
}
// Send the log entry to the backend.
log_formatter& formatter = log_sink.get_formatter();
detail::log_entry entry = {
&log_sink,
[&formatter](detail::log_entry_metadata&& metadata,
fmt::memory_buffer& buffer) {
formatter.format(std::move(metadata), buffer);
},
{std::chrono::high_resolution_clock::now(),
{ctx_value, should_print_context},
nullptr,
{},
log_name,
log_tag,
std::move(str)}};
backend.push(std::move(entry)); backend.push(std::move(entry));
} }
@ -152,6 +179,7 @@ public:
std::move(store), std::move(store),
log_name, log_name,
log_tag, log_tag,
small_str_buffer(),
std::vector<uint8_t>(buffer, buffer + len)}}; std::vector<uint8_t>(buffer, buffer + len)}};
backend.push(std::move(entry)); backend.push(std::move(entry));
} }
@ -178,7 +206,8 @@ public:
nullptr, nullptr,
{}, {},
log_name, log_name,
log_tag}}; log_tag,
small_str_buffer()}};
backend.push(std::move(entry)); backend.push(std::move(entry));
} }
@ -209,7 +238,8 @@ public:
fmtstr, fmtstr,
std::move(store), std::move(store),
log_name, log_name,
log_tag}}; log_tag,
small_str_buffer()}};
backend.push(std::move(entry)); backend.push(std::move(entry));
} }

@ -90,6 +90,11 @@ void backend_worker::process_log_entry(detail::log_entry&& entry)
assert(entry.format_func && "Invalid format function"); assert(entry.format_func && "Invalid format function");
fmt_buffer.clear(); fmt_buffer.clear();
// Already formatted strings in the foreground are passed to the formatter as the fmtstring.
if (entry.metadata.small_str.size()) {
entry.metadata.fmtstring = entry.metadata.small_str.data();
}
entry.format_func(std::move(entry.metadata), fmt_buffer); entry.format_func(std::move(entry.metadata), fmt_buffer);
if (auto err_str = entry.s->write({fmt_buffer.data(), fmt_buffer.size()})) { if (auto err_str = entry.s->write({fmt_buffer.data(), fmt_buffer.size()})) {
@ -99,8 +104,7 @@ void backend_worker::process_log_entry(detail::log_entry&& entry)
void backend_worker::process_outstanding_entries() void backend_worker::process_outstanding_entries()
{ {
assert(!running_flag && assert(!running_flag && "Cannot process outstanding entries while thread is running");
"Cannot process outstanding entries while thread is running");
while (true) { while (true) {
auto item = queue.timed_pop(1); auto item = queue.timed_pop(1);

@ -124,17 +124,18 @@ void trace_duration_end(const std::string& category, const std::string& name)
/// Private implementation of the complete event destructor. /// Private implementation of the complete event destructor.
srslog::detail::scoped_complete_event::~scoped_complete_event() srslog::detail::scoped_complete_event::~scoped_complete_event()
{ {
if (!tracer) if (!tracer) {
return; return;
}
auto end = std::chrono::steady_clock::now(); auto end = std::chrono::steady_clock::now();
unsigned long long diff = unsigned long long diff =
std::chrono::duration_cast<std::chrono::microseconds>(end - start) std::chrono::duration_cast<std::chrono::microseconds>(end - start)
.count(); .count();
(*tracer)("[TID:%0u] Complete event \"%s\" (duration %lld us): %s", small_str_buffer str;
(unsigned)::pthread_self(), // Limit to the category and name strings to a predefined length so everything fits in a small string.
category, fmt::format_to(str, "{:.32} {:.16}, {}", category, name, diff);
diff, str.push_back('\0');
name); (*tracer)(std::move(str));
} }

@ -27,7 +27,7 @@ static detail::log_entry_metadata build_log_entry_metadata()
fmt::dynamic_format_arg_store<fmt::printf_context> store; fmt::dynamic_format_arg_store<fmt::printf_context> store;
store.push_back(88); store.push_back(88);
return {tp, {10, true}, "Text %d", std::move(store), "ABC", 'Z'}; return {tp, {10, true}, "Text %d", std::move(store), "ABC", 'Z', small_str_buffer()};
} }
static bool when_fully_filled_log_entry_then_everything_is_formatted() static bool when_fully_filled_log_entry_then_everything_is_formatted()

@ -70,19 +70,6 @@ private:
} // namespace } // namespace
static bool when_backend_is_not_started_then_pushed_log_entries_are_ignored()
{
sink_spy spy;
log_backend_impl backend;
detail::log_entry entry = {&spy};
backend.push(std::move(entry));
ASSERT_EQ(spy.write_invocation_count(), 0);
return true;
}
/// Builds a basic log entry. /// Builds a basic log entry.
static detail::log_entry build_log_entry(sink* s) static detail::log_entry build_log_entry(sink* s)
{ {
@ -95,7 +82,19 @@ static detail::log_entry build_log_entry(sink* s)
return { return {
s, s,
[](detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer) {}, [](detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer) {},
{tp, {0, false}, "Text %d", std::move(store), "", '\0'}}; {tp, {0, false}, "Text %d", std::move(store), "", '\0', small_str_buffer()}};
}
static bool when_backend_is_not_started_then_pushed_log_entries_are_ignored()
{
sink_spy spy;
log_backend_impl backend;
backend.push(build_log_entry(&spy));
ASSERT_EQ(spy.write_invocation_count(), 0);
return true;
} }
static bool when_backend_is_started_then_pushed_log_entries_are_sent_to_sink() static bool when_backend_is_started_then_pushed_log_entries_are_sent_to_sink()

@ -278,6 +278,30 @@ when_logging_with_context_and_message_then_filled_in_log_entry_is_pushed_into_th
return true; return true;
} }
static bool
when_logging_with_small_string_then_filled_in_log_entry_is_pushed_into_the_backend()
{
backend_spy backend;
test_dummies::sink_dummy s;
log_channel log("id", s, backend);
small_str_buffer buf;
fmt::format_to(buf, "A {} {} {}", 1, 2, 3);
log(std::move(buf));
ASSERT_EQ(backend.push_invocation_count(), 1);
const detail::log_entry& entry = backend.last_entry();
ASSERT_EQ(&s, entry.s);
ASSERT_NE(entry.format_func, nullptr);
ASSERT_NE(entry.metadata.tp.time_since_epoch().count(), 0);
ASSERT_EQ(entry.metadata.hex_dump.empty(), true);
ASSERT_EQ(std::string(entry.metadata.small_str.data()), "A 1 2 3");
return true;
}
int main() int main()
{ {
TEST_FUNCTION(when_log_channel_is_created_then_id_matches_expected_value); TEST_FUNCTION(when_log_channel_is_created_then_id_matches_expected_value);
@ -296,6 +320,8 @@ int main()
when_logging_with_context_then_filled_in_log_entry_is_pushed_into_the_backend); when_logging_with_context_then_filled_in_log_entry_is_pushed_into_the_backend);
TEST_FUNCTION( TEST_FUNCTION(
when_logging_with_context_and_message_then_filled_in_log_entry_is_pushed_into_the_backend); when_logging_with_context_and_message_then_filled_in_log_entry_is_pushed_into_the_backend);
TEST_FUNCTION(
when_logging_with_small_string_then_filled_in_log_entry_is_pushed_into_the_backend);
return 0; return 0;
} }

@ -27,7 +27,7 @@ static detail::log_entry_metadata build_log_entry_metadata()
fmt::dynamic_format_arg_store<fmt::printf_context> store; fmt::dynamic_format_arg_store<fmt::printf_context> store;
store.push_back(88); store.push_back(88);
return {tp, {10, true}, "Text %d", std::move(store), "ABC", 'Z'}; return {tp, {10, true}, "Text %d", std::move(store), "ABC", 'Z', small_str_buffer()};
} }
static bool when_fully_filled_log_entry_then_everything_is_formatted() static bool when_fully_filled_log_entry_then_everything_is_formatted()

Loading…
Cancel
Save