xref: /aosp_15_r20/external/pigweed/pw_log_fuchsia/log_fuchsia.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://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, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include <fidl/fuchsia.logger/cpp/fidl.h>
16 #include <lib/component/incoming/cpp/protocol.h>
17 #include <lib/syslog/structured_backend/cpp/fuchsia_syslog.h>
18 #include <zircon/process.h>
19 
20 #include <cstdarg>
21 #include <cstdio>
22 #include <string_view>
23 
24 #include "pw_assert/check.h"
25 #include "pw_log/levels.h"
26 #include "pw_log_fuchsia/log_backend.h"
27 #include "pw_string/string_builder.h"
28 
29 namespace {
30 
31 // This is an arbitrary size limit of logs.
32 constexpr size_t kBufferSize = 400;
33 
34 // Returns the part of a path following the final '/', or the whole path if
35 // there is no '/'.
BaseName(const char * path)36 constexpr const char* BaseName(const char* path) {
37   for (const char* c = path; c && (*c != '\0'); c++) {
38     if (*c == '/') {
39       path = c + 1;
40     }
41   }
42   return path;
43 }
44 
LogLevelToString(int severity)45 const char* LogLevelToString(int severity) {
46   switch (severity) {
47     case PW_LOG_LEVEL_ERROR:
48       return "ERROR";
49     case PW_LOG_LEVEL_WARN:
50       return "WARN";
51     case PW_LOG_LEVEL_INFO:
52       return "INFO";
53     case PW_LOG_LEVEL_DEBUG:
54       return "DEBUG";
55     default:
56       return "UNKNOWN";
57   }
58 }
59 
FuchsiaLogSeverityFromFidl(fuchsia_diagnostics::Severity severity)60 FuchsiaLogSeverity FuchsiaLogSeverityFromFidl(
61     fuchsia_diagnostics::Severity severity) {
62   switch (severity) {
63     case fuchsia_diagnostics::Severity::kFatal:
64       return FUCHSIA_LOG_FATAL;
65     case fuchsia_diagnostics::Severity::kError:
66       return FUCHSIA_LOG_ERROR;
67     case fuchsia_diagnostics::Severity::kWarn:
68       return FUCHSIA_LOG_WARNING;
69     case fuchsia_diagnostics::Severity::kInfo:
70       return FUCHSIA_LOG_INFO;
71     case fuchsia_diagnostics::Severity::kDebug:
72       return FUCHSIA_LOG_DEBUG;
73     case fuchsia_diagnostics::Severity::kTrace:
74       return FUCHSIA_LOG_TRACE;
75   }
76 }
77 
PigweedLevelToFuchsiaSeverity(int pw_level)78 FuchsiaLogSeverity PigweedLevelToFuchsiaSeverity(int pw_level) {
79   switch (pw_level) {
80     case PW_LOG_LEVEL_ERROR:
81       return FUCHSIA_LOG_ERROR;
82     case PW_LOG_LEVEL_WARN:
83       return FUCHSIA_LOG_WARNING;
84     case PW_LOG_LEVEL_INFO:
85       return FUCHSIA_LOG_INFO;
86     case PW_LOG_LEVEL_DEBUG:
87       return FUCHSIA_LOG_DEBUG;
88     default:
89       return FUCHSIA_LOG_ERROR;
90   }
91 }
92 
93 class LogState {
94  public:
Initialize(async_dispatcher_t * dispatcher)95   void Initialize(async_dispatcher_t* dispatcher) {
96     dispatcher_ = dispatcher;
97 
98     auto client_end = ::component::Connect<fuchsia_logger::LogSink>();
99     PW_CHECK(client_end.is_ok());
100     log_sink_.Bind(std::move(*client_end), dispatcher_);
101 
102     zx::socket local, remote;
103     zx::socket::create(ZX_SOCKET_DATAGRAM, &local, &remote);
104     ::fidl::OneWayStatus result =
105         log_sink_->ConnectStructured(std::move(remote));
106     PW_CHECK(result.ok());
107 
108     // Get interest level synchronously to avoid dropping DEBUG logs during
109     // initialization (before an async interest response would be received).
110     ::fidl::WireResult<::fuchsia_logger::LogSink::WaitForInterestChange>
111         interest_result = log_sink_.sync()->WaitForInterestChange();
112     PW_CHECK(interest_result.ok());
113     HandleInterest(interest_result->value()->data);
114 
115     socket_ = std::move(local);
116 
117     WaitForInterestChanged();
118   }
119 
HandleInterest(fuchsia_diagnostics::wire::Interest & interest)120   void HandleInterest(fuchsia_diagnostics::wire::Interest& interest) {
121     if (!interest.has_min_severity()) {
122       severity_ = FUCHSIA_LOG_INFO;
123     } else {
124       severity_ = FuchsiaLogSeverityFromFidl(interest.min_severity());
125     }
126   }
127 
WaitForInterestChanged()128   void WaitForInterestChanged() {
129     log_sink_->WaitForInterestChange().Then(
130         [this](fidl::WireUnownedResult<
131                fuchsia_logger::LogSink::WaitForInterestChange>&
132                    interest_result) {
133           if (!interest_result.ok()) {
134             auto error = interest_result.error();
135             PW_CHECK(error.is_dispatcher_shutdown(),
136                      "%s",
137                      error.FormatDescription().c_str());
138             return;
139           }
140           HandleInterest(interest_result.value()->data);
141           WaitForInterestChanged();
142         });
143   }
144 
socket()145   zx::socket& socket() { return socket_; }
severity() const146   FuchsiaLogSeverity severity() const { return severity_; }
147 
148  private:
149   fidl::WireClient<::fuchsia_logger::LogSink> log_sink_;
150   async_dispatcher_t* dispatcher_;
151   zx::socket socket_;
152   FuchsiaLogSeverity severity_ = FUCHSIA_LOG_INFO;
153 };
154 
155 LogState log_state;
156 
GetKoid(zx_handle_t handle)157 zx_koid_t GetKoid(zx_handle_t handle) {
158   zx_info_handle_basic_t info;
159   zx_status_t status = zx_object_get_info(
160       handle, ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
161   return status == ZX_OK ? info.koid : ZX_KOID_INVALID;
162 }
163 
164 thread_local const zx_koid_t thread_koid = GetKoid(zx_thread_self());
165 zx_koid_t const process_koid = GetKoid(zx_process_self());
166 
167 }  // namespace
168 
169 namespace pw::log_fuchsia {
170 
InitializeLogging(async_dispatcher_t * dispatcher)171 void InitializeLogging(async_dispatcher_t* dispatcher) {
172   log_state.Initialize(dispatcher);
173 }
174 
175 }  // namespace pw::log_fuchsia
176 
pw_Log(int level,const char * module_name,unsigned int flags,const char * file_name,int line_number,const char * message,...)177 extern "C" void pw_Log(int level,
178                        const char* module_name,
179                        unsigned int flags,
180                        const char* file_name,
181                        int line_number,
182                        const char* message,
183                        ...) {
184   if (flags & PW_LOG_FLAG_IGNORE) {
185     return;
186   }
187 
188   pw::StringBuffer<kBufferSize> formatted;
189 
190   va_list args;
191   va_start(args, message);
192   formatted.FormatVaList(message, args);
193   va_end(args);
194 
195   if (flags & PW_LOG_FLAG_USE_PRINTF) {
196     printf("%s: [%s:%s:%d] %s\n",
197            LogLevelToString(level),
198            module_name,
199            BaseName(file_name),
200            line_number,
201            formatted.c_str());
202     return;
203   }
204 
205   FuchsiaLogSeverity fuchsia_severity = PigweedLevelToFuchsiaSeverity(level);
206   if (log_state.severity() > fuchsia_severity) {
207     return;
208   }
209 
210   ::fuchsia_syslog::LogBuffer buffer;
211   buffer.BeginRecord(fuchsia_severity,
212                      std::string_view(file_name),
213                      line_number,
214                      std::string_view(formatted.c_str()),
215                      log_state.socket().borrow(),
216                      /*dropped_count=*/0,
217                      process_koid,
218                      thread_koid);
219   buffer.WriteKeyValue("tag", module_name);
220   buffer.FlushRecord();
221 }
222