Skip to content

📝 Logging Standards

🎯 Log Levels

╔═══════════════════════════════════════════════════════════╗ ║ Level │ Cuándo usar │ Ejemplo ║ ╠═════════╪════════════════════════════╪═══════════════════╣ ║ DEBUG │ Dev diagnostics │ "Buffer size=512" ║ ║ INFO │ Important events │ "Plugin loaded" ║ ║ WARNING │ Recoverable issues │ "High CPU 85%" ║ ║ ERROR │ Failures, exceptions │ "File not found" ║ ║ FATAL │ Unrecoverable crash │ "Out of memory" ║ ╚═══════════════════════════════════════════════════════════╝

Level Guidelines

DEBUG - Variable values during development - Internal state inspection - Algorithm step-by-step execution - Only in development builds

INFO - Application lifecycle events (startup, shutdown) - Plugin/component loading - Configuration changes - Sample rate changes - Audio device changes

WARNING - Performance degradation (CPU > 80%, buffer underruns) - Deprecated API usage - Recoverable errors (retry succeeded) - Configuration fallbacks - Resource limits approaching

ERROR - File I/O failures - Audio device errors - Plugin load failures - Invalid user input - Network timeouts

FATAL - Out of memory - Unhandled exceptions - Critical resource failure - Corrupted state requiring shutdown

🔒 PII Handling

NEVER log:

// ❌ BAD - Logs PII
LOG_INFO("User john.doe@example.com loaded preset");
LOG_INFO("License key: ABC-123-DEF-456");
LOG_INFO("File: C:\\Users\\JohnDoe\\Documents\\project.als");
LOG_INFO("Connected from IP: 192.168.1.100");
// ✅ GOOD - No PII
LOG_INFO("User loaded preset", {{"user_id_hash", hashUserId(user)}});
LOG_INFO("License validated");
LOG_INFO("File: project.als");
LOG_INFO("Connected from local network");

Anonymization Rules

PII Type How to Anonymize Example
User ID SHA256 hash a3f5b8c...
Email Domain only @example.com
File path Relative path or filename only project.als
IP address First 3 octets + mask 192.168.1.xxx
License key First 4 chars only ABC-xxx-xxx-xxx
MAC address Hash or omit sha256(mac)

Implementation Example

// PII sanitization helper
std::string sanitizeFilePath(const std::string& fullPath) {
    // Return only filename, not full path
    size_t lastSlash = fullPath.find_last_of("/\\");
    return (lastSlash != std::string::npos)
        ? fullPath.substr(lastSlash + 1)
        : fullPath;
}

std::string hashUserId(const std::string& userId) {
    // Simple hash for demonstration
    return std::to_string(std::hash<std::string>{}(userId));
}

// Usage
LOG_INFO("Project loaded", {
    {"file", sanitizeFilePath(projectPath)},
    {"user_hash", hashUserId(currentUser)}
});

⚡ Performance

┌────────────────────────────────────────┐ │ Hot path (audio thread): │ │ → NO LOGGING (zero allocation) │ │ │ │ Cold path (UI, init): │ │ → Log libremente │ └────────────────────────────────────────┘

Audio Thread Rules

// ❌ NEVER in audio callback
void processAudio(float* buffer, int frames) {
    LOG_DEBUG("Processing " << frames << " frames");  // ❌ ALLOCATION!

    for (int i = 0; i < frames; ++i) {
        buffer[i] *= gain_;
    }
}

// ✅ Conditional logging outside hot path
void processAudio(float* buffer, int frames) {
    // No logging in hot path
    for (int i = 0; i < frames; ++i) {
        buffer[i] *= gain_;
    }
}

void setGain(float gain) {
    gain_ = gain;
    LOG_INFO("Gain changed", {{"value", gain}});  // ✅ Cold path OK
}

Performance Budget

Context Max Log Rate Max Message Size
Audio thread 0 Hz 0 bytes
UI thread 100 Hz 1 KB
Background thread 1000 Hz 10 KB
Startup/shutdown Unlimited 100 KB

Conditional Logging

// Only log in debug builds
#ifdef DEBUG
    LOG_DEBUG("Internal state", {{"foo", foo}, {"bar", bar}});
#endif

// Rate limiting for repeated events
static RateLimiter limiter(std::chrono::seconds(1));
if (limiter.allow()) {
    LOG_WARNING("Buffer underrun detected");
}

📐 Structured Logging

JSON Format

{
  "timestamp": "2025-01-15T10:30:00.123Z",
  "level": "INFO",
  "logger": "AudioProcessor",
  "message": "Buffer processed",
  "context": {
    "buffer_size": 512,
    "sample_rate": 48000,
    "thread_id": 12345,
    "cpu_usage": 0.42
  },
  "tags": ["audio", "realtime"]
}

