Branch data Line data Source code
1 : : // Copyright 2026 HPActor Contributors
2 : : //
3 : : // Licensed under the Apache License, Version 2.0 (the "License");
4 : : // you may not use this file except in compliance with the License.
5 : : // You may obtain a copy of the License at
6 : : //
7 : : // http://www.apache.org/licenses/LICENSE-2.0
8 : : //
9 : : // Unless required by applicable law or agreed to in writing, software
10 : : // distributed under the License is distributed on an "AS IS" BASIS,
11 : : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : : // See the License for the specific language governing permissions and
13 : : // limitations under the License.
14 : :
15 : : #include "hpactor/log/log_manager.hpp"
16 : :
17 : : #include <cassert>
18 : :
19 : : #include "hpactor/log/log_drain.hpp"
20 : : #include "hpactor/log/log_formatter.hpp"
21 : : #include "hpactor/log/log_ring_buffer.hpp"
22 : : #include "hpactor/log/log_sink.hpp"
23 : : #include "hpactor/log/logger.hpp"
24 : :
25 : : namespace hpactor::log {
26 : :
27 : : namespace {
28 : :
29 : 0 : void nudge_callback(void* ctx) noexcept {
30 : 0 : static_cast<LogDrain*>(ctx)->nudge();
31 : 0 : }
32 : :
33 : 1567 : bool is_noisy_category(LogCategory category) noexcept {
34 : 1567 : switch (category) {
35 : 559 : case LogCategory::kMailbox:
36 : : case LogCategory::kMemory:
37 : : case LogCategory::kNetwork:
38 : : case LogCategory::kActorState:
39 : : case LogCategory::kScheduler:
40 : 559 : return true;
41 : 1008 : default:
42 : 1008 : return false;
43 : : }
44 : : }
45 : :
46 : : } // namespace
47 : :
48 : 112 : LogManager::LogManager(const LogConfig& config) : config_(config) {
49 : : // 1. Validate ring_buffer_capacity is power of two (LogRingBuffer checks
50 : : // this too, but we assert here for early failure in debug builds).
51 : 112 : assert(config_.ring_buffer_capacity > 0 &&
52 : : (config_.ring_buffer_capacity & (config_.ring_buffer_capacity - 1)) == 0);
53 : :
54 : : // 2. Build per-category level thresholds.
55 : 1680 : for (size_t i = 0; i < static_cast<size_t>(LogCategory::kCount); ++i) {
56 : 1568 : auto cat = static_cast<LogCategory>(i);
57 : 1568 : LogLevel explicit_level = config_.levels[i];
58 : 1568 : if (explicit_level != LogLevel::kCritical) {
59 : : // kCritical (value 0) means "not set" in config_.levels.
60 : 1 : resolved_levels_[i] = explicit_level;
61 : 1567 : } else if (is_noisy_category(cat)) {
62 : 559 : resolved_levels_[i] = LogLevel::kWarning;
63 : : } else {
64 : 1008 : resolved_levels_[i] = config_.default_level;
65 : : }
66 : : }
67 : :
68 : : // 3. Create ring buffer.
69 : 112 : ring_buffer_ = std::make_unique<LogRingBuffer>(config_.ring_buffer_capacity);
70 : :
71 : : // 4. Create formatter.
72 : 112 : switch (config_.format) {
73 : 1 : case LogFormat::kText:
74 : 1 : formatter_ = std::make_unique<TextLogFormatter>();
75 : 1 : break;
76 : 111 : case LogFormat::kJson:
77 : : default:
78 : 111 : formatter_ = std::make_unique<JsonLogFormatter>();
79 : 111 : break;
80 : : }
81 : :
82 : : // 5. Create sinks. For now only MemorySink exists; stderr, file, and
83 : : // rotating-file sinks will be added in Tasks 14-16.
84 : : // When config_.sinks is empty, MemorySink serves as the default.
85 : : // When non-empty, future tasks will switch on LogSinkKind.
86 : 112 : sinks_.push_back(std::make_unique<MemorySink>());
87 : :
88 : : // 6. Create drain (moves sinks).
89 : 224 : drain_ = std::make_unique<LogDrain>(*ring_buffer_, *formatter_,
90 : 224 : std::move(sinks_), config_);
91 : :
92 : : // 7. Create logger.
93 : 112 : logger_ = std::make_unique<Logger>();
94 : :
95 : : // 8. Install as the global logger instance so HPACTOR_LOG_* macros work.
96 : 224 : global_logger().configure(ring_buffer_.get(), &resolved_levels_,
97 : 112 : config_.flush_on_level, nudge_callback, drain_.get());
98 : 112 : }
99 : :
100 : 112 : LogManager::~LogManager() {
101 : : // Reset the global logger to no-op so that any code still holding a
102 : : // reference does not access the ring buffer or levels array after they
103 : : // are freed. Without this, a subsequent ActorSystem whose scheduler
104 : : // workers start before its LogManager is created will dereference
105 : : // dangling pointers from the previous instance.
106 : 112 : global_logger().configure(nullptr, nullptr, LogLevel::kCritical, nullptr,
107 : : nullptr);
108 : 112 : stop();
109 : 112 : }
110 : :
111 : 111 : void LogManager::start() {
112 : 111 : drain_->start();
113 : 111 : }
114 : :
115 : 223 : void LogManager::stop() noexcept {
116 : 223 : drain_->stop();
117 : 223 : }
118 : :
119 : 2 : uint64_t LogManager::events_lost() const noexcept {
120 : 2 : return ring_buffer_->events_lost();
121 : : }
122 : :
123 : 0 : uint64_t LogManager::sink_errors() const noexcept {
124 : 0 : return drain_->sink_errors();
125 : : }
126 : :
127 : : } // namespace hpactor::log
|