Skip to content

Logging

mik-sdk provides structured JSON logging to stderr, compatible with major log aggregation systems.

#[allow(warnings)]
mod bindings;
use bindings::exports::mik::core::handler::{self, Guest, Response};
use mik_sdk::prelude::*;

The log module and macros are included in mik_sdk::prelude::*.

All logs are JSON objects written to stderr:

{
"level": "info",
"msg": "user created",
"id": "123",
"ts": "2025-01-16T10:30:00.500Z"
}

Fields:

  • level - Log level (debug, info, warn, error)
  • msg - Log message
  • ts - ISO 8601 timestamp
  • Additional fields from structured logging

Format-string style logging:

use mik_sdk::log;
log::info!("Server started on port 8080");
log::info!("User {} logged in", user_id);
log::warn!("Cache miss for key: {}", key);
log::warn!("Slow query detected: {}ms", elapsed);
log::error!("Database connection failed: {}", err);
log::error!("Failed to parse request body");
log::debug!("Request payload: {:?}", payload);
log::debug!("Cache size: {} entries", cache.len());

Output:

{"timestamp":"2025-01-16T10:30:00.500Z","level":"info","message":"Server started on port 8080"}
{"timestamp":"2025-01-16T10:30:00.501Z","level":"warn","message":"Cache miss for key: user:123"}

Key-value pairs for better searchability:

use mik_sdk::log;
log!(info, "user created", id: user_id, email: &email);
log!(warn, "rate limit approaching", remaining: count);
log!(error, "failed to fetch", url: &url, status: resp.status());
log!(debug, "request parsed", method: req.method(), path: req.path());

Output:

{"level":"info","msg":"user created","id":"123","email":"alice@example.com","ts":"2025-01-16T10:30:00.500Z"}
{"level":"warn","msg":"rate limit approaching","remaining":"5","ts":"2025-01-16T10:30:01.000Z"}
{"level":"error","msg":"failed to fetch","url":"https://api.example.com","status":"500","ts":"2025-01-16T10:30:02.000Z"}
LevelMacroUse Case
debuglog::debug!()Development info (compiled out in release)
infolog::info!()Normal operations
warnlog::warn!()Unexpected but handled situations
errorlog::error!()Errors requiring attention

log::debug!() is compiled out in release builds:

// Only appears in debug builds
log::debug!("Detailed debugging info: {:?}", data);
// In release builds, this generates no code
// Arguments are not evaluated
fn handler(req: &Request) -> Response {
let trace_id = req.trace_id_or("unknown");
let path = req.path_without_query();
log!(info, "request received",
trace_id: trace_id,
method: format!("{:?}", req.method()),
path: path
);
// ... handle request ...
log!(info, "request completed",
trace_id: trace_id,
status: 200
);
ok!({})
}
fn handler(req: &Request) -> Response {
let result = process_request(req);
match result {
Ok(data) => {
log!(info, "request succeeded", id: &data.id);
ok!({ "data": data })
}
Err(err) => {
log!(error, "request failed",
error: format!("{}", err),
path: req.path_without_query()
);
error! {
status: 500,
title: "Internal Error",
detail: "Processing failed"
}
}
}
}
fn handler(_req: &Request) -> Response {
let start = time::now_millis();
// ... do work ...
let elapsed = time::now_millis() - start;
log!(info, "operation completed", duration_ms: elapsed);
if elapsed > 1000 {
log::warn!("Slow operation detected: {}ms", elapsed);
}
ok!({})
}
fn handler(req: &Request) -> Response {
let incoming_trace = req.trace_id_or("");
let trace_id = if incoming_trace.is_empty() {
random::uuid()
} else {
incoming_trace.to_string()
};
log!(info, "starting request", trace_id: &trace_id);
// Forward trace ID to downstream
let response = fetch!(GET "http://api:8080/data")
.with_trace_id(Some(&trace_id))
.send()?;
log!(info, "downstream response",
trace_id: &trace_id,
status: response.status()
);
ok!({ "trace_id": trace_id })
}

The JSON format is compatible with major log aggregation systems:

SystemNotes
ELK StackAuto-parses JSON, maps fields
DatadogAuto-detects JSON, extracts standard fields
CloudWatch Logs InsightsQuery with parsed JSON fields
SplunkAuto-extracts JSON fields
Grafana LokiLabel extraction from JSON
MacroLevelRelease Build
log::info!(fmt, ...)infoIncluded
log::warn!(fmt, ...)warnIncluded
log::error!(fmt, ...)errorIncluded
log::debug!(fmt, ...)debugCompiled out
log!(level, "message", key: value, ...);

All structured logs are included in release builds.

use mik_sdk::log;
// Simple logging
log::info!("Starting handler");
log::warn!("Connection pool at 80%");
log::error!("Failed to connect: {}", err);
log::debug!("Debug: {:?}", data);
// Structured logging
log!(info, "user created", id: "123", email: "alice@example.com");
log!(warn, "rate limit", user: &user_id, remaining: 5);
log!(error, "api failed", url: &url, status: 500);

Output (stderr):

{"timestamp":"2025-01-16T10:30:00.500Z","level":"info","message":"Starting handler"}
{"level":"info","msg":"user created","id":"123","email":"alice@example.com","ts":"2025-01-16T10:30:00.501Z"}