What are printk Log Levels in Linux Kernel?

Series: PREV_LEC NEXT_LEC
printk Log Levels in Linux Kernel
Free Linux Kernel Programming Course – EmbeddedPathashala
⏱ 25 min read
🎯 Beginner to Intermediate
🐧 Linux Kernel 6.x
🆓 100% Free
Topics Covered:
printk log levels kernel log level Linux KERN_EMERG KERN_DEBUG pr_emerg pr_err pr_info /proc/sys/kernel/printk dmesg kernel messages Linux kernel module debugging free linux kernel programming course free linux device driver course

When your Linux kernel module needs to send a message — whether it is a critical hardware failure or a simple debug trace — it uses printk(). Unlike printf() in userspace programs, printk() works inside the kernel and supports a priority system called log levels.

Understanding log levels is one of the most important skills for any Linux kernel developer or embedded Linux engineer. This tutorial explains every log level, how the kernel decides what to print on the console, and how to control it for your development workflow.

What is printk and Why Does It Have Log Levels?

Inside the Linux kernel, there is no standard library. You cannot call printf(). The kernel has its own logging function: printk(). Every message sent through printk() carries a log level that tells the kernel how urgent or important that message is.

Think of it like this: a fire alarm is more urgent than a daily status report. The kernel needs to know which messages should immediately appear on the system console (like a fire alarm) and which can just be quietly stored in the log buffer (like the status report).

📌 Key Concept: Log level numbers run from 0 (most urgent) to 7 (least urgent). A lower number means a higher-priority message.
printk Log Level Urgency Scale (Linux Kernel 6.x)
0
KERN_EMERG — System is completely unusable. Immediate attention required. HIGHEST PRIORITY
1
KERN_ALERT — An action must be taken immediately. For example, data corruption detected.
2
KERN_CRIT — A critical condition in hardware or software. The system may not recover.
3
KERN_ERR — A non-fatal error. A hardware error was handled but something did not work correctly.
4
KERN_WARNING — A warning about something unexpected that the system handled. Worth investigating.
5
KERN_NOTICE — Normal but noteworthy event. System is working fine but something worth noting happened.
6
KERN_INFO — General informational messages. Used to report successful operations. Very commonly used.
7
KERN_DEBUG — Detailed debugging messages. Only useful during active development. LOWEST PRIORITY

The pr_* Helper Macros — What You Should Actually Use

Calling printk() directly with a log level string like KERN_ERR works, but it is verbose. The Linux kernel provides a cleaner set of helper macros — one for each log level — that are preferred in modern kernel code.

Macro Log Level Numeric Value When to Use
pr_emerg()KERN_EMERG0System is unusable — kernel panic territory
pr_alert()KERN_ALERT1Immediate action needed by an operator
pr_crit()KERN_CRIT2Critical hardware or software failure
pr_err()KERN_ERR3Driver or subsystem error — recoverable
pr_warn()KERN_WARNING4Something suspicious happened but system is OK
pr_notice()KERN_NOTICE5Normal events worth logging (e.g., device registered)
pr_info()KERN_INFO6General operational messages — most commonly used
pr_debug()KERN_DEBUG7Detailed debug info — only active with DEBUG defined
pr_devel()KERN_DEBUG7Same as pr_debug() but only in development builds
⚠️ Important about pr_debug() and pr_devel():
These two macros are compiled out completely unless DEBUG is defined for your module or CONFIG_DYNAMIC_DEBUG is enabled in your kernel configuration. Even if your console log level is set to 8, you will not see their output unless the kernel was built with debugging enabled for that module. This is a common source of confusion for beginners.

Writing a Kernel Module That Uses All Log Levels

Here is a complete, minimal kernel module that demonstrates all the pr_* macros. Each pr_* call goes to the kernel log buffer and potentially to the console, depending on the current log level setting.

// File: loglevel_demo.c
// Purpose: Demonstrate all printk log levels in Linux kernel 6.x
// EmbeddedPathashala - Free Linux Kernel Programming Course

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeddedPathashala");
MODULE_DESCRIPTION("printk log level demonstration module");
MODULE_VERSION("1.0");

