Controlling Linux Kernel Console Logging: console_loglevel & kernel.printk

Controlling the Kernel Console Log Level
Free Linux Kernel Programming Course – EmbeddedPathashala
⏱ 20 min read
🎯 Beginner to Intermediate
🐧 Linux Kernel 6.x
🆓 100% Free
Series: PREV_LEC NEXT_LEC
Topics Covered:
console log level Linux /proc/sys/kernel/printk dmesg log level filter kernel debug console output sysctl kernel.printk embedded Linux kernel debugging free linux device driver course free linux kernel programming course dynamic debug kernel

In the previous tutorial, you learned that every printk() message has a log level from 0 to 7. But knowing the log levels is only half the picture. The other half is understanding how to control which messages appear on your console — especially when you are actively developing and debugging a kernel module.

This tutorial covers everything you need to know about the console log level: how to read it, how to change it temporarily during development, how to make it persistent, and how to use dmesg effectively to filter kernel messages. It also covers the special pr_debug() behavior and how to unlock debug messages using dynamic debug.

The Three Places Where printk Messages Can Go

Before diving into how to control log levels, it helps to understand where kernel messages actually land. There are three destinations:

printk Output Destinations — All Three Paths
pr_info(“Module loaded successfully\n”);
Kernel Logging Core
① Kernel Ring Buffer
All messages always land here regardless of log level. Read via dmesg. Fixed size — old messages get overwritten when full.
↓ (if level < console threshold)
② Console Device
Only messages with level < current console log level appear here in real time. Could be a serial port, VGA, SSH terminal, or framebuffer.
↓ (if syslog daemon active)
③ Syslog / Log File
The rsyslog or syslog-ng daemon reads from the kernel and writes to /var/log/kern.log or /var/log/syslog.

Understanding this architecture is important: even if a message does not appear on your console, it is almost certainly in the ring buffer. If you cannot see a message on screen, your first action should always be to check dmesg.

How to Change the Console Log Level at Runtime

The most common need during kernel module development is to raise the console log level so that all pr_info() and pr_warn() messages appear directly on the terminal without needing dmesg. Here are the methods, from most convenient to most permanent:

Method 1: Write directly to /proc/sys/kernel/printk

This is the most direct method. You write four space-separated values representing the four log level parameters. The change takes effect immediately but is lost after a reboot.

# Show current values
cat /proc/sys/kernel/printk

# Set console log level to 8 — all printk messages appear on console
# Format: current_level  default_level  min_level  boot_default_level
echo "8 4 1 7" | sudo tee /proc/sys/kernel/printk

# Verify the change
cat /proc/sys/kernel/printk
# Output: 8    4    1    7

# Restore to the typical production default
echo "4 4 1 7" | sudo tee /proc/sys/kernel/printk
📌 Why value 8? Since log levels only go from 0 to 7, setting the console log level to 8 means every message (even level 7 KERN_DEBUG) has a level less than 8, so everything appears on the console. Setting it to 7 would still hide KERN_DEBUG messages.

Method 2: Use sysctl

The sysctl command provides a cleaner interface to the same kernel parameter. It reads from and writes to /proc/sys/ under the hood.

# Read the current value
sysctl kernel.printk

# Set all four values: console  default  min  boot_default
sudo sysctl -w kernel.printk="8 4 1 7"

# Read back to verify
sysctl kernel.printk
# Output: kernel.printk = 8    4    1    7

Method 3: Make it persistent across reboots via /etc/sysctl.conf

If you want a custom log level to survive reboots — common on a dedicated development board — add it to the sysctl configuration file.

# Open /etc/sysctl.conf or create a drop-in file
sudo nano /etc/sysctl.d/99-kernel-debug.conf

# Add this line:
kernel.printk = 8 4 1 7

# Apply immediately without rebooting:
sudo sysctl -p /etc/sysctl.d/99-kernel-debug.conf
⚠️ Do not leave console log level at 8 in production: Having all kernel messages flood the console can mask real problems, slow down the system during heavy logging, and expose debug information to unprivileged console users. Use level 8 only in your development environment.

Method 4: Use the kernel boot parameter

For embedded Linux systems or systems where you want to set the log level very early in the boot process (before the filesystem mounts), you can set it as a kernel command-line parameter.

# Add to the kernel command line in your bootloader (GRUB, U-Boot, etc.)
# This sets console log level to 8 from the very beginning of boot

