Bitmasks in Go
Why log.LstdFlags constant is an integer
If you’ve ever configured Go’s logging package, you’ve probably written something like:
log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)And maybe you wondered: “Why is log.LstdFlags an integer? Why are we using the | operator on what looks like constants?”
The answer: Go is using bitmasks, a compact and efficient pattern for representing multiple boolean options as a single integer. Once you understand this pattern, you’ll spot it everywhere—from file permissions to network socket options to feature flags.
The Bitmask Pattern
A bitmask (sometimes called bit flags) uses individual bits in an integer to represent independent boolean flags. Instead of having separate variables for each option, we pack them into a single number.
Here’s how the log package defines its flags:
const (
Ldate = 1 << iota // 0001 (1)
Ltime // 0010 (2)
Lmicroseconds // 0100 (4)
Llongfile // 1000 (8)
Lshortfile // 0001 0000 (16)
LUTC // 0010 0000 (32)
Lmsgprefix // 0100 0000 (64)
LstdFlags = Ldate | Ltime // 0011 (3)
)
Each constant occupies exactly one bit position. Let’s break down what’s happening.
The Magic of iota
Before we dive into bit shifting, let’s understand iota—Go’s built-in constant generator that makes the bitmask pattern elegant.
iota is a counter that starts at 0 and increments by 1 for each line in a const block.
Here’s the basic behavior:
const (
First = iota // 0
Second // 1
Third // 2
Fourth // 3
)
Each constant automatically gets the next value. You only need to specify iota once; subsequent lines inherit the pattern.
Key behaviors:
Starts at 0: The first constant in a block gets
iota = 0Auto-increments: Each new line increments
iotaby 1Resets per block: Each new
constblock startsiotaback at 0
const (
A = iota // 0
B // 1
C // 2
)
const (
X = iota // 0 (reset!)
Y // 1
Z // 2
)
Why this matters for bitmasks: When you combine iota with bit shifting, you automatically generate powers of 2:
const (
Flag1 = 1 << iota // 1 << 0 = 1
Flag2 // 1 << 1 = 2
Flag3 // 1 << 2 = 4
Flag4 // 1 << 3 = 8
)
No manual counting. No typos. No gaps. Just clean, sequential bit positions.
You can also skip values or group related constants:
const (
_ = iota // skip with blank identifier
KB = 1 << (10 * iota) // 1 << 10 = 1024
MB // 1 << 20 = 1048576
GB // 1 << 30 = 1073741824
)
The Left Shift Operator: <<
The << operator shifts bits to the left, effectively multiplying by powers of 2:
1 << 0 // 0001 -> 0001 = 1
1 << 1 // 0001 -> 0010 = 2
1 << 2 // 0001 -> 0100 = 4
1 << 3 // 0001 -> 1000 = 8
Combined with iota (which auto-increments for each constant), we get perfectly spaced bit positions:
Ldate= 1 (bit 0 set)Ltime= 2 (bit 1 set)Lmicroseconds= 4 (bit 2 set)Llongfile= 8 (bit 3 set)
Why this matters: Each power of 2 has exactly one bit set, so they never overlap. This lets us combine them without losing information.
The Bitwise OR Operator: |
To combine multiple flags, we use the bitwise OR operator. It sets a result bit to 1 if either input bit is 1:
0001 (Ldate = 1)
| 0010 (Ltime = 2)
------
0011 (Result = 3)
That’s why LstdFlags = Ldate | Ltime equals 3. The binary representation 0011 means “both date and time flags are enabled.”
When you write:
log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)You’re building the integer 0111 (7), which encodes all three flags in a single value.
The Bitwise AND operator: &
To test if a specific flag is enabled, use the bitwise AND operator &:
flags := log.Ldate | log.Ltime // 0011 (3)
// Check if Ldate is set.
if flags & log.Ldate != 0 {
// Ldate is set.
}
// Check if Lmicroseconds is set.
if flags & log.Lmicroseconds != 0 {
// Lmicroseconds is NOT set (this block won't run).
}
The & operator returns 1 only when both bits are 1:
0011 (flags = Ldate | Ltime)
& 0001 (Ldate)
------
0001 (non-zero, so Ldate is set)
0011 (flags)
& 0100 (Lmicroseconds)
------
0000 (zero, so Lmicroseconds is NOT set)
Why Bitmasks? Real-World Benefits
1. Compactness Instead of passing multiple boolean parameters or a struct with many fields, you pass a single integer.
2. Performance Bitwise operations are among the fastest CPU instructions. Checking flags is a single AND operation.
3. Backward Compatibility Adding new flags doesn’t break existing code—old code just ignores bits it doesn’t understand.
4. Ubiquity This pattern appears throughout systems programming:
File permissions:
os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644)Network sockets:
syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM|syscall.SOCK_NONBLOCK, 0)Feature flags: Enable/disable multiple features with one configuration value
Practical Example: Custom Logger Flags
Let’s say you’re building a logging library for Kubernetes operators. You want flags for:
Include namespace
Include pod name
Include container ID
Include timestamp
Here’s how you’d implement it:
package oplog
type Flag int
const (
FlagNamespace Flag = 1 << iota // 0001 (1)
FlagPodName // 0010 (2)
FlagContainerID // 0100 (4)
FlagTimestamp // 1000 (8)
FlagAll = FlagNamespace | FlagPodName | FlagContainerID | FlagTimestamp // 1111 (15)
)
type Logger struct {
flags Flag
}
func New(flags Flag) *Logger {
return &Logger{flags: flags}
}
func (l *Logger) Log(msg string) {
var prefix string
if l.flags&FlagTimestamp != 0 {
prefix += time.Now().Format("15:04:05") + " "
}
if l.flags&FlagNamespace != 0 {
prefix += "[ns:default] "
}
if l.flags&FlagPodName != 0 {
prefix += "[pod:web-7d8f] "
}
if l.flags&FlagContainerID != 0 {
prefix += "[ctr:a3f9] "
}
fmt.Println(prefix + msg)
}
Usage:
// Just namespace and pod name
logger := oplog.New(oplog.FlagNamespace | oplog.FlagPodName)
logger.Log("Processing request")
// Output: [ns:default] [pod:web-7d8f] Processing request
// Everything
logger = oplog.New(oplog.FlagAll)
logger.Log("Error occurred")
// Output: 15:04:05 [ns:default] [pod:web-7d8f] [ctr:a3f9] Error occurred
The Takeaway
Next time you see integer constants combined with | in Go code, you’ll recognize the bitmask pattern:
Define flags using
1 << iotato get powers of 2Combine flags with bitwise OR (
|)Check flags with bitwise AND (
&)
This pattern bridges high-level Go code and low-level systems programming, making it essential knowledge for anyone working with infrastructure, networking, or systems tools.
References:
Go
logpackage sourceUnix file permissions (another classic bitmask use case)

