Skill v1.0.1
currentAutomated scan100/1003 files
version: "1.0.1" 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.throwfor fatal errors
Usage Patterns
Pattern 1: Validation Errors (Always Use logger.throw)
// ✅ GOOD - Fatal configuration errorasync 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 contextasync push(event, { config, logger }) {const endpoint = 'https://api.vendor.com/events';// Log before calllogger.debug('Calling API', {endpoint,method: 'POST',eventId: event.id,});const response = await fetch(endpoint, {method: 'POST',body: JSON.stringify(event),});// Log after calllogger.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 creationasync 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'); // Redundantlogger.info('Data Manager initializing...'); // Redundantlogger.debug('Settings validated'); // Redundantconst authClient = await createAuthClient();logger.debug('Auth client created'); // OKlogger.info('Data Manager ready'); // Redundant}
Problem: Collector knows when init is called. Only log meaningful operations (auth client creation).
❌ BAD: Redundant Push Logging
async push(event, { logger }) {logger.debug('Processing event', {// Redundantname: event.name,id: event.id,});// Do work...logger.info('Event processed'); // Redundant}
Problem: Collector knows when push is called and can log automatically.
❌ BAD: Using console.log
// ❌ NEVER use console.log in sources/destinationsconsole.log('Processing event:', event.name);// ✅ Use logger insteadlogger.debug('API call', { endpoint });
Migration Checklist
When updating a source/destination to use the logger:
- [ ] Remove all
console.log,console.warn,console.errorstatements - [ ] Remove verbose init/push status logging (let collector handle)
- [ ] Add
logger.throwfor all validation errors (apiKey missing, etc.) - [ ] Add
logger.debugbefore external API calls (with endpoint, method) - [ ] Add
logger.debugafter external API calls (with response status) - [ ] Add
logger.debugfor auth operations (client creation, token refresh) - [ ] Use structured context (objects) instead of string concatenation
- [ ] Verify tests still pass with logger mocked
Testing with Logger
Use createMockLogger from @walkeros/core in tests:
import { createMockLogger } from '@walkeros/core';test('throws on missing apiKey', () => {const logger = createMockLogger();expect(() => {destination.init({ config: {}, logger });}).toThrow('Config settings apiKey missing');expect(logger.throw).toHaveBeenCalledWith('Config settings apiKey missing');});
Log Level Configuration
Default log level is ERROR. To see INFO/DEBUG logs:
import { startFlow } from '@walkeros/collector';const { elb } = await startFlow({logger: {level: 'DEBUG', // Show all logs},destinations: {/* ... */},});
Levels:
'ERROR': Only errors (default)'WARN': Errors + warnings'INFO': Errors + warnings + info'DEBUG': Everything
Related Skills
Destination interface
Source interface
Key Files:
- packages/core/src/logger.ts - Logger
implementation
Logger types
Testing utilities
Best Practice Examples:
API logging patterns
Server startup logging
Needs Improvement:
Missing all logging
Missing push logging
Missing push logging