loglevel=8

# Example GRUB entry in /etc/default/grub:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash loglevel=8"

# After editing, regenerate GRUB config:
sudo update-grub

Comparison: Which Method Should You Use?

Method Survives Reboot? Takes Effect Best For
echo to /proc/sys/kernel/printk No Instantly Quick one-time change during a debug session
sysctl -w kernel.printk No Instantly Same as above, cleaner syntax
/etc/sysctl.d/ config file Yes On next boot or after sysctl -p Development board or VM that you always use for kernel work
Kernel boot parameter loglevel= Yes (in bootloader) From earliest boot Embedded targets, capturing early boot messages

A Practical Pattern: Startup Script for Development Boards

When you regularly develop kernel modules on a dedicated board (a Raspberry Pi, BeagleBone, or any ARM SBC), it is convenient to set the console log level automatically when you log in as root. A lightweight way to do this is via a startup script or your shell’s profile.

#!/bin/sh
# /etc/profile.d/kernel-dev-setup.sh
# Automatically configure kernel log level for development
# Only applies when running as root

if [ "$(id -u)" -eq 0 ]; then
    # Set console log level to 8: all printk messages go to console
    echo "8 4 1 7" > /proc/sys/kernel/printk
    echo "[kernel-dev] Console log level set to 8. All printk visible."
fi
✅ Why check id -u? The check $(id -u) -eq 0 makes sure this only runs when you are root. Writing to /proc/sys/kernel/printk requires root privileges. If a non-root user sources the script, the echo command would fail silently without this guard.

Filtering dmesg Output by Log Level

Even when messages are not appearing on your console, dmesg shows everything in the ring buffer. Modern versions of dmesg (from util-linux 2.21 onwards, which covers most Linux distributions using kernel 4.x and above) support powerful filtering options.

# Show messages from the current boot session only
dmesg

# Follow kernel messages in real time (like tail -f for kernel log)
dmesg -w

# Show messages with human-readable timestamps instead of seconds since boot
dmesg -T

# Filter by log level — useful to see only critical problems
dmesg --level=err
dmesg --level=crit,alert,emerg
dmesg --level=warn,err,crit,alert,emerg

# Show the last 30 lines
dmesg | tail -30

# Search for your module's messages by filtering on your module name or prefix
dmesg | grep "EP Demo"

# Clear the ring buffer before loading your module for a clean slate
sudo dmesg -C
sudo insmod my_module.ko
dmesg
✅ Workflow tip: Before loading your module for testing, run sudo dmesg -C to clear the ring buffer. Then load with sudo insmod and immediately run dmesg. You will see only your module’s messages with no noise from previous activity.

Unlocking pr_debug() with Dynamic Debug

As covered in the previous tutorial, pr_debug() is compiled out unless the DEBUG macro is defined. However, the Linux kernel offers a more powerful and flexible system called Dynamic Debug (CONFIG_DYNAMIC_DEBUG), which lets you enable and disable debug messages at runtime without recompiling the kernel or your module.

Step 1: Check if Dynamic Debug is available

# Check if the dynamic debug control file exists
ls /sys/kernel/debug/dynamic_debug/control

# If the path does not exist, mount debugfs first:
sudo mount -t debugfs debugfs /sys/kernel/debug

Step 2: Enable pr_debug() for a specific module

# Enable all debug messages from a specific module
# Replace 'my_module' with your module's name
echo "module my_module +p" | sudo tee /sys/kernel/debug/dynamic_debug/control

# Enable debug messages from a specific source file only
echo "file my_module.c +p" | sudo tee /sys/kernel/debug/dynamic_debug/control

# Enable debug messages from a specific function only
echo "func my_init_function +p" | sudo tee /sys/kernel/debug/dynamic_debug/control

# Disable debug messages again
echo "module my_module -p" | sudo tee /sys/kernel/debug/dynamic_debug/control

Step 3: Alternative — Define DEBUG in your Makefile

If your kernel does not have CONFIG_DYNAMIC_DEBUG enabled, you can define the DEBUG macro specifically for your module in your Makefile. This causes pr_debug() and pr_devel() to be compiled in for that module only, without affecting the rest of the kernel.

# In your module's Makefile, add this line:
CFLAGS_my_module.o := -DDEBUG

