whoami
Lucas Jenß
cat /etc/motd
The Coding Journal ツ — Notes taken on an epic coding journey. Technical solutions, debugging notes, and practical guides from the trenches of software development.
ls -la ~/languages/
- drwxr-xr-x
- ▶ PHP
- ▶ Ruby
- ▶ Scala
- ▶ C#
- ▶ JavaScript
- ▶ Objective-C
- ▶ Shell Scripting
ls -la ~/toolchain/
- drwxr-xr-x
- ▶ Typo3
- ▶ Akka
- ▶ Capistrano
- ▶ Git
- ▶ MAMP
- ▶ Adobe Illustrator
- ▶ NSTrackingArea (Cocoa)
uname -a
- drwxr-xr-x
- ▶ Mac OS X
- ▶ Unix
How to Use the Android NDK to Log Native Calls With Android Logcat
When an Android application crosses from Kotlin or Java into C and C++ code, failures can become difficult to trace. A Java stack trace may show only the JNI boundary, while the real problem is buried in a native function, a bad pointer, an invalid buffer, or an unexpected return value. The Android NDK provides the tools to add useful diagnostics directly inside that native layer.
Android logcat is the most convenient place to collect these messages during development. With the Android logging API, native code can report function entry, arguments, state changes, warnings, and errors using the same system log viewed by Android Studio, adb, or a physical handset. The method works well for projects tested across Australian devices, whether development is taking place in Sydney, Melbourne, Brisbane, or a smaller regional office.
Add Android Logging Support to Native Code
The NDK exposes Android’s logging functions through the android/log.h header. The most common function is __android_log_print, which accepts a priority, a tag, a printf-style format string, and any values required by that format.
#include <android/log.h>
#define LOG_TAG "NativeEngine"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
A native function can then log its progress without sending output through Java:
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_engine_NativeBridge_process(JNIEnv* env, jobject object, jint value) {
LOGD("process() called with value=%d", value);
if (value < 0) {
LOGW("Ignoring negative value=%d", value);
return -1;
}
int result = value * 2;
LOGI("process() returning result=%d", result);
return result;
}
The tag should identify the native subsystem rather than every individual method. A stable tag such as NativeEngine, ImageDecoder, or CryptoBridge makes filtering easier. Developers maintaining practical troubleshooting notes, such as those collected on native debugging notes, can also use consistent tags to compare output between builds and devices.
Link the Native Library Correctly
Logging code must be compiled and linked with Android’s logging library. Projects using CMake generally add log to target_link_libraries:
cmake_minimum_required(VERSION 3.22.1)
project("nativeengine")
add_library(nativeengine SHARED
native-lib.cpp
)
find_library(log-lib log)
target_link_libraries(nativeengine
${log-lib}
)
The find_library command resolves the platform library, while ${log-lib} passes it to the linker. If this step is missing, compilation may succeed but linking will fail with an undefined reference to __android_log_print or a related logging symbol.
Older NDK projects using Android.mk need a different declaration:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := nativeengine
LOCAL_SRC_FILES := native-lib.cpp
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
After changing the build file, rebuild the native module rather than relying on an old .so file in the application. ABI differences matter when testing on an x86 emulator, an arm64 handset, or an older 32-bit device. This is especially relevant when a team in Australia is testing a broad Android market, where budget phones and carrier-specific models can coexist with current flagship hardware.
Read Native Messages With Logcat
The simplest command for viewing native output is:
adb logcat
To display only messages from the selected tag, use:
adb logcat -s NativeEngine
The -s option applies a filter specification and suppresses unrelated messages. You can also set a minimum priority:
adb logcat NativeEngine:D *:S
This shows debug and higher-priority messages from NativeEngine while silencing other tags. The same filter can be entered in Android Studio’s Logcat window, where the connected device and application process can be selected from the interface.
Before collecting logs, clear old output so that a previous crash does not obscure the current test:
adb logcat -c
adb logcat -v threadtime NativeEngine:D *:S
The threadtime format includes the date, time, process ID, thread ID, priority, tag, and message. Those fields are valuable when a native call runs on a worker thread. If a bug appears only during a commute or on a particular handset in Perth, the timestamp and process details can help distinguish an application issue from a stale log.
Make Function Traces Useful
Logging every line of native code creates noise and can reduce performance. A better trace records meaningful boundaries: JNI entry, important parameters, external calls, return values, and failure paths.
LOGD("decodeFrame: width=%d height=%d size=%zu",
width, height, bufferSize);
int status = decode_frame(buffer, bufferSize, width, height);
if (status != 0) {
LOGE("decodeFrame failed: status=%d", status);
return status;
}
LOGI("decodeFrame completed successfully");
Use the correct format specifier. %d is suitable for an int, %u for an unsigned integer, %zu for size_t, and %p for a pointer. Passing the wrong type to a variadic logging function can produce misleading output or undefined behaviour. Never print a pointer as though it were an integer.
For system or library failures, include the relevant error value:
#include <cerrno>
#include <cstring>
FILE* file = fopen(path, "rb");
if (file == nullptr) {
LOGE("Unable to open '%s': errno=%d (%s)",
path, errno, strerror(errno));
}
Avoid logging private user data, authentication tokens, health information, or complete documents. Australia’s Privacy Act 1988 and the Australian Privacy Principles make careless handling of personal information a real operational concern. Logs copied into a bug tracker, uploaded from a Melbourne test device, or shared with an overseas support team may outlive the original debugging session.
Control Verbosity Across Build Types
Debug output is useful during development but should be controlled in production. A compile-time switch can remove verbose messages from release builds:
#ifdef NDEBUG
#define LOGD(...) ((void)0)
#else
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#endif
Warnings and errors can remain available when they do not reveal sensitive values. An alternative is to define a project-specific logging wrapper that supports runtime configuration. This makes it possible to enable detailed diagnostics for a test build without changing every native function.
Remember that logcat is not a permanent application log. Android may rotate or discard entries, and access to another application’s logs is restricted on modern Android versions. A production app should use a carefully designed crash-reporting or diagnostic system when persistent evidence is required. Native logging is best for local testing, integration checks, and reproducing a problem under controlled conditions.
The comparison below summarises the main choices:
| Logging method | Best use | Advantages | Limitations |
|---|---|---|---|
__android_log_print |
Native C and C++ diagnostics | Simple, fast to add, visible in logcat | Output is temporary and may expose sensitive data |
printf or stderr |
Very early experiments | Familiar desktop-style output | Capture and formatting are less predictable on Android |
| Java or Kotlin logger | Managed application code | Integrates naturally with app-level diagnostics | Does not directly show native state |
| Crash-reporting SDK | Production failure analysis | Persistent reports and device context | Requires configuration, privacy review, and network handling |
| Debugger breakpoints | Interactive investigation | Inspect memory and execution state | Slower to use and less suitable for intermittent failures |
A small, well-filtered native trace often reveals more than a large unstructured dump. Start with the JNI method, record the values crossing the boundary, then add messages around the native operation that fails. Once the cause is clear, reduce or remove diagnostic detail before shipping through Google Play or distributing the application through an Australian business channel.
Add a consistent logging wrapper to your next NDK module, rebuild the correct ABI, and reproduce the problem with adb logcat -s YourTag. That workflow turns an opaque native crash into a sequence of observable calls and gives your future troubleshooting sessions a reliable starting point.
cat ~/interests.json
| Key | Value |
|---|---|
| editor | Terminal-first workflow |
| os | Mac OS X / Unix |
| vcs | Git, distributed version control |
| deploy | Capistrano, cron automation |
| graphics | SVG, Adobe Illustrator troubleshooting |
| networking | IP validation, SSH, VPN |
git log --oneline --reverse
Solving SVG import issues in Adobe Illustrator CS6 and CC
When importing an SVG into Illustrator, the operation fails with an unknown error [CANT]. A workaround for this Adobe-side bug.
Solving NDK build issues on OS X
Troubleshooting native development kit compilation problems on Mac OS X.
Programmatically adding PHP generated TypoScript to the backend configuration
Integrating dynamically generated TypoScript into Typo3 backend setups using PHP.
ArgumentError: Could not parse PKey: no start line
Debugging an SSH key parsing error encountered during deployment.
Validating IP-Addresses in PHP
Using PHP filter functions with flags like FILTER_FLAG_IPV4 and FILTER_FLAG_IPV6, and understanding how filter_var handles reserved IP addresses.
Cocoa: Using NSTrackingArea
A short tutorial on using Cocoa's NSTrackingArea to capture mouseEntered and mouseExited events.
cat ~/contact.txt