Skip to content

Commit 23ac479

Browse files
authored
Extract Dart logging to the embedders (#25402)
Previously messages logged from Dart code (e.g. via the print function) were handled directly in the engine by platform-specific code. This factors out a LogMessage(tag, message) callback that embedders can implement with platform-specific code. This also eliminates a dependency on platform-specific code in the core, and provides more flexibility to embedders than the current fallback to stdout, which can be a problem on platforms without a traditional stdout or with restrictions on stdout. Fixes #79685
1 parent 079857d commit 23ac479

File tree

17 files changed

+194
-43
lines changed

17 files changed

+194
-43
lines changed

common/settings.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ using TaskObserverRemove = std::function<void(intptr_t /* key */)>;
5050
using UnhandledExceptionCallback =
5151
std::function<bool(const std::string& /* error */,
5252
const std::string& /* stack trace */)>;
53+
using LogMessageCallback =
54+
std::function<void(const std::string& /* tag */,
55+
const std::string& /* message */)>;
5356

5457
// TODO(26783): Deprecate all the "path" struct members in favor of the
5558
// callback that generates the mapping from these paths.
@@ -201,6 +204,11 @@ struct Settings {
201204
// managed thread and embedders must re-thread as necessary. Performing
202205
// blocking calls in this callback will cause applications to jank.
203206
UnhandledExceptionCallback unhandled_exception_callback;
207+
// A callback given to the embedder to log print messages from the running
208+
// Flutter application. This callback is made on an internal engine managed
209+
// thread and embedders must re-thread if necessary. Performing blocking
210+
// calls in this callback will cause applications to jank.
211+
LogMessageCallback log_message_callback;
204212
bool enable_software_rendering = false;
205213
bool skia_deterministic_rendering_on_cpu = false;
206214
bool verbose_logging = false;

lib/ui/dart_runtime_hooks.cc

Lines changed: 14 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,6 @@
2727
#include "third_party/tonic/scopes/dart_api_scope.h"
2828
#include "third_party/tonic/scopes/dart_isolate_scope.h"
2929

30-
#if defined(OS_ANDROID)
31-
#include <android/log.h>
32-
#elif defined(OS_IOS)
33-
extern "C" {
34-
// Cannot import the syslog.h header directly because of macro collision.
35-
extern void syslog(int, const char*, ...);
36-
}
37-
#endif
38-
3930
using tonic::DartConverter;
4031
using tonic::LogIfError;
4132
using tonic::ToDart;
@@ -174,18 +165,8 @@ void Logger_PrintDebugString(Dart_NativeArguments args) {
174165
// Implementation of native functions which are used for some
175166
// test/debug functionality in standalone dart mode.
176167
void Logger_PrintString(Dart_NativeArguments args) {
177-
std::stringstream stream;
178-
const auto& logger_prefix = UIDartState::Current()->logger_prefix();
179-
180-
#if !OS_ANDROID
181-
// Prepend all logs with the isolate debug name except on Android where that
182-
// prefix is specified in the log tag.
183-
if (logger_prefix.size() > 0) {
184-
stream << logger_prefix << ": ";
185-
}
186-
#endif // !OS_ANDROID
187-
188-
// Append the log buffer obtained from Dart code.
168+
// Obtain the log buffer from Dart code.
169+
std::string message;
189170
{
190171
Dart_Handle str = Dart_GetNativeArgument(args, 0);
191172
uint8_t* chars = nullptr;
@@ -196,37 +177,27 @@ void Logger_PrintString(Dart_NativeArguments args) {
196177
return;
197178
}
198179
if (length > 0) {
199-
stream << std::string{reinterpret_cast<const char*>(chars),
180+
message = std::string{reinterpret_cast<const char*>(chars),
200181
static_cast<size_t>(length)};
201182
}
202183
}
203184

204-
const auto log_string = stream.str();
205-
const char* chars = log_string.c_str();
206-
const size_t length = log_string.size();
207-
208-
// Log using platform specific mechanisms
209-
{
210-
#if defined(OS_ANDROID)
211-
// Write to the logcat on Android.
212-
__android_log_print(ANDROID_LOG_INFO, logger_prefix.c_str(), "%.*s",
213-
(int)length, chars);
214-
#elif defined(OS_IOS)
215-
// Write to syslog on iOS.
216-
//
217-
// TODO(cbracken): replace with dedicated communication channel and bypass
218-
// iOS logging APIs altogether.
219-
syslog(1 /* LOG_ALERT */, "%.*s", (int)length, chars);
220-
#else
221-
std::cout << log_string << std::endl;
222-
#endif
223-
}
185+
const auto& tag = UIDartState::Current()->logger_prefix();
186+
UIDartState::Current()->LogMessage(tag, message);
224187

225188
if (dart::bin::ShouldCaptureStdout()) {
189+
std::stringstream stream;
190+
if (tag.size() > 0) {
191+
stream << tag << ": ";
192+
}
193+
stream << message;
194+
std::string log = stream.str();
195+
226196
// For now we report print output on the Stdout stream.
227197
uint8_t newline[] = {'\n'};
228198
Dart_ServiceSendDataEvent("Stdout", "WriteEvent",
229-
reinterpret_cast<const uint8_t*>(chars), length);
199+
reinterpret_cast<const uint8_t*>(log.c_str()),
200+
log.size());
230201
Dart_ServiceSendDataEvent("Stdout", "WriteEvent", newline, sizeof(newline));
231202
}
232203
}

lib/ui/ui_dart_state.cc

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,22 @@
44

55
#include "flutter/lib/ui/ui_dart_state.h"
66

7+
#include <iostream>
8+
79
#include "flutter/fml/message_loop.h"
810
#include "flutter/lib/ui/window/platform_configuration.h"
911
#include "third_party/tonic/converter/dart_converter.h"
1012
#include "third_party/tonic/dart_message_handler.h"
1113

14+
#if defined(OS_ANDROID)
15+
#include <android/log.h>
16+
#elif defined(OS_IOS)
17+
extern "C" {
18+
// Cannot import the syslog.h header directly because of macro collision.
19+
extern void syslog(int, const char*, ...);
20+
}
21+
#endif
22+
1223
using tonic::ToDart;
1324

1425
namespace flutter {
@@ -26,6 +37,7 @@ UIDartState::UIDartState(
2637
std::string advisory_script_entrypoint,
2738
std::string logger_prefix,
2839
UnhandledExceptionCallback unhandled_exception_callback,
40+
LogMessageCallback log_message_callback,
2941
std::shared_ptr<IsolateNameServer> isolate_name_server,
3042
bool is_root_isolate,
3143
std::shared_ptr<VolatilePathTracker> volatile_path_tracker,
@@ -44,6 +56,7 @@ UIDartState::UIDartState(
4456
logger_prefix_(std::move(logger_prefix)),
4557
is_root_isolate_(is_root_isolate),
4658
unhandled_exception_callback_(unhandled_exception_callback),
59+
log_message_callback_(log_message_callback),
4760
isolate_name_server_(std::move(isolate_name_server)),
4861
enable_skparagraph_(enable_skparagraph) {
4962
AddOrRemoveTaskObserver(true /* add */);
@@ -187,6 +200,32 @@ void UIDartState::ReportUnhandledException(const std::string& error,
187200
<< stack_trace;
188201
}
189202

203+
void UIDartState::LogMessage(const std::string& tag,
204+
const std::string& message) const {
205+
if (log_message_callback_) {
206+
log_message_callback_(tag, message);
207+
} else {
208+
// Fall back to previous behavior if unspecified.
209+
#if defined(OS_ANDROID)
210+
__android_log_print(ANDROID_LOG_INFO, tag.c_str(), "%.*s",
211+
(int)message.size(), message.c_str());
212+
#elif defined(OS_IOS)
213+
std::stringstream stream;
214+
if (tag.size() > 0) {
215+
stream << tag << ": ";
216+
}
217+
stream << message;
218+
std::string log = stream.str();
219+
syslog(1 /* LOG_ALERT */, "%.*s", (int)log.size(), log.c_str());
220+
#else
221+
if (tag.size() > 0) {
222+
std::cout << tag << ": ";
223+
}
224+
std::cout << message << std::endl;
225+
#endif
226+
}
227+
}
228+
190229
bool UIDartState::enable_skparagraph() const {
191230
return enable_skparagraph_;
192231
}

lib/ui/ui_dart_state.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,14 @@ class UIDartState : public tonic::DartState {
7777
void ReportUnhandledException(const std::string& error,
7878
const std::string& stack_trace);
7979

80+
// Logs `print` messages from the application via an embedder-specified
81+
// logging mechanism.
82+
//
83+
// @param[in] tag A component name or tag that identifies the logging
84+
// application.
85+
// @param[in] message The message to be logged.
86+
void LogMessage(const std::string& tag, const std::string& message) const;
87+
8088
bool enable_skparagraph() const;
8189

8290
template <class T>
@@ -103,6 +111,7 @@ class UIDartState : public tonic::DartState {
103111
std::string advisory_script_entrypoint,
104112
std::string logger_prefix,
105113
UnhandledExceptionCallback unhandled_exception_callback,
114+
LogMessageCallback log_message_callback,
106115
std::shared_ptr<IsolateNameServer> isolate_name_server,
107116
bool is_root_isolate_,
108117
std::shared_ptr<VolatilePathTracker> volatile_path_tracker,
@@ -138,6 +147,7 @@ class UIDartState : public tonic::DartState {
138147
std::unique_ptr<PlatformConfiguration> platform_configuration_;
139148
tonic::DartMicrotaskQueue microtask_queue_;
140149
UnhandledExceptionCallback unhandled_exception_callback_;
150+
LogMessageCallback log_message_callback_;
141151
const std::shared_ptr<IsolateNameServer> isolate_name_server_;
142152
const bool enable_skparagraph_;
143153

runtime/dart_isolate.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ DartIsolate::DartIsolate(
331331
advisory_script_entrypoint,
332332
settings.log_tag,
333333
settings.unhandled_exception_callback,
334+
settings.log_message_callback,
334335
DartVMRef::GetIsolateNameServer(),
335336
is_root_isolate,
336337
std::move(volatile_path_tracker),

shell/platform/android/flutter_main.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
#include "flutter/shell/platform/android/flutter_main.h"
88

9+
#include <android/log.h>
10+
911
#include <vector>
1012

1113
#include "flutter/fml/command_line.h"
@@ -107,6 +109,12 @@ void FlutterMain::Init(JNIEnv* env,
107109
fml::MessageLoop::GetCurrent().RemoveTaskObserver(key);
108110
};
109111

112+
settings.log_message_callback = [](const std::string& tag,
113+
const std::string& message) {
114+
__android_log_print(ANDROID_LOG_INFO, tag.c_str(), "%.*s",
115+
(int)message.size(), message.c_str());
116+
};
117+
110118
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
111119
// There are no ownership concerns here as all mappings are owned by the
112120
// embedder and not the engine.

shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66

77
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h"
88

9+
#include <syslog.h>
10+
11+
#include <sstream>
12+
#include <string>
13+
914
#include "flutter/common/constants.h"
1015
#include "flutter/common/task_runners.h"
1116
#include "flutter/fml/mapping.h"
@@ -57,6 +62,18 @@
5762
fml::MessageLoop::GetCurrent().RemoveTaskObserver(key);
5863
};
5964

65+
settings.log_message_callback = [](const std::string& tag, const std::string& message) {
66+
// TODO(cbracken): replace this with os_log-based approach.
67+
// https://github.com/flutter/flutter/issues/44030
68+
std::stringstream stream;
69+
if (tag.size() > 0) {
70+
stream << tag << ": ";
71+
}
72+
stream << message;
73+
std::string log = stream.str();
74+
syslog(LOG_ALERT, "%.*s", (int)log.size(), log.c_str());
75+
};
76+
6077
// The command line arguments may not always be complete. If they aren't, attempt to fill in
6178
// defaults.
6279

shell/platform/embedder/embedder.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -953,6 +953,15 @@ FlutterEngineResult FlutterEngineInitialize(size_t version,
953953
settings.root_isolate_create_callback =
954954
[callback, user_data](const auto& isolate) { callback(user_data); };
955955
}
956+
if (SAFE_ACCESS(args, log_message_callback, nullptr) != nullptr) {
957+
FlutterLogMessageCallback callback =
958+
SAFE_ACCESS(args, log_message_callback, nullptr);
959+
settings.log_message_callback = [callback, user_data](
960+
const std::string& tag,
961+
const std::string& message) {
962+
callback(tag.c_str(), message.c_str(), user_data);
963+
};
964+
}
956965

957966
flutter::PlatformViewEmbedder::UpdateSemanticsNodesCallback
958967
update_semantics_nodes_callback = nullptr;

shell/platform/embedder/embedder.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,6 +1272,17 @@ typedef struct {
12721272
};
12731273
} FlutterEngineAOTDataSource;
12741274

1275+
// Logging callback for Dart application messages.
1276+
//
1277+
// The `tag` parameter contains a null-terminated string containing a logging
1278+
// tag or component name that can be used to identify system log messages from
1279+
// the app. The `message` parameter contains a null-terminated string
1280+
// containing the message to be logged. `user_data` is a user data baton passed
1281+
// in `FlutterEngineRun`.
1282+
typedef void (*FlutterLogMessageCallback)(const char* /* tag */,
1283+
const char* /* message */,
1284+
void* /* user_data */);
1285+
12751286
/// An opaque object that describes the AOT data that can be used to launch a
12761287
/// FlutterEngine instance in AOT mode.
12771288
typedef struct _FlutterEngineAOTData* FlutterEngineAOTData;
@@ -1495,6 +1506,13 @@ typedef struct {
14951506
/// `FlutterProjectArgs`.
14961507
const char* const* dart_entrypoint_argv;
14971508

1509+
// Logging callback for Dart application messages.
1510+
//
1511+
// This callback is used by embedder to log print messages from the running
1512+
// Flutter application. This callback is made on an internal engine managed
1513+
// thread and embedders must re-thread if necessary. Performing blocking calls
1514+
// in this callback may introduce application jank.
1515+
FlutterLogMessageCallback log_message_callback;
14981516
} FlutterProjectArgs;
14991517

15001518
#ifndef FLUTTER_ENGINE_NO_PROTOTYPES

shell/platform/embedder/fixtures/main.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,11 @@ void render_targets_are_recycled() {
875875

876876
void nativeArgumentsCallback(List<String> args) native 'NativeArgumentsCallback';
877877

878+
@pragma('vm:entry-point')
879+
void custom_logger(List<String> args) {
880+
print("hello world");
881+
}
882+
878883
@pragma('vm:entry-point')
879884
void dart_entrypoint_args(List<String> args) {
880885
nativeArgumentsCallback(args);

0 commit comments

Comments
 (0)