# Full Makefile example:
obj-m += my_module.o
CFLAGS_my_module.o := -DDEBUG

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Putting It All Together: The Complete Log Level Decision Flow

How the Kernel Decides Where a printk Message Goes
pr_info(“Device probe complete\n”); → Level = 6
① Always: Written to Kernel Ring Buffer
Message stored in memory. Readable via dmesg at any time.
② Check: Is message level (6) < console log level?
YES
If console level = 7 or 8
→ Message goes to console immediately
NO
If console level = 4 (default)
→ NOT shown on console. Use dmesg.
③ If syslog daemon is running: Written to /var/log/kern.log
rsyslog or syslog-ng picks up the message and writes it to disk for persistent logging.

Common Mistakes When Working With Kernel Log Levels

1
Expecting pr_info() to appear on console without changing the log level
On a default Linux system, the console log level is 4. Messages at level 6 (KERN_INFO) will not appear on the console. They are in the ring buffer — use dmesg to see them, or raise the console level to 7 or 8.
2
Thinking pr_debug() output is suppressed because of the log level
Actually, pr_debug() is a compile-time no-op unless DEBUG is defined. No matter how high you set the console log level, you will not see its output unless you enable CONFIG_DYNAMIC_DEBUG or rebuild with -DDEBUG.
3
Using high-urgency log levels for routine messages
Calling pr_emerg() just to make a message appear on the console is wrong. It will appear on every terminal and in every log, and it trains operators to ignore genuine emergency messages. Use pr_info() and raise the console log level instead.
4
Forgetting that the ring buffer has a fixed size
If your module produces many messages and the system has been running for a long time, early messages may have been overwritten. If you need long-term kernel logging, configure rsyslog to capture and store kernel messages to disk.
5
Not checking the log level on embedded boards
Embedded Linux targets often have a default console log level of 3, not 4. This means even KERN_ERR messages do not appear on the serial console by default. Always read /proc/sys/kernel/printk on a new target before concluding that your module’s messages are missing.

Interview Questions — Console Log Level Control

Q1: How do you make all printk messages appear on the console during kernel module development?
Set the current console log level to 8 by running echo "8 4 1 7" | sudo tee /proc/sys/kernel/printk or sudo sysctl -w kernel.printk="8 4 1 7". Since all log levels are 0 through 7, every message has a level less than 8 and will appear on the console. This change is temporary and resets on reboot.
Q2: What are the four values in /proc/sys/kernel/printk and what does each control?
The four values are: (1) current console log level — messages with level lower than this appear on the console in real time; (2) default message level — assigned to any printk call that does not specify a level; (3) minimum allowed log level — you cannot set the console log level below this; (4) boot-time default — the log level used during early kernel boot before proc is available.
Q3: What is Dynamic Debug and when would you use it?
Dynamic Debug is a kernel feature (CONFIG_DYNAMIC_DEBUG) that maintains a database of all pr_debug() and dev_dbg() call sites in the kernel and all loaded modules. You can enable or disable specific debug messages at runtime via /sys/kernel/debug/dynamic_debug/control — by module, by file, or by function — without recompiling. You use it when you want to trace a specific driver’s behavior in a production kernel without a full rebuild.
Q4: You set the console log level to 8 but still cannot see pr_debug() output. Why?
Because pr_debug() is conditionally compiled. Unless the module was compiled with -DDEBUG in its CFLAGS, or the kernel was built with CONFIG_DYNAMIC_DEBUG enabled and the debug messages are activated at runtime, the pr_debug() calls are replaced with empty code that produces no output. The console log level is irrelevant for messages that were never compiled in.
Q5: How do you make a console log level change persist across reboots?
Add the setting to a sysctl configuration file such as /etc/sysctl.d/99-kernel-debug.conf with the line kernel.printk = 8 4 1 7. Run sudo sysctl -p /etc/sysctl.d/99-kernel-debug.conf to apply it immediately. On the next reboot, the system will read this file and apply the setting automatically. Alternatively, add loglevel=8 to the kernel command line in the bootloader configuration.
Q6: What command lets you watch kernel messages in real time, similar to tail -f?
The command dmesg -w (or dmesg --follow) waits for new kernel messages and prints them as they arrive, much like tail -f for a log file. This is available in util-linux 2.21 and later, which is standard on any modern Linux distribution. It is very useful for monitoring module loading and device events in real time.

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 *