In Part 1 we learned how printk() uses log level prefixes. In practice, modern Linux kernel code almost never calls raw printk() directly. Instead it uses a family of cleaner, shorter wrapper macros called pr_info(), pr_err(), pr_debug() and friends. This part explains what these macros do, how pr_fmt() gives you a consistent message prefix, and how the console log level decides what you see on your terminal.
Why Not Just Use printk() Directly?
There is nothing technically wrong with raw printk(KERN_INFO "..."). But there are two practical problems with it in real driver and module code:
1. It is verbose. You have to remember to type KERN_INFO and keep the right level string correct at every call site. In a file with thirty log messages that is tedious and error-prone.
2. You have no automatic prefix. Every message just says “some text” — you cannot tell from the log which module or function it came from unless you manually add that to every string.
The pr_* macros solve both problems. They are defined in include/linux/printk.h and are the standard way to log messages in modern kernel and driver code.
The pr_* Macro Family
Each macro maps directly to a specific log level. They take a format string and variadic arguments, exactly like printf():
/* Defined in include/linux/printk.h */
pr_emerg(fmt, ...) /* level 0 — system unusable */
pr_alert(fmt, ...) /* level 1 — act immediately */
pr_crit(fmt, ...) /* level 2 — critical condition */
pr_err(fmt, ...) /* level 3 — error */
pr_warn(fmt, ...) /* level 4 — warning (also pr_warning) */
pr_notice(fmt, ...) /* level 5 — significant normal event */
pr_info(fmt, ...) /* level 6 — informational */
pr_cont(fmt, ...) /* continue a line with no newline */
Old style — raw printk
printk(KERN_INFO
"ep_demo: loaded\n");
printk(KERN_ERR
"ep_demo: alloc failed\n");
printk(KERN_WARNING
"ep_demo: buffer small\n");
Modern style — pr_ macros
pr_info("ep_demo: loaded\n");
pr_err("ep_demo: alloc failed\n");
pr_warn("ep_demo: buffer small\n");
The output in the kernel log is identical. The only difference is cleaner source code. Under the hood, pr_info(fmt, ...) expands to exactly printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__).
What Is pr_fmt() and Why Does It Matter?
pr_fmt() is a macro hook that every pr_* call passes its format string through. The default definition (when you include printk.h without overriding it) is a no-op:
/* default — does nothing */
#define pr_fmt(fmt) fmt
But you can override it at the top of your source file to add a consistent prefix to every single pr_* message in that file — automatically, without touching each call site:
/* Put this BEFORE any #include at the top of your .c file */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
Now every pr_info(), pr_err(), etc. in that file will automatically start with your module name. For example, if your module is built as ep_uart:
pr_info("initializing port 0\n");
/* Output in dmesg: ep_uart: initializing port 0 */
pr_err("TX timeout on port 0\n");
/* Output in dmesg: ep_uart: TX timeout on port 0 */
pr_fmt at the top of each kernel source file before any includes. The most common patterns are KBUILD_MODNAME ": " fmt for a simple module-name prefix, or KBUILD_MODNAME ":%s: " fmt, __func__ to also include the function name. This makes grepping the kernel log for your module’s messages trivial.
pr_debug() — The Special Case
pr_debug() is fundamentally different from the other pr_* macros. Its behavior at runtime depends entirely on how the kernel was built. There are three possible behaviors:
| Build Configuration | What pr_debug() Does | Runtime Cost |
|---|---|---|
| Neither DEBUG nor CONFIG_DYNAMIC_DEBUG | Expands to no_printk() — zero output, but compiler still checks format args |
Zero — no code emitted |
| #define DEBUG (in source file) | Becomes a regular printk(KERN_DEBUG ...) |
Same as any printk |
| CONFIG_DYNAMIC_DEBUG=y | Compiled in but disabled by default; switched on per call site at runtime via debugfs | One NOP when off; full printk when on |
What is no_printk()? It is defined as if (0) printk(...). The if (0) means the compiler never emits machine code for it. However, the compiler still type-checks your format string and arguments. This is important — you catch format-string bugs in your debug code even when it is compiled out.
/* Case 1: no debug enabled — compiles to nothing */
pr_debug("entered function, x=%d\n", x); /* zero cost in production */
/* Case 2: add this line at the top of your file to always emit debug */
#define DEBUG /* must be before any #include */
pr_debug("entered function, x=%d\n", x); /* now a real printk */
CONFIG_DYNAMIC_DEBUG=y, every pr_debug() call is compiled into the binary but turned off by default. You switch individual call sites on or off at runtime — without rebooting — through the file /sys/kernel/debug/dynamic_debug/control. This is extremely useful for production systems where you cannot afford always-on debug output but still need the ability to turn it on when investigating an issue.
Dynamic Debug — Turning pr_debug() On at Runtime
With CONFIG_DYNAMIC_DEBUG=y in your kernel build, you control debug output from the command line without recompiling. Here are the most common commands:
# Enable all pr_debug() messages from one module
echo 'module ep_uart +p' > /sys/kernel/debug/dynamic_debug/control
# Enable all pr_debug() in one source file
echo 'file ep_uart_core.c +p' > /sys/kernel/debug/dynamic_debug/control
# Enable only one specific line in a file
echo 'file ep_uart_core.c line 87 +p' > /sys/kernel/debug/dynamic_debug/control
# Also print the function name alongside each message
echo 'module ep_uart +pf' > /sys/kernel/debug/dynamic_debug/control
# Disable again
echo 'module ep_uart -p' > /sys/kernel/debug/dynamic_debug/control
The +p flag enables printing. The +f flag adds the function name to each message. The +l flag adds the line number. You can combine them: +pfl turns on printing with function name and line number.
pr_debug() output only reaches the console if console_loglevel is set to 8 or higher. The message is always captured in the ring buffer (visible in dmesg). Set echo 8 > /proc/sys/kernel/printk if you need debug messages on the console.
dev_*() — The Right Choice for Device Drivers
When your code has access to a struct device * pointer — which is almost always true in driver code — you should prefer the dev_*() family over pr_*(). These are defined in include/linux/dev_printk.h.
dev_emerg(dev, fmt, ...)
dev_alert(dev, fmt, ...)
dev_crit(dev, fmt, ...)
dev_err(dev, fmt, ...)
dev_warn(dev, fmt, ...)
dev_notice(dev, fmt, ...)
dev_info(dev, fmt, ...)
dev_dbg(dev, fmt, ...)
The advantage is that the device name is automatically prepended to every message in a standard format, without any pr_fmt override needed:
dev_err(&pdev->dev, "DMA transfer failed, status = %d\n", status);
/* Output: ep_uart 0000:02:00.0: DMA transfer failed, status = -5 */
✔ Subsystem code with no device
✔ Early init / boot code
✔ When no struct device* is available
✔ Any code with struct device *
✔ When you want automatic device name
✔ Preferred for all hardware drivers
Writing to the Console Device
The kernel console is separate from a normal terminal. It is the raw output path for kernel messages — before any desktop or display manager starts. On a typical Linux server or embedded system it might be a serial port, a VGA text framebuffer, or /dev/console. The kernel can have multiple console devices registered simultaneously.
A printk() message reaches the console only if the message level is numerically less than console_loglevel. The current value is stored in /proc/sys/kernel/printk. You can raise it to see more messages or lower it to quiet the console:
# Show all messages including debug (level 8 means 0..7 all pass)
echo 8 > /proc/sys/kernel/printk
# Only show emergencies and alerts on console
echo 2 > /proc/sys/kernel/printk
# Restore default (7)
echo 7 > /proc/sys/kernel/printk
# Or set all four values at once
sysctl -w kernel.printk="7 4 1 7"
(always written, every level, lockless in 6.x)
/dev/console, serial, VGA
but still in ring buffer
(always available regardless of console_loglevel)
A Complete Module Using pr_ Macros Correctly
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt /* MUST be before any #include */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
static int __init ep_uart_init(void)
{
pr_info("UART driver loaded, version 2.1\n");
/* Output: ep_uart: UART driver loaded, version 2.1 */
if (some_hardware_missing()) {
pr_err("hardware not found, aborting probe\n");
/* Output: ep_uart: hardware not found, aborting probe */
return -ENODEV;
}
pr_warn("operating in compatibility mode\n");
/* Output: ep_uart: operating in compatibility mode */
pr_debug("probe complete, IRQ=%d base=0x%x\n", irq, base);
/* Visible only when debug is enabled — zero cost otherwise */
return 0;
}
static void __exit ep_uart_exit(void)
{
pr_info("UART driver unloaded\n");
/* Output: ep_uart: UART driver unloaded */
}
module_init(ep_uart_init);
module_exit(ep_uart_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala UART driver demo");
#define pr_fmt(...) after an #include <linux/printk.h>. This does not work because pr_fmt is used inside macros that are already expanded by the time the preprocessor reaches your later define. Always put #define pr_fmt as the very first line, before all includes.
Interview Questions — pr_ Macros and Console
pr_info("msg") expands to printk(KERN_INFO pr_fmt("msg")). The macro form is shorter, harder to get wrong, and automatically applies any pr_fmt() override defined at the top of the file.pr_fmt(fmt) is a macro that every pr_*() call passes its format string through. You override it at the top of your source file (before all includes) to add a uniform prefix — typically the module name — to every pr_* message in that file. The default is a no-op that returns the format string unchanged.no_printk(), which is an if (0) printk(...). The compiler emits no machine code, so there is zero runtime cost. However the compiler still type-checks the format string and arguments, catching bugs even in disabled debug code.CONFIG_DYNAMIC_DEBUG=y. Then write a query to /sys/kernel/debug/dynamic_debug/control, for example: echo 'module mymodule +p' > /sys/kernel/debug/dynamic_debug/control. This enables all pr_debug() call sites in that module at runtime without any reboot or recompile.struct device * pointer. dev_err(dev, "msg") automatically prepends the device name in standard kernel format, making it immediately clear in the log which hardware instance produced the error. This is the preferred logging API for all driver code that interacts with specific hardware devices.console_loglevel might be set below 7, meaning KERN_INFO (level 6) is filtered out. Check with cat /proc/sys/kernel/printk and raise the first value to 7 or higher. Second, the system might be booted with the quiet kernel parameter which sets console_loglevel to 4 (KERN_WARNING). The messages are always in the ring buffer and visible via dmesg regardless.+p enables printing the message, +f adds the function name to each output line, and +l adds the source line number. Combining them as +pfl enables printing with both function name and line number, which is very useful for tracing exactly where in the code a debug message originates.Master the tools that read the kernel ring buffer. Learn dmesg filters, journalctl for kernel messages, the four values in /proc/sys/kernel/printk, and how to prevent log floods with rate-limited printk.
Read Part 3 →