Skip to content

Pre-GA Design Partner and Early Access only. Request access

Helix Docs

ctx.log

Reference for ctx.log with the info, warn, error, and debug signatures, metadata, and how logs appear locally versus in the dashboard.

For developers Updated Aug 18, 2026
View as Markdown

ctx.log is the structured logger. Each call emits one log record correlated with the current execution, so log lines appear inline with HTTP and KV steps on the execution timeline. Use it instead of console.log, which is captured only best-effort.

ContextLogger

interface ContextLogger {
  info(message: string, metadata?: Record<string, unknown>): void;
  warn(message: string, metadata?: Record<string, unknown>): void;
  error(message: string, metadata?: Record<string, unknown>): void;
  debug(message: string, metadata?: Record<string, unknown>): void;
}
ParameterTypeDescription
messagestringThe log record body
metadataRecord<string, unknown> (optional)Structured context, serialized reliably

All four methods are synchronous and return void; the level sets the record’s severity.

How metadata is carried

Metadata is kept as a structured object on the log record rather than flattened into one field per key. That preserves the full object for display and search while keeping cardinality bounded.

Correlation with the execution

Every record is tied to the current execution so it lands on the right timeline in the dashboard, alongside auto-instrumented HTTP and KV steps. Filter and search from the project’s Logs tab.

Dev versus production

The code is identical in both environments. Under helix dev, ctx.log pretty-prints to the terminal (colored level, timestamp, message, and metadata inline). In production, the same records appear on the execution timeline in the Helix dashboard.

Example

// functions/api/v1/sync.post.ts
import { z } from 'zod';
import { defineFunction } from '@trayai/helix-sdk';

export const input = z.object({
  userId: z.string(),
});

export default defineFunction<typeof input>(async (ctx) => {
  ctx.log.info('Starting sync', { userId: ctx.input.userId });

  const contacts = await ctx.http
    .authed('salesforce_prod')
    .get('https://acme.my.salesforce.com/services/data/v59.0/query', {
      searchParams: { q: 'SELECT Id, Name FROM Contact LIMIT 10' },
    })
    .json<{ records: unknown[] }>();

  ctx.log.info('Fetched contacts', { count: contacts.records.length });

  if (contacts.records.length === 0) {
    ctx.log.warn('Nothing to sync');
  }

  return { synced: contacts.records.length };
});

The execution timeline, auto-instrumented steps, and console.log capture limits are covered in the observability guide.

Loading search…

Jump to a section

tab to move · esc to close