Skip to content

Commit fb357c2

Browse files
authored
[k2] implement user-defined tags in logs (#1386)
implement function `kphp_set_context_on_error` implement raw and contextual loggers in runtime-light
1 parent 2fbaa64 commit fb357c2

File tree

14 files changed

+431
-190
lines changed

14 files changed

+431
-190
lines changed

builtin-functions/kphp-light/stdlib/error.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
// === SUPPORTED ===
44

5-
function kphp_set_context_on_error(mixed[] $tags, mixed $extra_info, string $env = "") ::: void;
5+
function kphp_set_context_on_error(mixed[] $tags, ?mixed[] $extra_info = null, ?string $env = null) ::: void;
66

77
interface Throwable {
88
public function getMessage () ::: string;

runtime-light/state/instance-state.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "runtime-light/server/job-worker/job-worker-server-state.h"
2222
#include "runtime-light/server/rpc/rpc-server-state.h"
2323
#include "runtime-light/stdlib/curl/curl-state.h"
24+
#include "runtime-light/stdlib/diagnostics/contextual-logger.h"
2425
#include "runtime-light/stdlib/fork/fork-state.h"
2526
#include "runtime-light/stdlib/instance-cache/instance-cache-state.h"
2627
#include "runtime-light/stdlib/job-worker/job-worker-client-state.h"
@@ -79,6 +80,7 @@ struct InstanceState final : vk::not_copyable {
7980
}
8081

8182
AllocatorState instance_allocator_state{INIT_INSTANCE_ALLOCATOR_SIZE, 0};
83+
kphp::log::contextual_logger instance_logger;
8284

8385
kphp::coro::io_scheduler io_scheduler;
8486
CoroutineInstanceState coroutine_instance_state;
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Compiler for PHP (aka KPHP)
2+
// Copyright (c) 2025 LLC «V Kontakte»
3+
// Distributed under the GPL v3 License, see LICENSE.notice.txt
4+
5+
#include "runtime-light/stdlib/diagnostics/contextual-logger.h"
6+
7+
#include <cstddef>
8+
#include <functional>
9+
#include <optional>
10+
#include <span>
11+
#include <string_view>
12+
#include <utility>
13+
14+
#include "runtime-common/core/allocator/script-allocator.h"
15+
#include "runtime-common/core/std/containers.h"
16+
#include "runtime-light/k2-platform/k2-api.h"
17+
#include "runtime-light/state/instance-state.h"
18+
#include "runtime-light/stdlib/diagnostics/detail/logs.h"
19+
20+
namespace kphp::log {
21+
22+
std::optional<std::reference_wrapper<contextual_logger>> contextual_logger::try_get() noexcept {
23+
if (auto* instance_state_ptr{k2::instance_state()}; instance_state_ptr != nullptr) [[likely]] {
24+
return instance_state_ptr->instance_logger;
25+
}
26+
return std::nullopt;
27+
}
28+
29+
void contextual_logger::log_with_tags(kphp::log::level level, std::optional<std::span<void* const>> trace, std::string_view message) const noexcept {
30+
kphp::stl::vector<k2::LogTaggedEntry, kphp::memory::script_allocator> tagged_entries{};
31+
const bool is_extra_tags_enabled{level == level::warn || level == level::error};
32+
const size_t tagged_entries_size{static_cast<size_t>((trace.has_value() ? 1 : 0) + (is_extra_tags_enabled ? extra_tags.size() : 0))};
33+
tagged_entries.reserve(tagged_entries_size);
34+
if (is_extra_tags_enabled) {
35+
for (const auto& [key, value] : extra_tags) {
36+
tagged_entries.push_back(k2::LogTaggedEntry{.key = key.data(), .value = value.data(), .key_len = key.size(), .value_len = value.size()});
37+
}
38+
}
39+
if (trace.has_value()) {
40+
static constexpr size_t BACKTRACE_BUFFER_SIZE = 1024UZ * 4UZ;
41+
std::array<char, BACKTRACE_BUFFER_SIZE> backtrace_buffer; // NOLINT
42+
size_t backtrace_size{impl::resolve_log_trace(backtrace_buffer, *trace)};
43+
tagged_entries.push_back(
44+
k2::LogTaggedEntry{.key = BACKTRACE_KEY.data(), .value = backtrace_buffer.data(), .key_len = BACKTRACE_KEY.size(), .value_len = backtrace_size});
45+
k2::log(std::to_underlying(level), message, tagged_entries);
46+
return;
47+
}
48+
k2::log(std::to_underlying(level), message, tagged_entries);
49+
}
50+
51+
} // namespace kphp::log
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Compiler for PHP (aka KPHP)
2+
// Copyright (c) 2025 LLC «V Kontakte»
3+
// Distributed under the GPL v3 License, see LICENSE.notice.txt
4+
5+
#pragma once
6+
7+
#include <cstddef>
8+
#include <format>
9+
#include <functional>
10+
#include <optional>
11+
#include <span>
12+
#include <string_view>
13+
#include <utility>
14+
15+
#include "common/mixin/not_copyable.h"
16+
#include "runtime-common/core/allocator/script-allocator.h"
17+
#include "runtime-common/core/std/containers.h"
18+
#include "runtime-light/stdlib/diagnostics/detail/logs.h"
19+
20+
namespace kphp::log {
21+
22+
class contextual_logger final : vk::not_copyable {
23+
using tag_key_t = kphp::stl::string<kphp::memory::script_allocator>;
24+
using tag_value_t = kphp::stl::string<kphp::memory::script_allocator>;
25+
26+
struct extra_tags_hash final {
27+
using is_transparent = void;
28+
29+
size_t operator()(std::string_view sv) const noexcept {
30+
return std::hash<std::string_view>{}(sv);
31+
}
32+
33+
size_t operator()(const tag_key_t& s) const noexcept {
34+
return std::hash<tag_key_t>{}(s);
35+
}
36+
};
37+
38+
struct extra_tags_equal final {
39+
using is_transparent = void;
40+
41+
constexpr bool operator()(std::string_view lhs, std::string_view rhs) const noexcept {
42+
return lhs == rhs;
43+
}
44+
45+
constexpr bool operator()(const tag_key_t& lhs, const tag_key_t& rhs) const noexcept {
46+
return lhs == rhs;
47+
}
48+
49+
constexpr bool operator()(std::string_view lhs, const tag_key_t& rhs) const noexcept {
50+
return lhs == rhs;
51+
}
52+
53+
constexpr bool operator()(const tag_key_t& lhs, std::string_view rhs) const noexcept {
54+
return lhs == rhs;
55+
}
56+
};
57+
58+
static constexpr std::string_view BACKTRACE_KEY = "trace";
59+
60+
kphp::stl::unordered_map<tag_key_t, tag_value_t, kphp::memory::script_allocator, extra_tags_hash, extra_tags_equal> extra_tags;
61+
62+
void log_with_tags(kphp::log::level level, std::optional<std::span<void* const>> trace, std::string_view message) const noexcept;
63+
64+
public:
65+
template<typename... Args>
66+
void log(kphp::log::level level, std::optional<std::span<void* const>> trace, std::format_string<impl::wrapped_arg_t<Args>...> fmt,
67+
Args&&... args) const noexcept;
68+
69+
void add_extra_tag(std::string_view key, std::string_view value) noexcept;
70+
void remove_extra_tag(std::string_view key) noexcept;
71+
void clear_extra_tags() noexcept;
72+
73+
static std::optional<std::reference_wrapper<contextual_logger>> try_get() noexcept;
74+
};
75+
76+
template<typename... Args>
77+
void contextual_logger::log(kphp::log::level level, std::optional<std::span<void* const>> trace, std::format_string<impl::wrapped_arg_t<Args>...> fmt,
78+
Args&&... args) const noexcept {
79+
static constexpr size_t LOG_BUFFER_SIZE = 512UZ;
80+
std::array<char, LOG_BUFFER_SIZE> log_buffer; // NOLINT
81+
size_t message_size{impl::format_log_message(log_buffer, fmt, std::forward<Args>(args)...)};
82+
auto message{std::string_view{log_buffer.data(), static_cast<std::string_view::size_type>(message_size)}};
83+
log_with_tags(level, trace, message);
84+
}
85+
86+
inline void contextual_logger::add_extra_tag(std::string_view key, std::string_view value) noexcept {
87+
if (auto it{extra_tags.find(key)}; it == extra_tags.end()) {
88+
extra_tags.emplace(key, value);
89+
} else {
90+
it->second = value;
91+
}
92+
}
93+
94+
inline void contextual_logger::remove_extra_tag(std::string_view key) noexcept {
95+
if (auto it{extra_tags.find(key)}; it != extra_tags.end()) {
96+
extra_tags.erase(it);
97+
}
98+
}
99+
100+
inline void contextual_logger::clear_extra_tags() noexcept {
101+
extra_tags.clear();
102+
}
103+
104+
} // namespace kphp::log
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Compiler for PHP (aka KPHP)
2+
// Copyright (c) 2025 LLC «V Kontakte»
3+
// Distributed under the GPL v3 License, see LICENSE.notice.txt
4+
5+
#pragma once
6+
7+
#include <cstddef>
8+
#include <cstring>
9+
#include <format>
10+
#include <span>
11+
#include <string_view>
12+
#include <type_traits>
13+
14+
#include "runtime-light/stdlib/diagnostics/backtrace.h"
15+
16+
namespace kphp::log {
17+
18+
namespace impl {
19+
template<typename T>
20+
requires std::is_floating_point_v<std::remove_cvref_t<T>>
21+
struct floating_wrapper final {
22+
T value{};
23+
};
24+
25+
template<typename T>
26+
constexpr auto wrap_log_argument(T&& t) noexcept -> decltype(auto) {
27+
if constexpr (std::is_floating_point_v<std::remove_cvref_t<T>>) {
28+
return impl::floating_wrapper<std::remove_cvref_t<T>>{.value = std::forward<T>(t)};
29+
} else {
30+
return std::forward<T>(t);
31+
}
32+
}
33+
34+
template<typename T>
35+
using wrapped_arg_t = std::invoke_result_t<decltype(impl::wrap_log_argument<T>), T>;
36+
37+
template<typename... Args>
38+
size_t format_log_message(std::span<char> message_buffer, std::format_string<impl::wrapped_arg_t<Args>...> fmt, Args&&... args) noexcept {
39+
auto [out, size]{std::format_to_n<decltype(message_buffer.data()), impl::wrapped_arg_t<Args>...>(message_buffer.data(), message_buffer.size() - 1, fmt,
40+
impl::wrap_log_argument(std::forward<Args>(args))...)};
41+
*out = '\0';
42+
return size;
43+
}
44+
45+
inline size_t resolve_log_trace(std::span<char> trace_buffer, std::span<void* const> raw_trace) noexcept {
46+
if (trace_buffer.empty()) {
47+
return 0;
48+
}
49+
50+
if (auto backtrace_symbols{kphp::diagnostic::backtrace_symbols(raw_trace)}; !backtrace_symbols.empty()) {
51+
const auto [trace_out, trace_size]{std::format_to_n(trace_buffer.data(), trace_buffer.size() - 1, "\n{}", backtrace_symbols)};
52+
*trace_out = '\0';
53+
return trace_size;
54+
} else if (auto backtrace_addresses{kphp::diagnostic::backtrace_addresses(raw_trace)}; !backtrace_addresses.empty()) {
55+
const auto [trace_out, trace_size]{std::format_to_n(trace_buffer.data(), trace_buffer.size() - 1, "{}", backtrace_addresses)};
56+
*trace_out = '\0';
57+
return trace_size;
58+
} else {
59+
static constexpr std::string_view DEFAULT_TRACE = "[]";
60+
const auto [trace_out, trace_size]{std::format_to_n(trace_buffer.data(), trace_buffer.size() - 1, "{}", DEFAULT_TRACE)};
61+
*trace_out = '\0';
62+
return trace_size;
63+
}
64+
}
65+
66+
} // namespace impl
67+
68+
enum class level : size_t { error = 1, warn, info, debug, trace }; // NOLINT
69+
70+
} // namespace kphp::log

runtime-light/stdlib/diagnostics/logs.cpp

Lines changed: 0 additions & 45 deletions
This file was deleted.

0 commit comments

Comments
 (0)