static int __init loglevel_demo_init(void)
{
    pr_emerg ("EP Demo: KERN_EMERG  [0] - System unusable\n");
    pr_alert ("EP Demo: KERN_ALERT  [1] - Act immediately\n");
    pr_crit  ("EP Demo: KERN_CRIT   [2] - Critical condition\n");
    pr_err   ("EP Demo: KERN_ERR    [3] - Error occurred\n");
    pr_warn  ("EP Demo: KERN_WARNING[4] - Warning\n");
    pr_notice("EP Demo: KERN_NOTICE [5] - Normal but notable\n");
    pr_info  ("EP Demo: KERN_INFO   [6] - Informational\n");
    pr_debug ("EP Demo: KERN_DEBUG  [7] - Debug (needs DEBUG)\n");
    pr_devel ("EP Demo: pr_devel()  [7] - Dev build only\n");
    return 0;
}

static void __exit loglevel_demo_exit(void)
{
    pr_info("EP Demo: Module unloaded successfully\n");
}

module_init(loglevel_demo_init);
module_exit(loglevel_demo_exit);
✅ Build and test it:
Save the file, create a Makefile, then run make followed by sudo insmod loglevel_demo.ko. View the output with dmesg | tail -20.

How the Kernel Console Decides Which Messages to Display

Not every printk() message appears on the console (the terminal or serial port you are looking at). The kernel uses a threshold called the current console log level to decide what shows up in real time.

The rule is simple: if a message’s log level number is less than the current console log level, it appears on the console. Messages at or above the threshold go only into the kernel ring buffer (readable via dmesg).

Console Log Level Threshold — Which Messages Appear on Screen
Example: Console Log Level = 4 (KERN_WARNING) — the typical Linux default
0
KERN_EMERG — ✔ Appears on console (0 < 4)
1
KERN_ALERT — ✔ Appears on console (1 < 4)
2
KERN_CRIT — ✔ Appears on console (2 < 4)
3
KERN_ERR — ✔ Appears on console (3 < 4)
← THRESHOLD: Console Log Level = 4 →
4
KERN_WARNING — ✗ NOT on console (4 is NOT less than 4)
5
KERN_NOTICE — ✗ NOT on console (5 is NOT less than 4)
6
KERN_INFO — ✗ NOT on console → Use dmesg to read
7
KERN_DEBUG — ✗ NOT on console → Needs DEBUG build flag too
All messages — both above and below the threshold — are stored in the kernel ring buffer and can be read via dmesg

Reading and Understanding /proc/sys/kernel/printk

The kernel exposes its current log level settings through a proc pseudo-file. You can read it at any time from your terminal:

$ cat /proc/sys/kernel/printk
4    4    1    7

There are exactly four numbers separated by spaces. Each number has a specific meaning:

The Four Numbers in /proc/sys/kernel/printk — Explained
4
Current Console Log Level
Messages with level < 4 appear on console right now. You can change this at runtime.
4
Default Message Level
If a printk() call has no log level specified, this level is assigned to it automatically.
1
Minimum Allowed Level
You cannot set the console log level below this value. Guards against silencing critical messages.
7
Boot-time Default Level
The log level used by the kernel during early boot before the proc filesystem is available.
📌 Embedded Linux Note: On some embedded boards (for example, ARM-based SBCs running minimal Linux distributions), the default current console log level may be 3 instead of 4. This means only KERN_EMERG, KERN_ALERT, and KERN_CRIT messages appear on the serial console by default. If you are loading a module and see no output, check this value first.

The Kernel Ring Buffer and dmesg — Your Best Friend for Debugging

Every printk() message — regardless of log level — is stored in a fixed-size memory region called the kernel ring buffer. When the buffer fills up, older messages are overwritten. The dmesg command lets you read this buffer from userspace.

# View all kernel messages
dmesg

# View only the last 20 lines
dmesg | tail -20

# Watch kernel messages live as they come in (Linux 3.5+ / util-linux 2.21+)
dmesg -w

