Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/iii-hq/sdk/llms.txt

Use this file to discover all available pages before exploring further.

Overview

The Context API provides access to function-scoped resources like logging and tracing spans. Every function handler runs within a context that is accessible via get_context().

Accessing Context

get_context

Get the current function’s context.
pub fn get_context() -> Context
Context
Context
The context for the currently executing function
Example:
use iii_sdk::{III, get_context};
use serde_json::{json, Value};

let iii = III::new("ws://localhost:49134");

iii.register_function("process", |input: Value| async move {
    let ctx = get_context();
    
    ctx.logger.info("Function started", None);
    
    // Your logic here
    let result = json!({ "status": "ok" });
    
    ctx.logger.info("Function completed", Some(result.clone()));
    
    Ok(result)
});
get_context() returns a default context when called outside of a function handler (e.g., in application startup code).

with_context

Execute a function within a custom context.
pub async fn with_context<F, Fut, T>(context: Context, f: F) -> T
where
    F: FnOnce() -> Fut,
    Fut: std::future::Future<Output = T>
context
Context
required
Custom context to use for the execution
f
F
required
Async function to execute within the context
Example:
use iii_sdk::{Context, Logger, with_context};

let custom_context = Context {
    logger: Logger::new(Some("custom-function".to_string())),
    span: None,
};

with_context(custom_context, || async {
    let ctx = get_context();
    ctx.logger.info("Inside custom context", None);
}).await;
The SDK automatically wraps function handlers with with_context, so you typically don’t need to call this manually.

Context Structure

Context

The context available within function handlers.
pub struct Context {
    pub logger: Logger,
    pub span: Option<tracing::Span>,
}
Fields:
  • logger: Logger instance scoped to the current function
  • span: Active tracing span (used internally by the SDK, typically not accessed directly)

Logger

The Logger provides structured logging with automatic trace context integration.

Logger Methods

info

Log an informational message.
pub fn info(&self, message: &str, data: Option<Value>)
message
&str
required
Log message
data
Option<Value>
Optional structured data to include with the log
Example:
let ctx = get_context();
ctx.logger.info("User logged in", None);
ctx.logger.info("Order processed", Some(json!({
    "order_id": "order-123",
    "amount": 99.99
})));

warn

Log a warning message.
pub fn warn(&self, message: &str, data: Option<Value>)
Example:
let ctx = get_context();
ctx.logger.warn("Rate limit approaching", Some(json!({
    "remaining": 5,
    "reset_at": "2024-01-15T10:30:00Z"
})));

error

Log an error message.
pub fn error(&self, message: &str, data: Option<Value>)
Example:
let ctx = get_context();
ctx.logger.error("Database connection failed", Some(json!({
    "error": "Connection timeout",
    "retry_count": 3
})));

debug

Log a debug message.
pub fn debug(&self, message: &str, data: Option<Value>)
Example:
let ctx = get_context();
ctx.logger.debug("Cache hit", Some(json!({
    "key": "user:123",
    "ttl": 3600
})));

OpenTelemetry Integration

When the otel feature is enabled, logs are automatically exported via OpenTelemetry:
#[cfg(feature = "otel")]
{
    use iii_sdk::{III, get_context, OtelConfig};
    use serde_json::json;
    
    let otel_config = OtelConfig {
        enabled: Some(true),
        logs_enabled: Some(true),
        ..Default::default()
    };
    
    let iii = III::new("ws://localhost:49134");
    iii.set_otel_config(otel_config);
    iii.connect().await?;
    
    iii.register_function("process", |input| async move {
        let ctx = get_context();
        
        // These logs are exported as OpenTelemetry LogRecords
        // and automatically include trace context
        ctx.logger.info("Processing started", None);
        ctx.logger.debug("Input data", Some(input.clone()));
        
        // Your logic
        
        ctx.logger.info("Processing completed", None);
        Ok(json!({ "status": "ok" }))
    });
}
OpenTelemetry LogRecords include:
  • Timestamp (observed and actual)
  • Severity level (Debug, Info, Warn, Error)
  • Message body
  • Function name attribute
  • Structured data as attributes
  • Trace context (trace_id, span_id, trace_flags)
