Logging & Logcat

Before reaching for a debugger, most Android developers reach for a log statement first. The Log class writes timestamped messages to Logcat, Android Studio's live log viewer — the single most-used tool for understanding what a running app is actually doing, in RecipeBox or any other project.

The Log Methods

Log exposes one method per severity level. Every call takes a tag (usually the class name, used for filtering) and a message.

MethodLevelWhen to use it
Log.v()VerboseExtremely detailed, noisy tracing — rarely left in code
Log.d()DebugGeneral development-time tracing of app flow
Log.i()InfoNotable, expected events (e.g., "user signed in")
Log.w()WarningSomething recoverable but worth noticing (e.g., a retry)
Log.e()ErrorSomething failed — typically paired with an exception

Filtering Logcat in Android Studio

A running device or emulator can easily produce thousands of log lines a minute from the system and every installed app — filtering is what makes Logcat usable day to day.

By tag: type a tag (e.g., RecipeBox) into the Logcat search box to see only messages logged with that exact tag.

By log level: the level dropdown (Verbose/Debug/Info/Warn/Error) shows that level and everything more severe — selecting "Warn" hides Verbose/Debug/Info but shows Warn and Error.

By process: the process dropdown restricts output to a single running app, so system noise and other installed apps don't clutter the view.

Combining a tag filter with an Error-level filter is a common habit: it shows only failures from your own app, hiding everything else instantly.

Worked Example: Tracing a Network Fetch

Meaningful log statements at each step of a flow make it possible to see exactly where things go wrong, without attaching a debugger. Here they trace RecipeBox fetching a recipe's details over the network (see HTTP Basics & Threads and Retrofit (Java) + Gson for the networking layer itself).

private static final String TAG = "RecipeBox";

public void fetchRecipe(String recipeId) {
    Log.d(TAG, "fetchRecipe: starting for id=" + recipeId);

    api.getRecipe(recipeId).enqueue(new Callback<Recipe>() {
        @Override
        public void onResponse(Call<Recipe> call, Response<Recipe> response) {
            if (!response.isSuccessful()) {
                Log.w(TAG, "fetchRecipe: server returned code " + response.code());
                return;
            }
            Log.d(TAG, "fetchRecipe: success, title=" + response.body().title);
        }

        @Override
        public void onFailure(Call<Recipe> call, Throwable t) {
            Log.e(TAG, "fetchRecipe: network call failed", t);
        }
    });
}
Explanation:
  • The first Log.d() confirms the method was actually called with the expected ID — useful when a UI action seems to do nothing.
  • Log.w() for a non-2xx response distinguishes "the server rejected this" from a full network failure.
  • Passing t directly to Log.e(TAG, message, t) automatically prints the exception's full stack trace beneath the message.

Log.getStackTraceString()

When an exception needs to be logged somewhere that doesn't accept a Throwable directly (for example, concatenated into a larger message string), Log.getStackTraceString() converts it to text.

try {
    saveRecipeToDisk(recipe);
} catch (IOException e) {
    String details = Log.getStackTraceString(e);
    Log.e(TAG, "saveRecipeToDisk failed:\n" + details);
}
This produces the same readable, multi-line stack trace format as passing the exception directly, just as a plain String you can also embed elsewhere (like a crash-report payload).

Keep Logging Out of Release Builds

Verbose debug logging left running in a released app wastes CPU, can leak information, and clutters any user-facing device logs. The ProGuard / R8 shrinker covered earlier can be configured with rules that strip specific logging calls (commonly Log.d and Log.v) out of release builds entirely, so the calls simply don't exist in the shipped APK.

An Alternative: Timber

Many real-world apps don't call Log directly at all — they use Timber, a small, popular logging wrapper library. Conceptually, Timber auto-generates the tag from the calling class (no more repeating TAG everywhere), and makes it straightforward to plant a "tree" that silently discards debug logs in release builds while still forwarding errors to a crash-reporting service. This lesson only needs the awareness that Timber exists as the common next step once raw Log calls start feeling repetitive.

Logging tells you what already happened. The next lesson, Debugger & Tools, covers pausing a running RecipeBox app mid-execution to inspect exactly what's happening right now.