Add Client.setLogMessageHandler to Java example.

This commit is contained in:
levlam 2022-07-08 15:01:38 +03:00
parent 20fa839dfe
commit 89aaf648fc
5 changed files with 86 additions and 45 deletions

View File

@ -39,6 +39,21 @@ public final class Client {
void onException(Throwable e);
}
/**
* Interface for handler of messages that are added to the internal TDLib log.
*/
public interface LogMessageHandler {
/**
* Callback called on messages that are added to the internal TDLib log.
*
* @param verbosityLevel Log verbosity level with which the message was added from -1 up to 1024.
* If 0, then TDLib will crash as soon as the callback returns.
* None of the TDLib methods can be called from the callback.
* @param message The message added to the internal TDLib log.
*/
void onLogMessage(int verbosityLevel, String message);
}
/**
* Sends a request to the TDLib.
*
@ -105,6 +120,17 @@ public final class Client {
return client;
}
/**
* Sets the handler for messages that are added to the internal TDLib log.
* None of the TDLib methods can be called from the callback.
*
* @param maxVerbosityLevel The maximum verbosity level of messages for which the callback will be called.
* @param logMessageHandler Handler for messages that are added to the internal TDLib log. Pass null to remove the handler.
*/
public static void setLogMessageHandler(int maxVerbosityLevel, Client.LogMessageHandler logMessageHandler) {
nativeClientSetLogMessageHandler(maxVerbosityLevel, logMessageHandler);
}
private static class ResponseReceiver implements Runnable {
public boolean isRun = false;
@ -203,4 +229,6 @@ public final class Client {
private static native int nativeClientReceive(int[] clientIds, long[] eventIds, TdApi.Object[] events, double timeout);
private static native TdApi.Object nativeClientExecute(TdApi.Function function);
private static native void nativeClientSetLogMessageHandler(int maxVerbosityLevel, LogMessageHandler logMessageHandler);
}

View File

@ -49,34 +49,4 @@ public final class Log {
*/
@Deprecated
public static native void setMaxFileSize(long maxFileSize);
/**
* This function is called from the JNI when a fatal error happens to provide a better error message.
* The function does not return.
*
* @param errorMessage Error message.
*/
private static void onFatalError(String errorMessage) {
class ThrowError implements Runnable {
private ThrowError(String errorMessage) {
this.errorMessage = errorMessage;
}
@Override
public void run() {
throw new RuntimeException("TDLib fatal error: " + errorMessage);
}
private final String errorMessage;
}
new Thread(new ThrowError(errorMessage), "TDLib fatal error thread").start();
while (true) {
try {
Thread.sleep(1000); // milliseconds
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
}

View File

@ -298,7 +298,10 @@ public final class Example {
}
public static void main(String[] args) throws InterruptedException {
// disable TDLib log
// set log message handler to handle only fatal errors (0) and plain log messages (-1)
Client.setLogMessageHandler(0, new LogMessageHandler());
// disable TDLib log and redirect fatal errors and plain log messages to a file
Client.execute(new TdApi.SetLogVerbosityLevel(0));
if (Client.execute(new TdApi.SetLogStream(new TdApi.LogStreamFile("tdlib.log", 1 << 27, false))) instanceof TdApi.Error) {
throw new IOError(new IOException("Write access to the current directory is required"));
@ -598,4 +601,14 @@ public final class Example {
}
}
}
private static class LogMessageHandler implements Client.LogMessageHandler {
@Override
public void onLogMessage(int verbosityLevel, String message) {
if (verbosityLevel == 0) {
// a fatal error, the app will crash right after the function returns
}
System.err.println(message);
}
}
}

View File

@ -99,23 +99,52 @@ static jstring Function_toString(JNIEnv *env, jobject object) {
static constexpr jint JAVA_VERSION = JNI_VERSION_1_6;
static JavaVM *java_vm;
static jclass log_class;
static jobject log_message_handler;
static void on_log_message(int verbosity_level, const char *error_message) {
if (verbosity_level != 0) {
return;
}
static void on_log_message(int verbosity_level, const char *log_message) {
auto env = td::jni::get_jni_env(java_vm, JAVA_VERSION);
if (env == nullptr) {
return;
}
jmethodID on_fatal_error_method = env->GetStaticMethodID(log_class, "onFatalError", "(Ljava/lang/String;)V");
if (on_fatal_error_method) {
jstring error_str = td::jni::to_jstring(env.get(), error_message);
env->CallStaticVoidMethod(log_class, on_fatal_error_method, error_str);
if (error_str) {
env->DeleteLocalRef(error_str);
jobject handler = env->NewLocalRef(log_message_handler);
if (!handler) {
return;
}
jclass handler_class = env->GetObjectClass(handler);
if (handler_class) {
jmethodID on_log_message_method = env->GetMethodID(handler_class, "onLogMessage", "(ILjava/lang/String;)V");
if (on_log_message_method) {
jstring log_message_str = td::jni::to_jstring(env.get(), log_message);
if (log_message_str) {
env->CallVoidMethod(handler, on_log_message_method, static_cast<jint>(verbosity_level), log_message_str);
env->DeleteLocalRef((jobject)log_message_str);
}
}
env->DeleteLocalRef((jobject)handler_class);
}
env->DeleteLocalRef(handler);
}
static void Client_nativeClientSetLogMessageHandler(JNIEnv *env, jclass clazz, jint max_verbosity_level,
jobject new_log_message_handler) {
if (log_message_handler) {
td::ClientManager::set_log_message_callback(0, nullptr);
jobject old_log_message_handler = log_message_handler;
log_message_handler = jobject();
env->DeleteGlobalRef(old_log_message_handler);
}
if (new_log_message_handler) {
log_message_handler = env->NewGlobalRef(new_log_message_handler);
if (!log_message_handler) {
// out of memory
return;
}
td::ClientManager::set_log_message_callback(static_cast<int>(max_verbosity_level), on_log_message);
}
}
@ -133,7 +162,7 @@ static jint register_native(JavaVM *vm) {
};
auto client_class = td::jni::get_jclass(env, PACKAGE_NAME "/Client");
log_class = td::jni::get_jclass(env, PACKAGE_NAME "/Log");
auto log_class = td::jni::get_jclass(env, PACKAGE_NAME "/Log");
auto object_class = td::jni::get_jclass(env, PACKAGE_NAME "/TdApi$Object");
auto function_class = td::jni::get_jclass(env, PACKAGE_NAME "/TdApi$Function");
@ -143,6 +172,8 @@ static jint register_native(JavaVM *vm) {
register_method(client_class, "nativeClientSend", "(IJ" TD_FUNCTION ")V", Client_nativeClientSend);
register_method(client_class, "nativeClientReceive", "([I[J[" TD_OBJECT "D)I", Client_nativeClientReceive);
register_method(client_class, "nativeClientExecute", "(" TD_FUNCTION ")" TD_OBJECT, Client_nativeClientExecute);
register_method(client_class, "nativeClientSetLogMessageHandler", "(IL" PACKAGE_NAME "/Client$LogMessageHandler;)V",
Client_nativeClientSetLogMessageHandler);
register_method(log_class, "setVerbosityLevel", "(I)V", Log_setVerbosityLevel);
register_method(log_class, "setFilePath", "(Ljava/lang/String;)Z", Log_setFilePath);
@ -157,7 +188,6 @@ static jint register_native(JavaVM *vm) {
td::jni::init_vars(env, PACKAGE_NAME);
td::td_api::Object::init_jni_vars(env, PACKAGE_NAME);
td::td_api::Function::init_jni_vars(env, PACKAGE_NAME);
td::ClientManager::set_log_message_callback(0, on_log_message);
return JAVA_VERSION;
}

View File

@ -28,7 +28,7 @@ using namespace CxCli;
/// <param name="verbosityLevel">Log verbosity level with which the message was added from -1 up to 1024.
/// If 0, then TDLib will crash as soon as the callback returns.
/// None of the TDLib methods can be called from the callback.</param>
/// <param name="message">Null-terminated string with the message added to the log.</param>
/// <param name="message">The message added to the log.</param>
public delegate void LogMessageCallback(int verbosityLevel, String^ message);
#endif