Context Fields

Always include: - timestamp - ISO 8601 format with milliseconds - level - Log level (DEBUG, INFO, WARNING, ERROR, FATAL) - logger - Component/class name - message - Human-readable description

Optionally include: - thread_id - For debugging race conditions - cpu_usage - For performance correlation - memory_usage - For leak detection - request_id - For distributed tracing - session_id - For user session tracking

Implementation Example (C++)

// Using nlohmann/json library
#include <nlohmann/json.hpp>

void logStructured(LogLevel level, const std::string& message,
                   const std::map<std::string, std::string>& context) {
    nlohmann::json log;
    log["timestamp"] = getCurrentTimestampISO8601();
    log["level"] = logLevelToString(level);
    log["logger"] = getLoggerName();
    log["message"] = message;
    log["context"] = context;

    std::cout << log.dump() << std::endl;
}

// Usage
logStructured(LogLevel::INFO, "Plugin loaded", {
    {"plugin_name", "Compressor"},
    {"plugin_version", "1.2.3"},
    {"load_time_ms", std::to_string(loadTimeMs)}
});

Implementation Example (.NET)

// Using Serilog
Log.Information("Plugin loaded {@Context}", new {
    PluginName = "Compressor",
    PluginVersion = "1.2.3",
    LoadTimeMs = loadTimeMs
});

📊 Log Message Design

Good Message Structure

[VERB] [NOUN] [OUTCOME] + Context

Examples:
✅ "Plugin loaded successfully" + {name, version, time}
✅ "Audio device started" + {device_name, sample_rate, buffer_size}
✅ "File parsing failed" + {file, error, line_number}

Bad Message Structure

❌ "Error" (no context)
❌ "Something went wrong" (vague)
❌ "Debug message 123" (meaningless)
❌ "TODO: add better logging" (unprofessional)

Actionable Messages

// ❌ Not actionable
LOG_ERROR("Processing failed");

// ✅ Actionable
LOG_ERROR("Audio buffer overflow", {
    {"buffer_size", bufferSize},
    {"required_size", requiredSize},
    {"action", "Increase buffer size in settings"}
});

🔍 Correlation

Request ID Pattern

// Generate unique ID per operation
std::string requestId = generateUUID();

LOG_INFO("Operation started", {{"request_id", requestId}});

// ... do work ...

LOG_INFO("Operation completed", {
    {"request_id", requestId},
    {"duration_ms", durationMs}
});

Thread Context

// Thread-local storage for context
thread_local std::map<std::string, std::string> logContext;

void setLogContext(const std::string& key, const std::string& value) {
    logContext[key] = value;
}

void log(LogLevel level, const std::string& message) {
    // Automatically include thread context
    auto fullContext = logContext;
    fullContext["message"] = message;
    logStructured(level, message, fullContext);
}

// Usage
setLogContext("session_id", sessionId);
setLogContext("user_hash", userHash);

// All subsequent logs in this thread include session/user context
LOG_INFO("Action performed");

📏 Size Limits

Element Limit Reason
Message 1 KB Prevent log explosion
Context 10 KB Reasonable structured data
Single log 100 KB Max for debugging stack trace
Log file 100 MB Rolling file size

🎭 Environment-Specific Configuration

// Development
LOG_LEVEL = DEBUG
LOG_OUTPUT = Console + File

// Staging
LOG_LEVEL = INFO
LOG_OUTPUT = File + ELK

// Production
LOG_LEVEL = WARNING
LOG_OUTPUT = ELK + S3 (errors only)

📚 Examples by Component

Audio Engine

LOG_INFO("Audio engine started", {
    {"sample_rate", sampleRate},
    {"buffer_size", bufferSize},
    {"channels", numChannels}
});

LOG_WARNING("CPU usage high", {
    {"cpu_percent", cpuUsage},
    {"threshold", 80},
    {"action", "Consider increasing buffer size"}
});

LOG_ERROR("Audio device error", {
    {"device", deviceName},
    {"error", errorMessage},
    {"recovery", "Attempting to reconnect"}
});

Plugin Host

LOG_INFO("Plugin loaded", {
    {"plugin", pluginName},
    {"format", "VST3"},
    {"latency_samples", latency}
});

LOG_WARNING("Plugin validation failed", {
    {"plugin", pluginPath},
    {"reason", validationError},
    {"fallback", "Using default plugin"}
});

File I/O

LOG_INFO("Project loaded", {
    {"file", sanitizeFilePath(path)},
    {"format", "ALS"},
    {"load_time_ms", loadTimeMs}
});

LOG_ERROR("File parsing failed", {
    {"file", sanitizeFilePath(path)},
    {"line", lineNumber},
    {"error", parseError}
});