When the otel feature is disabled, logs fall back to the tracing crate.

Creating Custom Loggers

Logger::new

Create a logger with a custom function name.
pub fn new(function_name: Option<String>) -> Self
function_name
Option<String>
Function name to include in log records
Example:
use iii_sdk::Logger;

let logger = Logger::new(Some("background-task".to_string()));
logger.info("Task started", None);

Complete Example

Here’s a complete example showing context usage:
use iii_sdk::{III, get_context, IIIError};
use serde_json::{json, Value};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let iii = III::new("ws://localhost:49134");
    
    // Register a function that uses context
    iii.register_function("user.create", |input: Value| async move {
        let ctx = get_context();
        
        // Log the start
        ctx.logger.info("Creating new user", Some(input.clone()));
        
        // Validate input
        let email = input.get("email")
            .and_then(|v| v.as_str())
            .ok_or_else(|| {
                ctx.logger.error("Missing email field", None);
                IIIError::Handler("email is required".into())
            })?;
        
        ctx.logger.debug("Email validated", Some(json!({ "email": email })));
        
        // Create user (simulated)
        let user_id = uuid::Uuid::new_v4().to_string();
        
        let result = json!({
            "id": user_id,
            "email": email,
            "created_at": chrono::Utc::now().to_rfc3339()
        });
        
        ctx.logger.info("User created successfully", Some(result.clone()));
        
        Ok(result)
    });
    
    // Register a function that calls another function
    iii.register_function("order.create", move |input: Value| {
        let iii = iii.clone();
        async move {
            let ctx = get_context();
            ctx.logger.info("Creating order", None);
            
            // Get user data
            let user_id = input.get("user_id")
                .and_then(|v| v.as_str())
                .ok_or_else(|| IIIError::Handler("user_id required".into()))?;
            
            ctx.logger.debug("Fetching user", Some(json!({ "user_id": user_id })));
            
            // Call user service (context is propagated)
            let user = iii.call("user.get", json!({ "id": user_id })).await?;
            
            ctx.logger.info("User fetched", Some(user.clone()));
            
            // Create order
            let order = json!({
                "id": uuid::Uuid::new_v4().to_string(),
                "user": user,
                "items": input.get("items").cloned().unwrap_or(json!([]))
            });
            
            ctx.logger.info("Order created", Some(order.clone()));
            
            Ok(order)
        }
    });
    
    iii.connect().await?;
    
    // Test the functions
    let user = iii.call("user.create", json!({
        "email": "alice@example.com"
    })).await?;
    
    println!("Created user: {}", user);
    
    let order = iii.call("order.create", json!({
        "user_id": user.get("id").unwrap(),
        "items": [{"sku": "ABC123", "qty": 2}]
    })).await?;
    
    println!("Created order: {}", order);
    
    iii.shutdown_async().await;
    
    Ok(())
}

Log Levels

When to use each level:
  • debug: Detailed information for debugging (verbose)
  • info: General informational messages about application flow
  • warn: Warning messages for potentially problematic situations
  • error: Error messages for failures that require attention

Best Practices

  1. Always use structured data: Pass JSON objects to the data parameter instead of formatting strings:
    // Good
    ctx.logger.info("User created", Some(json!({ "user_id": id })));
    
    // Avoid
    ctx.logger.info(&format!("User {} created", id), None);
    
  2. Log at appropriate levels: Use debug for verbose details, info for key events, warn for issues, and error for failures.
  3. Include context in structured data: Add relevant IDs and metadata to help with debugging:
    ctx.logger.error("Payment failed", Some(json!({
        "order_id": order_id,
        "amount": amount,
        "error_code": code
    })));
    
  4. Don’t log sensitive data: Avoid logging passwords, tokens, or PII:
    // Bad - logs password
    ctx.logger.debug("Auth attempt", Some(json!({ "password": pwd })));
    
    // Good - doesn't log password
    ctx.logger.debug("Auth attempt", Some(json!({ "username": user })));
    

See Also

Build docs developers (and LLMs) love