Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions common/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ using TaskObserverRemove = std::function<void(intptr_t /* key */)>;
using UnhandledExceptionCallback =
std::function<bool(const std::string& /* error */,
const std::string& /* stack trace */)>;
using LogMessageCallback =
std::function<void(const std::string& /* tag */,
const std::string& /* message */)>;

// TODO(26783): Deprecate all the "path" struct members in favor of the
// callback that generates the mapping from these paths.
Expand Down Expand Up @@ -201,6 +204,11 @@ struct Settings {
// managed thread and embedders must re-thread as necessary. Performing
// blocking calls in this callback will cause applications to jank.
UnhandledExceptionCallback unhandled_exception_callback;
// A callback given to the embedder to log print messages from the running
// Flutter application. This callback is made on an internal engine managed
// thread and embedders must re-thread if necessary. Performing blocking
// calls in this callback will cause applications to jank.
LogMessageCallback log_message_callback;
bool enable_software_rendering = false;
bool skia_deterministic_rendering_on_cpu = false;
bool verbose_logging = false;
Expand Down
57 changes: 14 additions & 43 deletions lib/ui/dart_runtime_hooks.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,6 @@
#include "third_party/tonic/scopes/dart_api_scope.h"
#include "third_party/tonic/scopes/dart_isolate_scope.h"

#if defined(OS_ANDROID)
#include <android/log.h>
#elif defined(OS_IOS)
extern "C" {
// Cannot import the syslog.h header directly because of macro collision.
extern void syslog(int, const char*, ...);
}
#endif

using tonic::DartConverter;
using tonic::LogIfError;
using tonic::ToDart;
Expand Down Expand Up @@ -174,18 +165,8 @@ void Logger_PrintDebugString(Dart_NativeArguments args) {
// Implementation of native functions which are used for some
// test/debug functionality in standalone dart mode.
void Logger_PrintString(Dart_NativeArguments args) {
std::stringstream stream;
const auto& logger_prefix = UIDartState::Current()->logger_prefix();

#if !OS_ANDROID
// Prepend all logs with the isolate debug name except on Android where that
// prefix is specified in the log tag.
if (logger_prefix.size() > 0) {
stream << logger_prefix << ": ";
}
#endif // !OS_ANDROID

// Append the log buffer obtained from Dart code.
// Obtain the log buffer from Dart code.
std::string message;
{
Dart_Handle str = Dart_GetNativeArgument(args, 0);
uint8_t* chars = nullptr;
Expand All @@ -196,37 +177,27 @@ void Logger_PrintString(Dart_NativeArguments args) {
return;
}
if (length > 0) {
stream << std::string{reinterpret_cast<const char*>(chars),
message = std::string{reinterpret_cast<const char*>(chars),
static_cast<size_t>(length)};
}
}

const auto log_string = stream.str();
const char* chars = log_string.c_str();
const size_t length = log_string.size();

// Log using platform specific mechanisms
{
#if defined(OS_ANDROID)
// Write to the logcat on Android.
__android_log_print(ANDROID_LOG_INFO, logger_prefix.c_str(), "%.*s",
(int)length, chars);
#elif defined(OS_IOS)
// Write to syslog on iOS.
//
// TODO(cbracken): replace with dedicated communication channel and bypass
// iOS logging APIs altogether.
syslog(1 /* LOG_ALERT */, "%.*s", (int)length, chars);
#else
std::cout << log_string << std::endl;
#endif
}
const auto& tag = UIDartState::Current()->logger_prefix();
UIDartState::Current()->LogMessage(tag, message);

if (dart::bin::ShouldCaptureStdout()) {
std::stringstream stream;
if (tag.size() > 0) {
stream << tag << ": ";
}
stream << message;
std::string log = stream.str();

// For now we report print output on the Stdout stream.
uint8_t newline[] = {'\n'};
Dart_ServiceSendDataEvent("Stdout", "WriteEvent",
reinterpret_cast<const uint8_t*>(chars), length);
reinterpret_cast<const uint8_t*>(log.c_str()),
log.size());
Dart_ServiceSendDataEvent("Stdout", "WriteEvent", newline, sizeof(newline));
}
}
Expand Down
39 changes: 39 additions & 0 deletions lib/ui/ui_dart_state.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,22 @@

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

#include <iostream>

#include "flutter/fml/message_loop.h"
#include "flutter/lib/ui/window/platform_configuration.h"
#include "third_party/tonic/converter/dart_converter.h"
#include "third_party/tonic/dart_message_handler.h"

#if defined(OS_ANDROID)
#include <android/log.h>
#elif defined(OS_IOS)
extern "C" {
// Cannot import the syslog.h header directly because of macro collision.
extern void syslog(int, const char*, ...);
}
#endif

using tonic::ToDart;

namespace flutter {
Expand All @@ -26,6 +37,7 @@ UIDartState::UIDartState(
std::string advisory_script_entrypoint,
std::string logger_prefix,
UnhandledExceptionCallback unhandled_exception_callback,
LogMessageCallback log_message_callback,
std::shared_ptr<IsolateNameServer> isolate_name_server,
bool is_root_isolate,
std::shared_ptr<VolatilePathTracker> volatile_path_tracker,
Expand All @@ -44,6 +56,7 @@ UIDartState::UIDartState(
logger_prefix_(std::move(logger_prefix)),
is_root_isolate_(is_root_isolate),
unhandled_exception_callback_(unhandled_exception_callback),
log_message_callback_(log_message_callback),
isolate_name_server_(std::move(isolate_name_server)),
enable_skparagraph_(enable_skparagraph) {
AddOrRemoveTaskObserver(true /* add */);
Expand Down Expand Up @@ -187,6 +200,32 @@ void UIDartState::ReportUnhandledException(const std::string& error,
<< stack_trace;
}

void UIDartState::LogMessage(const std::string& tag,
const std::string& message) const {
if (log_message_callback_) {
log_message_callback_(tag, message);
} else {
// Fall back to previous behavior if unspecified.
#if defined(OS_ANDROID)
__android_log_print(ANDROID_LOG_INFO, tag.c_str(), "%.*s",
(int)message.size(), message.c_str());
#elif defined(OS_IOS)
std::stringstream stream;
if (tag.size() > 0) {
stream << tag << ": ";
}
stream << message;
std::string log = stream.str();
syslog(1 /* LOG_ALERT */, "%.*s", (int)log.size(), log.c_str());
#else
if (tag.size() > 0) {
std::cout << tag << ": ";
}
std::cout << message << std::endl;
#endif
}
}

bool UIDartState::enable_skparagraph() const {
return enable_skparagraph_;
}
Expand Down
10 changes: 10 additions & 0 deletions lib/ui/ui_dart_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ class UIDartState : public tonic::DartState {
void ReportUnhandledException(const std::string& error,
const std::string& stack_trace);

// Logs `print` messages from the application via an embedder-specified
// logging mechanism.
//
// @param[in] tag A component name or tag that identifies the logging
// application.
// @param[in] message The message to be logged.
void LogMessage(const std::string& tag, const std::string& message) const;

bool enable_skparagraph() const;

template <class T>
Expand All @@ -103,6 +111,7 @@ class UIDartState : public tonic::DartState {
std::string advisory_script_entrypoint,
std::string logger_prefix,
UnhandledExceptionCallback unhandled_exception_callback,
LogMessageCallback log_message_callback,
std::shared_ptr<IsolateNameServer> isolate_name_server,
bool is_root_isolate_,
std::shared_ptr<VolatilePathTracker> volatile_path_tracker,
Expand Down Expand Up @@ -138,6 +147,7 @@ class UIDartState : public tonic::DartState {
std::unique_ptr<PlatformConfiguration> platform_configuration_;
tonic::DartMicrotaskQueue microtask_queue_;
UnhandledExceptionCallback unhandled_exception_callback_;
LogMessageCallback log_message_callback_;
const std::shared_ptr<IsolateNameServer> isolate_name_server_;
const bool enable_skparagraph_;

Expand Down
1 change: 1 addition & 0 deletions runtime/dart_isolate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ DartIsolate::DartIsolate(
advisory_script_entrypoint,
settings.log_tag,
settings.unhandled_exception_callback,
settings.log_message_callback,
DartVMRef::GetIsolateNameServer(),
is_root_isolate,
std::move(volatile_path_tracker),
Expand Down
8 changes: 8 additions & 0 deletions shell/platform/android/flutter_main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

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

#include <android/log.h>

#include <vector>

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

settings.log_message_callback = [](const std::string& tag,
const std::string& message) {
__android_log_print(ANDROID_LOG_INFO, tag.c_str(), "%.*s",
(int)message.size(), message.c_str());
};

#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
// There are no ownership concerns here as all mappings are owned by the
// embedder and not the engine.
Expand Down
17 changes: 17 additions & 0 deletions shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@

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

#include <syslog.h>

#include <sstream>
#include <string>

#include "flutter/common/constants.h"
#include "flutter/common/task_runners.h"
#include "flutter/fml/mapping.h"
Expand Down Expand Up @@ -57,6 +62,18 @@
fml::MessageLoop::GetCurrent().RemoveTaskObserver(key);
};

settings.log_message_callback = [](const std::string& tag, const std::string& message) {
// TODO(cbracken): replace this with os_log-based approach.
// https://github.com/flutter/flutter/issues/44030
std::stringstream stream;
if (tag.size() > 0) {
stream << tag << ": ";
}
stream << message;
std::string log = stream.str();
syslog(LOG_ALERT, "%.*s", (int)log.size(), log.c_str());
};

// The command line arguments may not always be complete. If they aren't, attempt to fill in
// defaults.

Expand Down
9 changes: 9 additions & 0 deletions shell/platform/embedder/embedder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,15 @@ FlutterEngineResult FlutterEngineInitialize(size_t version,
settings.root_isolate_create_callback =
[callback, user_data](const auto& isolate) { callback(user_data); };
}
if (SAFE_ACCESS(args, log_message_callback, nullptr) != nullptr) {
FlutterLogMessageCallback callback =
SAFE_ACCESS(args, log_message_callback, nullptr);
settings.log_message_callback = [callback, user_data](
const std::string& tag,
const std::string& message) {
callback(tag.c_str(), message.c_str(), user_data);
};
}

flutter::PlatformViewEmbedder::UpdateSemanticsNodesCallback
update_semantics_nodes_callback = nullptr;
Expand Down
18 changes: 18 additions & 0 deletions shell/platform/embedder/embedder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1272,6 +1272,17 @@ typedef struct {
};
} FlutterEngineAOTDataSource;

// Logging callback for Dart application messages.
//
// The `tag` parameter contains a null-terminated string containing a logging
// tag or component name that can be used to identify system log messages from
// the app. The `message` parameter contains a null-terminated string
// containing the message to be logged. `user_data` is a user data baton passed
// in `FlutterEngineRun`.
typedef void (*FlutterLogMessageCallback)(const char* /* tag */,
const char* /* message */,
void* /* user_data */);

/// An opaque object that describes the AOT data that can be used to launch a
/// FlutterEngine instance in AOT mode.
typedef struct _FlutterEngineAOTData* FlutterEngineAOTData;
Expand Down Expand Up @@ -1495,6 +1506,13 @@ typedef struct {
/// `FlutterProjectArgs`.
const char* const* dart_entrypoint_argv;

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

#ifndef FLUTTER_ENGINE_NO_PROTOTYPES
Expand Down
5 changes: 5 additions & 0 deletions shell/platform/embedder/fixtures/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,11 @@ void render_targets_are_recycled() {

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

@pragma('vm:entry-point')
void custom_logger(List<String> args) {
print("hello world");
}

@pragma('vm:entry-point')
void dart_entrypoint_args(List<String> args) {
nativeArgumentsCallback(args);
Expand Down
6 changes: 6 additions & 0 deletions shell/platform/embedder/tests/embedder_config_builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ EmbedderConfigBuilder::EmbedderConfigBuilder(
SetAssetsPath();
SetIsolateCreateCallbackHook();
SetSemanticsCallbackHooks();
SetLogMessageCallbackHook();
SetLocalizationCallbackHooks();
AddCommandLineArgument("--disable-observatory");

Expand Down Expand Up @@ -211,6 +212,11 @@ void EmbedderConfigBuilder::SetSemanticsCallbackHooks() {
EmbedderTestContext::GetUpdateSemanticsCustomActionCallbackHook();
}

void EmbedderConfigBuilder::SetLogMessageCallbackHook() {
project_args_.log_message_callback =
EmbedderTestContext::GetLogMessageCallbackHook();
}

void EmbedderConfigBuilder::SetLocalizationCallbackHooks() {
project_args_.compute_platform_resolved_locale_callback =
EmbedderTestContext::GetComputePlatformResolvedLocaleCallbackHook();
Expand Down
2 changes: 2 additions & 0 deletions shell/platform/embedder/tests/embedder_config_builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ class EmbedderConfigBuilder {

void SetSemanticsCallbackHooks();

void SetLogMessageCallbackHook();

void SetLocalizationCallbackHooks();

void SetDartEntrypoint(std::string entrypoint);
Expand Down
14 changes: 14 additions & 0 deletions shell/platform/embedder/tests/embedder_test_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ void EmbedderTestContext::PlatformMessageCallback(
}
}

void EmbedderTestContext::SetLogMessageCallback(
const LogMessageCallback& callback) {
log_message_callback_ = callback;
}

FlutterUpdateSemanticsNodeCallback
EmbedderTestContext::GetUpdateSemanticsNodeCallbackHook() {
return [](const FlutterSemanticsNode* semantics_node, void* user_data) {
Expand All @@ -160,6 +165,15 @@ EmbedderTestContext::GetUpdateSemanticsCustomActionCallbackHook() {
};
}

FlutterLogMessageCallback EmbedderTestContext::GetLogMessageCallbackHook() {
return [](const char* tag, const char* message, void* user_data) {
auto context = reinterpret_cast<EmbedderTestContext*>(user_data);
if (auto callback = context->log_message_callback_) {
callback(tag, message);
}
};
}

FlutterComputePlatformResolvedLocaleCallback
EmbedderTestContext::GetComputePlatformResolvedLocaleCallbackHook() {
return [](const FlutterLocale** supported_locales,
Expand Down
Loading