> ## 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.

# Context

> Access function context and logging

## 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.

```rust theme={null}
pub fn get_context() -> Context
```

<ResponseField name="Context" type="Context">
  The context for the currently executing function
</ResponseField>

**Example:**

```rust theme={null}
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)
});
```

<Note>
  `get_context()` returns a default context when called outside of a function handler (e.g., in application startup code).
</Note>

### with\_context

Execute a function within a custom context.

```rust theme={null}
pub async fn with_context<F, Fut, T>(context: Context, f: F) -> T
where
    F: FnOnce() -> Fut,
    Fut: std::future::Future<Output = T>
```

<ParamField path="context" type="Context" required>
  Custom context to use for the execution
</ParamField>

<ParamField path="f" type="F" required>
  Async function to execute within the context
</ParamField>

**Example:**

```rust theme={null}
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;
```

<Note>
  The SDK automatically wraps function handlers with `with_context`, so you typically don't need to call this manually.
</Note>

## Context Structure

### Context

The context available within function handlers.

```rust theme={null}
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.

```rust theme={null}
pub fn info(&self, message: &str, data: Option<Value>)
```

<ParamField path="message" type="&str" required>
  Log message
</ParamField>

<ParamField path="data" type="Option<Value>">
  Optional structured data to include with the log
</ParamField>

**Example:**

```rust theme={null}
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.

```rust theme={null}
pub fn warn(&self, message: &str, data: Option<Value>)
```

**Example:**

```rust theme={null}
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.

```rust theme={null}
pub fn error(&self, message: &str, data: Option<Value>)
```

**Example:**

```rust theme={null}
let ctx = get_context();
ctx.logger.error("Database connection failed", Some(json!({
    "error": "Connection timeout",
    "retry_count": 3
})));
```

#### debug

Log a debug message.

```rust theme={null}
pub fn debug(&self, message: &str, data: Option<Value>)
```

**Example:**

```rust theme={null}
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:

```rust theme={null}
#[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)

<Note>
  When the `otel` feature is disabled, logs fall back to the `tracing` crate.
</Note>

## Creating Custom Loggers

### Logger::new

Create a logger with a custom function name.

```rust theme={null}
pub fn new(function_name: Option<String>) -> Self
```

<ParamField path="function_name" type="Option<String>">
  Function name to include in log records
</ParamField>

**Example:**

```rust theme={null}
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:

```rust theme={null}
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:
   ```rust theme={null}
   // 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:
   ```rust theme={null}
   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:
   ```rust theme={null}
   // 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

* [Functions API](/rust/api/functions) - Function handlers automatically receive context
* [Telemetry API](/rust/api/telemetry) - Configure OpenTelemetry for log export
* [Invocation API](/rust/api/invocation) - Context is propagated across function calls
