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.

Every III function handler has access to a context object that provides logging, tracing, and request metadata.

getContext()

Get the current context within a function handler.
import { getContext } from 'iii-sdk'

const context = getContext()
context
Context
Current execution context

Example: Basic Usage

import { init, getContext } from 'iii-sdk'

const iii = init('ws://localhost:49199')

iii.registerFunction(
  { id: 'users::create' },
  async (data: { email: string; name: string }) => {
    const { logger, trace } = getContext()
    
    // Log with structured data
    logger.info('Creating user', { email: data.email })
    
    // Add trace attributes
    trace?.setAttribute('user.email', data.email)
    
    const user = {
      id: crypto.randomUUID(),
      email: data.email,
      name: data.name,
      created_at: new Date().toISOString()
    }
    
    logger.info('User created', { userId: user.id })
    trace?.addEvent('User created successfully')
    
    return user
  }
)

Logger

The Logger provides structured logging with automatic trace correlation.

Methods

info()

Log an informational message.
logger.info(message, data?)
message
string
required
Log message
data
unknown
Optional structured data to include in the log

warn()

Log a warning message.
logger.warn(message, data?)
message
string
required
Warning message
data
unknown
Optional structured data

error()

Log an error message.
logger.error(message, data?)
message
string
required
Error message
data
unknown
Optional error details or structured data

debug()

Log a debug message.
logger.debug(message, data?)
message
string
required
Debug message
data
unknown
Optional debug data

Example: Structured Logging

import { init, getContext } from 'iii-sdk'

const iii = init('ws://localhost:49199')

iii.registerFunction(
  { id: 'orders::process' },
  async (data: { order_id: string; items: any[] }) => {
    const { logger } = getContext()
    
    logger.info('Processing order', {
      order_id: data.order_id,
      item_count: data.items.length
    })
    
    try {
      // Validate order
      if (data.items.length === 0) {
        logger.warn('Order has no items', { order_id: data.order_id })
        throw new Error('Order must contain at least one item')
      }
      
      // Process items
      for (const item of data.items) {
        logger.debug('Processing item', {
          order_id: data.order_id,
          item_id: item.id,
          quantity: item.quantity
        })
      }
      
      logger.info('Order processed successfully', {
        order_id: data.order_id
      })
      
      return { success: true, order_id: data.order_id }
      
    } catch (error) {
      logger.error('Failed to process order', {
        order_id: data.order_id,
        error: error.message
      })
      throw error
    }
  }
)

Automatic Trace Correlation

Logs are automatically correlated with traces:
import { init, getContext } from 'iii-sdk'

const iii = init('ws://localhost:49199')

iii.registerFunction(
  { id: 'service_a::handler' },
  async (data) => {
    const { logger } = getContext()
    
    logger.info('Service A: Processing request')
    // Log includes trace_id and span_id automatically
    
    // Call another service
    const result = await iii.call('service_b::handler', data)
    
    logger.info('Service A: Received response from B')
    
    return result
  }
)

iii.registerFunction(
  { id: 'service_b::handler' },
  async (data) => {
    const { logger } = getContext()
    
    logger.info('Service B: Processing request')
    // This log has the same trace_id, allowing correlation
    
    return { processed: true }
  }
)
All logs from both services share the same trace_id, making it easy to trace requests across services.

Trace Span

The trace span allows adding custom attributes, events, and status to the current trace.

setAttribute()

Add a custom attribute to the span.
trace?.setAttribute(key, value)
key
string
required
Attribute key
value
string | number | boolean
required
Attribute value

addEvent()

Add a timestamped event to the span.
trace?.addEvent(name, attributes?)
name
string
required
Event name
attributes
Record<string, any>
Optional event attributes

setStatus()

Set the span status.
import { SpanStatusCode } from 'iii-sdk/telemetry'

trace?.setStatus({ code: SpanStatusCode.ERROR, message: 'Operation failed' })

recordException()

Record an exception in the span.
try {
  // risky operation
} catch (error) {
  trace?.recordException(error as Error)
  throw error
}

Example: Rich Tracing

import { init, getContext } from 'iii-sdk'
import { SpanStatusCode } from 'iii-sdk/telemetry'

const iii = init('ws://localhost:49199')

