Logging Without Headers

Sometimes you find yourself looking at a C++ file where you would love to log something out quickly, but it may be a pain to properly plumb your logging system into it properly for a one-off bit of investigation.

Here are some recipes to get your stuff logged with minimal hassle if you can edit the code.

Android

In Android, logging to logcat is often the easiest way to get something out quickly. There is a native API to do this with a simple #include <log.h>, but of course we can do without.

The simplest way is to declare this function.

extern "C" int __android_log_write(
  int prio,
  const char *tag,
  const char *text
);

If you already have varargs, you can do the version that will format for you.

extern "C" int __android_log_print(
  int prio,
  const char *tag,
  const char *fmt,
  ...
);

Finally, you'll want to use the right constant for the priority level, which will be one of these:

enum android_LogPriority {
  ANDROID_LOG_UNKNOWN = 0,
  ANDROID_LOG_DEFAULT = 1,
  ANDROID_LOG_VERBOSE = 2,
  ANDROID_LOG_DEBUG = 3,
  ANDROID_LOG_INFO = 4,
  ANDROID_LOG_WARN = 5,
  ANDROID_LOG_ERROR = 6,
  ANDROID_LOG_FATAL = 7,
  ANDROID_LOG_SILENT = 8
};

You can simply replace with the correct value if you don't want to bother with the enumeration.

__android_log_write(6, "MYSTUFF", "Oh no, an error!");

Ninja edit from 2024: to use ATrace, here are the signatures. Make sure you link with libandroid and you use version 23 or higher, eg with $ANDROID_NDK/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang -target aarch64-linux-android23 atracefun.cpp -o atracefun -g -O1

extern "C" void ATrace_beginSection(const char *sectionName);
extern "C" void ATrace_endSection();
extern "C" bool ATrace_isEnabled();
extern "C" void ATrace_beginAsyncSection(const char *sectionName,int32_t cookie);
extern "C" void ATrace_endAsyncSection(const char *sectionName, int32_t cookie);

You can use atrace as a data source for Perfetto and enable this for systrace as well.

iOS / MacOS

On iOS, you'll probably want to use the venerable NSLog.

This uses NSString and a quick wrapper from non-Objective-C code isn't as simple, so I've often resorted to either wrapping this with a new .m file that exports a C API, or simply writing out to the standard streams.

Linux

On Linux, standard error is a classic, although sometimes putting things in the standard output is convenient.

If you can #include <stdio.h>, you don't need any redeclarations. It's unlikely that getting this header is difficult.

#include <stdio.h>

// ...

fprintf(stderr, "oh no!");
fprintf(stdout, "oh no!");

If you really don't want to include anything extra, you can include you declaration for write and use that with a standard file descriptor.

extern "C" ssize_t write(int fildes, const void *buf, size_t nbytes);
// 0 is input, 1 is output, 2 is error
write(2, "oh no\n", strlen("oh no\n"));

Windows

If you're looking at this with a debugger attached, OutputDebugStringW or OutputDebugStringA are your friends.

You can declare them without dependencies like this:

extern "C" void OutputDebugStringA(const char*);
extern "C" void OutputDebugStringW(const wchar_t*);

// ...
OutputDebugStringA("hit this line of code!\n");

You'll need to link Kernel32.lib, which you almost certainly already do.

If you absolutely need to, you can use a comment pragma to leave a library-search record in the object file and ask the linker to pick that up.

#pragma comment(lib, "kernel32")

Happy logging!

Tags:  androidcodingcpp

Home