# Show messages with human-readable timestamps
dmesg -T

# Filter by log level — show only errors and above
dmesg --level=err,crit,alert,emerg

# Clear the ring buffer (requires root)
sudo dmesg -C
✅ Pro Tip for Module Development: Always run dmesg -w in a separate terminal window before loading your module. This way, you see all kernel messages in real time as insmod runs your init function.

The dev_* Family — Log Levels for Device Drivers

When you write an actual device driver, you typically have access to a struct device *dev pointer. In that case, prefer the dev_* macros over the plain pr_* macros. These automatically include the device name in the log message, making it much easier to identify which device generated the log output in a system with multiple devices.

// dev_* macros automatically prepend the device name to the message
// Example: "my_driver my_device: Probe successful"

dev_emerg(dev, "Critical failure in device %s\n", dev_name(dev));
dev_err  (dev, "Register read failed: error %d\n", ret);
dev_warn (dev, "Unexpected state, resetting...\n");
dev_info (dev, "Probe successful, IRQ=%d\n", irq);
dev_dbg  (dev, "Register dump: 0x%08x\n", reg_val);

The dev_dbg() macro, like pr_debug(), is compiled out unless DEBUG is defined. In production drivers, use dev_info() and dev_err() for the most important messages and keep verbose logging under dev_dbg().

The Special Rule: KERN_EMERG Always Gets Through

There is one unconditional rule in the kernel’s logging system: a message at level KERN_EMERG (0) always appears on the console and on every open terminal window, no matter what the current console log level is set to. This cannot be overridden.

This makes sense: if the kernel is about to crash or has detected a catastrophic hardware failure, you need to know about it immediately regardless of how the system is configured. This is also why kernel panics always produce visible output on the console.

⚠️ Never abuse KERN_EMERG: Do not use pr_emerg() for routine messages in your drivers just to make them appear on the console. Reserve it for genuine system-level catastrophes. Misusing high-priority log levels makes real emergencies harder to spot.

Interview Questions — printk Log Levels

Q1: What is the range of printk log levels in Linux and which is the most urgent?
Log levels go from 0 to 7. Level 0 (KERN_EMERG) is the most urgent, and level 7 (KERN_DEBUG) is the least urgent. A smaller number always means higher priority.
Q2: What happens when a printk() call does not specify a log level?
The kernel automatically assigns it the default message log level, which is the second value in /proc/sys/kernel/printk. On most systems this defaults to 4 (KERN_WARNING).
Q3: Why do pr_debug() messages not appear even when the console log level is set to 8?
Because pr_debug() is conditionally compiled. Unless the macro DEBUG is defined for that translation unit, or CONFIG_DYNAMIC_DEBUG is enabled in the kernel build, the macro expands to an empty no-op and never generates any code at all.
Q4: What is the difference between pr_err() and dev_err()?
Both log at KERN_ERR level. The difference is that dev_err() takes a struct device * pointer and automatically prepends the device name to the log message. This is very helpful in drivers that manage multiple device instances because you can immediately identify which device produced the error.
Q5: Can printk be used from interrupt context?
Yes. Unlike userspace I/O functions, printk() is safe to call from interrupt context, softirqs, and tasklets. It uses a lock-free ring buffer internally to handle this. However, frequently calling printk from interrupt context can impact interrupt latency, so use it only for debugging in production drivers.
Q6: How is the kernel ring buffer size determined and what happens when it fills up?
The ring buffer size is set at kernel build time via CONFIG_LOG_BUF_SHIFT. The actual size is 2 ^ CONFIG_LOG_BUF_SHIFT bytes. When the buffer is full, new messages overwrite the oldest ones. You can see the current buffer size with dmesg --buffer-size.

Continue Your Free Linux Kernel Programming Journey

EmbeddedPathashala offers 100% free courses on Linux Kernel Programming, Linux Device Drivers, Bluetooth/BLE, and Embedded Systems.

Visit EmbeddedPathashala

Leave a Reply

Your email address will not be published. Required fields are marked *