When your kernel module calls printk(), the message doesn’t just float into the void. The kernel sorts every message into one of eight severity levels — from a full system emergency down to a routine debug line. Understanding these levels is essential for any Linux kernel developer or device driver writer. This tutorial explains exactly how they work, how they are encoded, and when to use each one.
What Is a printk Log Level?
Every printk() message in the Linux kernel carries a log level. This level tells the kernel — and any userspace tool reading the log — how important the message is. It also controls whether the message shows up immediately on the console or stays silently in the ring buffer.
A common misconception is that the log level is a function argument. It is not. There is no comma between the level and the message string. Look carefully:
printk(KERN_INFO "System initialized successfully\n");
KERN_INFO and the string after it are both string literals. In C, two string literals placed next to each other are joined into one at compile time. So the above becomes a single string that starts with the level prefix, followed by your message. The kernel then reads the first bytes to figure out the level — it is embedded inside the string itself.
| You write | printk(KERN_ERR “disk failed\n”); |
| KERN_ERR expands to | “\001” “3” → two-byte prefix: byte 0x01, then ‘3’ |
| C string concat gives | “\0013disk failed\n” (one string) |
| Kernel reads | byte[0]==0x01 → SOH marker → byte[1]==’3′ → level KERN_ERR |
| Message text starts at | byte[2] → “disk failed\n” |
KERN_SOH — The Hidden Marker
KERN_SOH is the ASCII character Start Of Header, which has the byte value 0x01 (written as \001 in C octal escape). It was chosen as the marker because it never appears in normal human-readable text, so the kernel can reliably detect that a log level prefix is present.
Here is where it is defined in the kernel source (file: include/linux/kern_levels.h):
#define KERN_SOH "\001" /* ASCII Start Of Header */
#define KERN_SOH_ASCII '\001'
Every KERN_* constant is built by combining KERN_SOH with a single digit string. Lower number means higher severity (more urgent). Higher number means lower severity (less urgent).
The Eight Log Levels
The kernel defines exactly eight levels, all in include/linux/kern_levels.h. Here they are with their full definitions and meanings:
#define KERN_EMERG KERN_SOH "0" /* system is unusable */
#define KERN_ALERT KERN_SOH "1" /* action must be taken immediately */
#define KERN_CRIT KERN_SOH "2" /* critical conditions */
#define KERN_ERR KERN_SOH "3" /* error conditions */
#define KERN_WARNING KERN_SOH "4" /* warning conditions */
#define KERN_NOTICE KERN_SOH "5" /* normal but significant condition */
#define KERN_INFO KERN_SOH "6" /* informational */
#define KERN_DEBUG KERN_SOH "7" /* debug-level messages */
| Macro | Level | Meaning | When to Use |
|---|---|---|---|
KERN_EMERG |
0 — EMERG | System is unusable | Kernel panic, machine about to halt |
KERN_ALERT |
1 — ALERT | Immediate action needed | Critical hardware failure, must act right now |
KERN_CRIT |
2 — CRIT | Critical condition | Severe hardware or software fault |
KERN_ERR |
3 — ERR | Error condition | A driver or subsystem hit an error it can report |
KERN_WARNING |
4 — WARNING | Warning condition | Something unexpected, but system continues |
KERN_NOTICE |
5 — NOTICE | Significant but normal | Network interface up/down, device attached |
KERN_INFO |
6 — INFO | Informational | Module loaded, probe succeeded, one-time messages |
KERN_DEBUG |
7 — DEBUG | Debug-level messages | Development tracing, verbose internal state |
What Happens If You Skip the Log Level?
If you call printk() without any KERN_* prefix, the kernel assigns a default level. That default is KERN_WARNING (level 4). This is controlled by the kernel configuration option CONFIG_MESSAGE_LOGLEVEL_DEFAULT, which defaults to 4.
/* These two are equivalent on a standard kernel build */
printk("Something happened\n"); /* gets level 4 = WARNING */
printk(KERN_WARNING "Something happened\n"); /* explicit level 4 */
Two Special Extras: KERN_DEFAULT and KERN_CONT
Beyond the eight levels, the kernel defines two more constants for special situations:
#define KERN_DEFAULT "" /* assign the default loglevel */
#define KERN_CONT KERN_SOH "c" /* continue a previous line */
KERN_DEFAULT is an empty string. Using it is the same as writing no prefix at all — the kernel assigns the default message level. You will rarely use this intentionally; it exists mainly so code can pass a level variable that might be empty.
KERN_CONT is for continuing a log line that had no newline at the end. For example, if a previous printk ended without \n, a follow-up printk(KERN_CONT "more text\n") appends to it. This is mainly used in early boot code. On multi-core systems, it is not safe to rely on KERN_CONT in regular driver code because another CPU might emit messages between your two calls.
A Practical Code Example
Here is a simple kernel module that uses different log levels at appropriate places. Note that every level is chosen deliberately — this is what good kernel code looks like:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
/* Always define pr_fmt before including printk.h to get a consistent prefix */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
static int __init ep_demo_init(void)
{
/* One-time startup message — use INFO */
printk(KERN_INFO "ep_demo: module loaded, version 1.0\n");
/* Warn about a non-fatal issue */
printk(KERN_WARNING "ep_demo: buffer size is below recommended minimum\n");
/* Report a recoverable error */
printk(KERN_ERR "ep_demo: failed to allocate optional cache\n");
/* Debug detail — only visible when debug output is enabled */
printk(KERN_DEBUG "ep_demo: init complete, returning 0\n");
return 0;
}
static void __exit ep_demo_exit(void)
{
printk(KERN_INFO "ep_demo: module removed\n");
}
module_init(ep_demo_init);
module_exit(ep_demo_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala printk log level demo");
printk(KERN_INFO ...) calls with pr_info(...), pr_warn(...), and pr_err(...) — the pr_ convenience macros. We cover those in Part 2 of this series.
How the Log Level Affects Console Output
Every message always goes into the kernel ring buffer — that is guaranteed regardless of level. Whether the message also appears on your console (terminal, serial port, etc.) depends on the console log level.
A message reaches the console only if its level number is strictly less than the current console log level. With the typical default console log level of 7, messages at levels 0 through 6 reach the console, but level 7 (DEBUG) does not.
| Level | Number | Goes to Ring Buffer? | Goes to Console? (loglevel=7) |
|---|---|---|---|
| KERN_EMERG | 0 | ✔ Always | ✔ Yes (0 < 7) |
| KERN_ALERT | 1 | ✔ Always | ✔ Yes (1 < 7) |
| KERN_CRIT | 2 | ✔ Always | ✔ Yes (2 < 7) |
| KERN_ERR | 3 | ✔ Always | ✔ Yes (3 < 7) |
| KERN_WARNING | 4 | ✔ Always | ✔ Yes (4 < 7) |
| KERN_NOTICE | 5 | ✔ Always | ✔ Yes (5 < 7) |
| KERN_INFO | 6 | ✔ Always | ✔ Yes (6 < 7) |
| KERN_DEBUG | 7 | ✔ Always | ✘ No (7 is NOT < 7) |
You can view and change the console log level by reading or writing /proc/sys/kernel/printk. We explain all four values in that file in Part 3 of this series.
Interview Questions — printk Log Levels
include/linux/kern_levels.h: KERN_EMERG (0), KERN_ALERT (1), KERN_CRIT (2), KERN_ERR (3), KERN_WARNING (4), KERN_NOTICE (5), KERN_INFO (6), and KERN_DEBUG (7). Lower number means higher severity.KERN_ERR "message" is two adjacent C string literals that the compiler concatenates into one. There is no comma between them — they are not separate arguments.default_message_loglevel, which is KERN_WARNING (level 4) by default. This is set by the Kconfig option CONFIG_MESSAGE_LOGLEVEL_DEFAULT. Always specify an explicit level in your code.dmesg), but it only appears on the console if the console_loglevel is set to 8 or higher. With the default console log level of 7, debug messages (level 7) are suppressed from the console because the condition requires the level to be strictly less than the console log level.include/linux/kern_levels.h. This header is very short — it contains just the KERN_SOH definition and the eight KERN_* macros plus KERN_DEFAULT and KERN_CONT.Learn how pr_info(), pr_err(), pr_debug() and pr_fmt() make your kernel code cleaner, plus how console_loglevel controls what you see on screen.