iii.registerFunction(
  { id: 'payments::process' },
  async (data: { amount: number; currency: string; user_id: string }) => {
    const { logger, trace } = getContext()
    
    // Add attributes
    trace?.setAttribute('payment.amount', data.amount)
    trace?.setAttribute('payment.currency', data.currency)
    trace?.setAttribute('user.id', data.user_id)
    
    try {
      // Step 1: Validate
      trace?.addEvent('Validating payment details')
      await validatePayment(data)
      
      // Step 2: Charge
      trace?.addEvent('Charging payment', {
        'payment.method': 'stripe'
      })
      const charge = await chargePayment(data)
      
      trace?.setAttribute('payment.charge_id', charge.id)
      trace?.setAttribute('payment.status', 'succeeded')
      
      // Step 3: Record
      trace?.addEvent('Recording transaction')
      await recordTransaction(charge)
      
      logger.info('Payment processed', {
        charge_id: charge.id,
        amount: data.amount
      })
      
      trace?.setStatus({ code: SpanStatusCode.OK })
      
      return {
        success: true,
        charge_id: charge.id
      }
      
    } catch (error) {
      // Record the exception in the trace
      trace?.recordException(error as Error)
      trace?.setStatus({
        code: SpanStatusCode.ERROR,
        message: error.message
      })
      
      logger.error('Payment failed', {
        error: error.message,
        user_id: data.user_id
      })
      
      throw error
    }
  }
)

withContext()

Manually set context for async operations (advanced use case).
import { withContext } from 'iii-sdk'

await withContext(fn, context)
fn
(context: Context) => Promise<T>
required
Async function to execute with the context
context
Context
required
Context object to use
result
T
The function’s return value
You typically don’t need to use withContext() directly. The III SDK manages context automatically for function handlers.

Example: Custom Context

import { withContext, Logger } from 'iii-sdk'

// Create a custom logger
const customLogger = new Logger('custom-trace-id', 'my-service')

// Run code with custom context
await withContext(
  async (ctx) => {
    ctx.logger.info('Using custom context')
    // Your code here
  },
  { logger: customLogger }
)

Logger Constructor

Create a standalone logger instance outside of function handlers.
import { Logger } from 'iii-sdk'

const logger = new Logger(traceId?, serviceName?, spanId?)
traceId
string
Optional trace ID for correlation
serviceName
string
Optional service name
spanId
string
Optional span ID

Example: Standalone Logger

import { Logger } from 'iii-sdk'

// Logger without trace context
const logger = new Logger()
logger.info('Application starting')

// Logger with trace ID
const traceLogger = new Logger('abc123', 'my-service')
traceLogger.info('Processing request', { request_id: '456' })

Best Practices

// Good - structured data
logger.info('User created', { userId: user.id, email: user.email })

// Avoid - string interpolation
logger.info(`User ${user.id} created with email ${user.email}`)
const { trace } = getContext()

// Business context
trace?.setAttribute('order.id', order.id)
trace?.setAttribute('order.total', order.total)
trace?.setAttribute('user.tier', user.tier)

// Technical context
trace?.setAttribute('db.query_count', queries.length)
trace?.setAttribute('cache.hit', true)
// debug: Detailed information for debugging
logger.debug('Cache miss', { key })

// info: General informational messages
logger.info('User logged in', { userId })

// warn: Warning messages for recoverable issues
logger.warn('Rate limit approaching', { current, limit })

// error: Error messages for failures
logger.error('Payment failed', { error: err.message })
const { trace } = getContext()

trace?.addEvent('Order validated')
// ... validation logic

trace?.addEvent('Payment processed', {
  'payment.method': 'card',
  'payment.amount': amount
})
// ... payment logic

trace?.addEvent('Order completed')
try {
  await riskyOperation()
} catch (error) {
  const { logger, trace } = getContext()
  
  // Log the error
  logger.error('Operation failed', {
    error: error.message,
    stack: error.stack
  })
  
  // Record in trace
  trace?.recordException(error as Error)
  trace?.setStatus({
    code: SpanStatusCode.ERROR,
    message: error.message
  })
  
  throw error
}

Context Outside Handlers

If you call getContext() outside a function handler, you get a default context with a basic logger:
import { getContext } from 'iii-sdk'

// Outside any function handler
const { logger } = getContext()
logger.info('Application initialized') // Works, but no trace correlation

// Inside a function handler
iii.registerFunction({ id: 'fn' }, async (data) => {
  const { logger, trace } = getContext()
  // Now logger has trace context and trace is available
})

Build docs developers (and LLMs) love