What are printk Log Levels in the Linux Kernel – Free Linux Kernel Development Course

Series: PREV_LEC NEXT_LEC
printk Log Levels in the Linux Kernel
Free Linux Kernel Programming Course — EmbeddedPathashala
⏱ 15 min read
🐧 Linux 6.x
🆓 Free Course
Topics Covered
printk log levels KERN_ERR KERN_INFO KERN_DEBUG KERN_SOH kern_levels.h kernel log filter linux device drivers course

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.

How KERN_SOH Encodes the Log Level Inside the String
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 */
MacroLevelMeaningWhen 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
Remember: Lower number = higher urgency. Level 0 (EMERG) is the most severe. Level 7 (DEBUG) is the least severe. This is the same convention used in BSD syslog and POSIX, so it carries across the whole Linux ecosystem.
Log Level Severity — From Most Critical to Least Critical
0 EMERG
System is completely unusable — imminent crash
1 ALERT
Act immediately, hardware in danger
2 CRIT
Critical fault, subsystem may be broken
3 ERR
Error — driver or subsystem reported a failure
4 WARNING
Warning — unexpected, but system recovers
5 NOTICE
Normal but notable — interface up, device attached
6 INFO
Informational — module loaded, probe done
7 DEBUG
Debug only — verbose tracing for development
▲ Higher severity (lower number)    |    Lower severity (higher number) ▼

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 */
Best practice: Always specify a log level explicitly. Relying on the default makes your code harder to audit and review, and the default may differ across kernel configurations.

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");
Tip: In modern kernel code you would replace the 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.

Message Routing: Ring Buffer vs Console (console_loglevel = 7)
Level Number Goes to Ring Buffer? Goes to Console? (loglevel=7)
KERN_EMERG0✔ Always✔ Yes (0 < 7)
KERN_ALERT1✔ Always✔ Yes (1 < 7)
KERN_CRIT2✔ Always✔ Yes (2 < 7)
KERN_ERR3✔ Always✔ Yes (3 < 7)
KERN_WARNING4✔ Always✔ Yes (4 < 7)
KERN_NOTICE5✔ Always✔ Yes (5 < 7)
KERN_INFO6✔ Always✔ Yes (6 < 7)
KERN_DEBUG7✔ 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

Q1. How many log levels does the Linux kernel printk support, and what are they?
The kernel supports eight levels defined in 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.
Q2. What is KERN_SOH and why does the kernel use it?
KERN_SOH is the ASCII “Start Of Header” character, byte value 0x01. The kernel uses it as an in-band marker at the start of the format string to signal that the next byte is a log level digit. Because 0x01 never appears in normal printable text, the kernel can always reliably detect whether a level prefix is present or absent.
Q3. Is the log level a function argument to printk?
No. It is a string prefix embedded in the format string itself. 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.
Q4. What log level is used if you call printk() without specifying one?
The kernel assigns 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.
Q5. Does a KERN_DEBUG message always appear on the console?
No. It always goes into the kernel ring buffer (accessible via 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.
Q6. What is the difference between KERN_WARNING and KERN_NOTICE?
KERN_WARNING (level 4) indicates something unexpected happened but the system continues — it is a sign that something may be wrong. KERN_NOTICE (level 5) is for normal, expected events that are still significant and worth logging, such as a network interface coming up or a USB device being connected.
Q7. Where in the kernel source are the log levels defined?
They are defined in 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.
Next: Part 2 — pr_ Convenience Macros & Writing to the Console

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.

Read Part 2 →

Leave a Reply

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