Skip to content

Commit

Permalink
Enhance Android Logging with setLogCallback API (#35254)
Browse files Browse the repository at this point in the history
* Enhance Android Logging with setLogCallback API

This change adds the setLogCallback API to the AndroidChipLogging class
of the Matter SDK, enhancing its logging capabilities. With this API,
developers can customize logging behavior and integrate it with
Android's logging system, allowing for more flexible and comprehensive
logging.
An example of usage in Kotlin is as follows:
ChipDeviceController.loadJni()
AndroidChipLogging.setLogCallback { module, priority, message ->
    when (priority) {
        Log.ERROR -> Log.e(module, message)
        Log.INFO -> Log.i(module, message)
        Log.DEBUG -> Log.d(module, message)
    }
}

Signed-off-by: Youngho Yoon <34558998+yhoyoon@users.noreply.github.com>

* Add s prefix for global variables

Signed-off-by: Youngho Yoon <34558998+yhoyoon@users.noreply.github.com>

* Do not call onLogMessage callback when msg is null

Handle the case where jMsg is null due to UTF decode errors in buffer.
Don't call the callback when the string is null.

Signed-off-by: Youngho Yoon <34558998+yhoyoon@users.noreply.github.com>

* Return logRedirectCallback when env is nullptr

Signed-off-by: Youngho Yoon <34558998+yhoyoon@users.noreply.github.com>

* Use JniLocalReferenceScope instead of DeleteLocalRef

Signed-off-by: Youngho Yoon <34558998+yhoyoon@users.noreply.github.com>

* Change type of global ref to JniGlobalReference

Signed-off-by: Youngho Yoon <34558998+yhoyoon@users.noreply.github.com>

* Use CharToStringUTF instead of NewStringUTF

Signed-off-by: Youngho Yoon <34558998+yhoyoon@users.noreply.github.com>

* Use VerifyOrReturn for CharToStringUTF

Signed-off-by: Youngho Yoon <34558998+yhoyoon@users.noreply.github.com>

---------

Signed-off-by: Youngho Yoon <34558998+yhoyoon@users.noreply.github.com>
  • Loading branch information
yhoyoon authored Sep 3, 2024
1 parent 7f33e09 commit 5dd517c
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 0 deletions.
72 changes: 72 additions & 0 deletions src/platform/android/AndroidChipPlatform-JNI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ static bool JavaBytesToUUID(JNIEnv * env, jbyteArray value, chip::Ble::ChipBleUU
namespace {
JavaVM * sJVM = nullptr;
JniGlobalReference sAndroidChipPlatformExceptionCls;
jmethodID sOnLogMessageMethod = nullptr;
JniGlobalReference sJavaLogCallbackObject;
} // namespace

CHIP_ERROR AndroidChipPlatformJNI_OnLoad(JavaVM * jvm, void * reserved)
Expand Down Expand Up @@ -274,6 +276,76 @@ JNI_LOGGING_METHOD(void, setLogFilter)(JNIEnv * env, jclass clazz, jint level)
SetLogFilter(category);
}

static void ENFORCE_FORMAT(3, 0) logRedirectCallback(const char * module, uint8_t category, const char * msg, va_list args)
{
using namespace chip::Logging;

JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr);
VerifyOrReturn(sJavaLogCallbackObject.HasValidObjectRef());
VerifyOrReturn(sOnLogMessageMethod != nullptr);

JniLocalReferenceScope scope(env);
int priority = ANDROID_LOG_DEBUG;
switch (category)
{
case kLogCategory_Error:
priority = ANDROID_LOG_ERROR;
break;
case kLogCategory_Progress:
priority = ANDROID_LOG_INFO;
break;
case kLogCategory_Detail:
priority = ANDROID_LOG_DEBUG;
break;
default:
break;
}

jint jPriority = static_cast<jint>(priority);
jobject jModule;
VerifyOrReturn(JniReferences::GetInstance().CharToStringUTF(CharSpan::fromCharString(module), jModule) == CHIP_NO_ERROR);
VerifyOrReturn(jModule != nullptr);

char buffer[CHIP_CONFIG_LOG_MESSAGE_MAX_SIZE];
vsnprintf(buffer, sizeof(buffer), msg, args);
jobject jMsg;
VerifyOrReturn(JniReferences::GetInstance().CharToStringUTF(CharSpan::fromCharString(buffer), jMsg) == CHIP_NO_ERROR);
VerifyOrReturn(jMsg != nullptr);

env->CallVoidMethod(sJavaLogCallbackObject.ObjectRef(), sOnLogMessageMethod, static_cast<jstring>(jModule), jPriority,
static_cast<jstring>(jMsg));
}

JNI_LOGGING_METHOD(void, setLogCallback)(JNIEnv * env, jclass clazz, jobject callback)
{
using namespace chip::Logging;

if (sOnLogMessageMethod == nullptr)
{
jclass callbackClass = env->GetObjectClass(callback);
sOnLogMessageMethod = env->GetMethodID(callbackClass, "onLogMessage", "(Ljava/lang/String;ILjava/lang/String;)V");
}
VerifyOrReturn(sOnLogMessageMethod != nullptr,
ChipLogError(DeviceLayer, "Failed to access AndroidChipLogging.LogCallback 'onLogMessage' method"));

if (sJavaLogCallbackObject.HasValidObjectRef())
{
sJavaLogCallbackObject.Reset();
}

if (env->IsSameObject(callback, NULL))
{
SetLogRedirectCallback(nullptr);
}
else
{
VerifyOrReturn(sJavaLogCallbackObject.Init(callback) == CHIP_NO_ERROR,
ChipLogError(DeviceLayer, "Failed to init sJavaLogCallbackObject"));
SetLogRedirectCallback(logRedirectCallback);
}
}

JNI_MDNSCALLBACK_METHOD(void, handleServiceResolve)
(JNIEnv * env, jclass self, jstring instanceName, jstring serviceType, jstring hostName, jstring address, jint port,
jobject attributes, jlong callbackHandle, jlong contextHandle)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,11 @@
public class AndroidChipLogging {
// logging level is in android.util.Log class
public static native void setLogFilter(int level);

// This must be called after System.loadLibrary
public static native void setLogCallback(LogCallback callback);

public interface LogCallback {
void onLogMessage(String module, int priority, String message);
}
}

0 comments on commit 5dd517c

Please sign in to comment.