Project 4: Logging Library

Build a logging library with a clean C API, levels, and safe formatting.

Quick Reference

Attribute Value
Difficulty Beginner
Time Estimate 1 week
Language C
Prerequisites Variadic functions, stdio
Key Topics API ergonomics, error handling, safety

1. Learning Objectives

By completing this project, you will:

  1. Design a minimal, stable logging API.
  2. Implement log levels and output targets.
  3. Ensure thread-safe or documented non-thread-safe behavior.
  4. Prevent format string misuse.

2. Theoretical Foundation

2.1 Core Concepts

  • Log levels: INFO, WARN, ERROR, DEBUG for filtering.
  • Variadic APIs: printf-style formatting.
  • Thread safety: Global state requires locking or clear docs.

2.2 Why This Matters

Logging sits at module boundaries. A safe, predictable logging API reduces debugging time and avoids format-string vulnerabilities.

2.3 Historical Context / Background

Many C projects roll custom logging libraries with inconsistent APIs. This project focuses on clarity and safety.

2.4 Common Misconceptions

  • “printf is safe”: Format strings can be exploited.
  • “Global state is fine”: It can break in multi-threaded contexts.

3. Project Specification

3.1 What You Will Build

A logging library with:

  • log_init, log_set_level, log_set_output
  • log_info, log_warn, log_error, log_debug
  • Optional structured fields

3.2 Functional Requirements

  1. Support multiple log levels.
  2. Print timestamp and level.
  3. Allow output to file or stderr.
  4. Provide a clean header API.

3.3 Non-Functional Requirements

  • Safety: Avoid format string vulnerabilities.
  • Usability: Clear, simple API.
  • Reliability: Flush logs on crash.

3.4 Example Usage / Output

log_init(LOG_INFO);
log_info("starting server on %s:%d", host, port);
log_error("failed to bind: %s", strerror(errno));

3.5 Real World Outcome

You have a reusable logging library that makes API boundaries clearer and debugging easier across multiple modules.


4. Solution Architecture

4.1 High-Level Design

log API -> formatter -> output sink (stderr/file)

4.2 Key Components

Component Responsibility Key Decisions
Logger state Level, output Global vs handle
Formatter Build log lines Timestamp + level
Sink Write output stderr or file

4.3 Data Structures

typedef enum { LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR } LogLevel;

typedef struct {
    LogLevel level;
    FILE *out;
} LogConfig;

4.4 Algorithm Overview

Key Algorithm: Log function

  1. Compare level to threshold.
  2. Format timestamp and prefix.
  3. vfprintf message.
  4. Flush output.

Complexity Analysis:

  • Time: O(n) message length
  • Space: O(1)

5. Implementation Guide

5.1 Development Environment Setup

cc -Wall -Wextra -O2 -g -o test_log test_log.c log.c

5.2 Project Structure

loglib/
├── src/
│   ├── log.c
│   └── log.h
├── tests/
│   └── test_log.c
└── README.md

5.3 The Core Question You’re Answering

“How do I expose a logging API that is safe, consistent, and easy to use?”

5.4 Concepts You Must Understand First

Stop and research these before coding:

  1. Variadic functions
    • How does va_list work?
  2. Thread safety
    • What happens if multiple threads log?
  3. Format string risks
    • Why should format strings be static?

5.5 Questions to Guide Your Design

Before implementing, think through these:

  1. Will you use a global logger or per-instance handle?
  2. Should users be able to set a custom output callback?
  3. How will you handle logging from signal handlers?

5.6 Thinking Exercise

Format Strings

What happens if a user passes untrusted data as the format string? How do you prevent that?

5.7 The Interview Questions They’ll Ask

Prepare to answer these:

  1. “How do variadic functions work in C?”
  2. “What is a format string vulnerability?”
  3. “How do you make logging thread-safe?”

5.8 Hints in Layers

Hint 1: Start with stderr logging Skip files at first.

Hint 2: Add log levels Filter by numeric level.

Hint 3: Add output callback Allow user-defined sinks.

5.9 Books That Will Help

Topic Book Chapter
Variadic APIs “The C Programming Language” Ch. 7
Defensive APIs “Code Complete” Ch. 8

5.10 Implementation Phases

Phase 1: Foundation (2-3 days)

Goals:

  • Basic logging to stderr

Tasks:

  1. Implement log_info and log_error.

Checkpoint: Logs print with prefix.

Phase 2: Core Functionality (2-3 days)

Goals:

  • Levels and formatting

Tasks:

  1. Add level filtering.
  2. Add timestamps.

Checkpoint: Only level >= threshold prints.

Phase 3: Polish & Edge Cases (2-3 days)

Goals:

  • Output selection

Tasks:

  1. Add file output.
  2. Add custom sink callback.

Checkpoint: File logging works.

5.11 Key Implementation Decisions

Decision Options Recommendation Rationale
State Global vs handle Global Simpler for beginners
Thread safety None vs mutex Documented non-thread-safe Keep scope clear

6. Testing Strategy

6.1 Test Categories

Category Purpose Examples
Unit Tests Format output Expected prefixes
Integration Tests File output Writes to file
Edge Cases Null output Errors

6.2 Critical Test Cases

  1. Level filtering: Lower levels suppressed.
  2. File output: Logs written to file.
  3. Long messages: Truncation handled.

6.3 Test Data

"test message"

7. Common Pitfalls & Debugging

7.1 Frequent Mistakes

Pitfall Symptom Solution
Untrusted format string Security risk Require literal format strings
Missing flush Logs not visible fflush after write
Global state races Garbled logs Use mutex or document

7.2 Debugging Strategies

  • Compare log output against expected golden file.
  • Add unit tests for formatting.

7.3 Performance Traps

Formatting timestamps on every call can be expensive; cache if needed.


8. Extensions & Challenges

8.1 Beginner Extensions

  • Add colored output.
  • Add log file rotation.

8.2 Intermediate Extensions

  • Add JSON log format.
  • Add thread-safe logging.

8.3 Advanced Extensions

  • Async logging with a queue.
  • Structured fields with key/value pairs.

9. Real-World Connections

9.1 Industry Applications

  • Servers: Structured logs for observability.
  • Embedded systems: Lightweight logging for debug.
  • log.c: Minimal logging library patterns.

9.3 Interview Relevance

API design and variadic functions are common system questions.


10. Resources

10.1 Essential Reading

  • “The C Programming Language” - Ch. 7
  • “Code Complete” - Ch. 8

10.2 Video Resources

  • C variadic function tutorials

10.3 Tools & Documentation

  • man 3 vfprintf: Variadic formatting
  • JSON Parser: Structured output logging.
  • libhttp-lite: Server logging API.

11. Self-Assessment Checklist

11.1 Understanding

  • I can explain variadic functions.
  • I can design log levels.
  • I can avoid format string risks.

11.2 Implementation

  • Logs print correctly.
  • File output works.
  • API is consistent.

11.3 Growth

  • I can add thread safety.
  • I can explain this project in an interview.

12. Submission / Completion Criteria

Minimum Viable Completion:

  • Basic logging with levels.

Full Completion:

  • File output and timestamps.

Excellence (Going Above & Beyond):

  • Async logging and structured output.

This guide was generated from SPRINT_4_BOUNDARIES_INTERFACES_PROJECTS.md. For the complete learning path, see the parent directory.