Skip to content
Data
Skill

/walkeros-using-logger

The logger is walkerOS's standard logging system, available in all sources and destinations via `env.logger` or `logger` parameter. It provides scoped, level-aware logging that replaces console.log.

BOOST
From plugin
walkeros
34822 skills
Install
$ npx -y skills add elbwalker/walkerOS --skill walkeros-using-logger --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/walkeros-using-logger

Context preview

The summary Claude sees to decide when to auto-load this skill.

The logger is walkerOS's standard logging system, available in all sources and destinations via `env.logger` or `logger` parameter. It provides scoped, level-aware logging that replaces console.log.

SKILL.md

walkeros-using-logger.SKILL.md
name: walkeros-using-logger
description:
  Use when working with walkerOS sources/destinations to understand standard
  logging patterns, replace console.log, or add logging to external API calls.
  Covers DRY principles, when to log, and migration patterns.

Using the walkerOS Logger

Overview

The logger is walkerOS's standard logging system, available in all sources and destinations via `env.logger` or `logger` parameter. It provides scoped, level-aware logging that replaces console.log.

**Core principle:** Don't log what the collector already logs. Only log meaningful operations like external API calls, transformations, and validation errors.

Logger Access

In Sources

export const sourceFetch = async (
  config: PartialConfig,
  env: Types['env'], // env.logger is available here
): Promise<FetchSource> => {
  // Logger is scoped automatically by collector: [type:sourceId]
  env.logger.info('Server listening on port 3000');
};

In Destinations

export const destinationDataManager: DestinationInterface = {
  async init({ config, env, logger }) {
    // logger parameter is scoped automatically: [datamanager]
    logger.debug('Auth client created');
  },

  async push(event, { config, data, env, logger }) {
    // logger parameter is scoped: [datamanager]
    logger.debug('API response', { status: 200 });
  },
};

**Note:** You don't need to create or configure the logger—it's provided automatically with proper scoping.

Logger Methods

interface Logger.Instance {
  error(message: string | Error, context?: unknown | Error): void;
  warn(message: string | Error, context?: unknown | Error): void;
  info(message: string | Error, context?: unknown | Error): void;
  debug(message: string | Error, context?: unknown | Error): void;
  throw(message: string | Error, context?: unknown): never;
  json(data: unknown): void;
  scope(name: string): Logger.Instance;
}

Log Levels

  • **ERROR (0)**: Always visible—use for fatal/critical errors only
  • **WARN (1)**: Degraded state, config issues, transient failures
  • **INFO (2)**: High-level operations (server startup, event processed)
  • **DEBUG (3)**: Low-level details (API calls, transformations)

**Default**: ERROR only (must configure to see WARN/INFO/DEBUG)

Context Parameter

All methods accept optional structured context:

logger.debug('Sending to API', {
  endpoint: '/events',
  method: 'POST',
  eventCount: 5,
});
// Output: DEBUG [datamanager] Sending to API { endpoint: '/events', method: 'POST', eventCount: 5 }

When to Log (and When NOT to)

❌ DON'T Log These (Collector Handles)

  • **Init status**: "Initializing...", "Init started", "Init complete"
  • **Push status**: "Processing event...", "Event received"
  • **Generic status**: "Settings validated", "Config loaded"
  • **Duplicate scoping**: Don't add source/dest name (already in scope)

**Why:** Collector can log these automatically since it calls init/push and has scoped logger.

✅ DO Log These (Meaningful Operations)

  • **External API calls**: Before/after with request/response details
  • **Auth operations**: Token refresh, client creation/failures
  • **Transformations**: Complex mappings or data processing
  • **Validation errors**: Always use `logger.throw` for fatal errors

Usage Patterns

Pattern 1: Validation Errors (Always Use logger.throw)

// ✅ GOOD - Fatal configuration error
async init({ config, logger }) {
  const { apiKey, projectId } = config.settings || {};

  if (!apiKey) {
    logger.throw('Config settings apiKey missing');
  }

  if (!projectId) {
    logger.throw('Config settings projectId missing');
  }
}

**Why logger.throw:**

  • Logs the error at ERROR level (always visible)
  • Throws Error automatically (no separate throw needed)
  • Collector catches and handles gracefully
  • Never returns (TypeScript type: `never`)

Pattern 2: External API Calls

// ✅ GOOD - Log external calls with context
async push(event, { config, logger }) {
  const endpoint = 'https://api.vendor.com/events';

  // Log before call
  logger.debug('Calling API', {
    endpoint,
    method: 'POST',
    eventId: event.id,
  });

  const response = await fetch(endpoint, {
    method: 'POST',
    body: JSON.stringify(event),
  });

  // Log after call
  logger.debug('API response', {
    status: response.status,
    ok: response.ok,
  });

  if (!response.ok) {
    const errorText = await response.text();
    logger.throw(`API error (${response.status}): ${errorText}`);
  }
}

Pattern 3: Auth Operations

// ✅ GOOD - Log auth client creation
async init({ config, logger }) {
  try {
    const authClient = await createAuthClient(config.settings);
    logger.debug('Auth client created');

    return {
      env: { authClient },
    };
  } catch (error) {
    logger.throw(
      `Authentication failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
    );
  }
}

Pattern 4: Server Startup (Sources)

// ✅ GOOD - Log server listening (high-level info)
if (settings.port !== undefined) {
  server = app.listen(settings.port, () => {
    env.logger.info(
      `Express source listening on port ${settings.port}\n` +
        `   POST ${settings.path} - Event collection (JSON body)\n` +
        `   GET ${settings.path} - Pixel tracking (query params)\n` +
        `   OPTIONS ${settings.path} - CORS preflight`,
    );
  });
}

Anti-Patterns (What NOT to Do)

❌ BAD: Verbose Init Logging

async init({ logger }) {
  logger.debug('Data Manager init started'); // Redundant
  logger.info('Data Manager initializing...'); // Redundant
  logger.debug('Settings validated'); // Redundant

  const authClient = await createAuthClient();
  logger.debug('Auth client created'); // OK

  logger.info('Data Manager ready'); // Redundant
}

**Problem:** Collector knows when init is called. Only log me

Read more
Ships withwalkeros

From browser to BigQuery and ad platforms - warehouse-native, and ready for AI agents via MCP.

Get the whole plugin

Other skills on walkeros.