> Part of the walkerOS documentation. Project overview and full index: <https://www.walkeros.io/llms.txt>

# Logger

walkerOS provides a centralized logging system that enables consistent logging across the collector, sources, and destinations. The logger supports four log levels and provides scoped logging for better debugging.

## Configuration[​](#configuration "Direct link to Configuration")

Configure logging when initializing the collector:

```
import { startFlow } from '@walkeros/collector';

const { collector, elb } = await startFlow({
  logger: {
    level: 'DEBUG', // 'ERROR' | 'WARN' | 'INFO' | 'DEBUG'
  },
});
```

### Log levels[​](#log-levels "Direct link to Log levels")

| Level   | Value | Description                              |
| ------- | ----- | ---------------------------------------- |
| `ERROR` | 0     | Only errors (default)                    |
| `WARN`  | 1     | Errors and warnings                      |
| `INFO`  | 2     | Errors, warnings, and informational      |
| `DEBUG` | 3     | All messages including debug information |

### Choosing a level on public endpoints[​](#choosing-a-level-on-public-endpoints "Direct link to Choosing a level on public endpoints")

A collector endpoint is exposed to the open internet, so log level decides whether ambient noise becomes an alert.

| Situation                                                                     | Level                                         |
| ----------------------------------------------------------------------------- | --------------------------------------------- |
| Ambient internet noise: unparseable bodies, unknown paths, oversized payloads | `debug`, plus a status counter                |
| An event the pipeline rejects as invalid                                      | `warn` with the reason, counted as `rejected` |
| A settled `ok: false` arriving after an HTTP ack was already sent             | `warn`                                        |
| Handler faults and rejected push promises                                     | `error`                                       |

The collector already logs thrown pipeline failures at error, so sources do not duplicate that. Nothing a scanner can trigger should reach `error`.

## Using the logger[​](#using-the-logger "Direct link to Using the logger")

The logger is automatically available in destination and source contexts:

### In destinations[​](#in-destinations "Direct link to In destinations")

```
const myDestination = {
  type: 'my-destination',
  config: {},

  push(event, { logger }) {
    logger.debug('Processing event', { name: event.name });

    try {
      // Send event to service
      sendToService(event);
      logger.info('Event sent successfully');
    } catch (error) {
      if (error.retryable) {
        logger.warn('Event send failed, will retry', error);
      } else {
        logger.error('Failed to send event', error);
      }
    }
  },
};
```

### In sources[​](#in-sources "Direct link to In sources")

```
const mySource = async (config, env) => {
  const { logger } = env;

  logger.info('Source initialized');

  return {
    type: 'my-source',
    config,
    push: async (event) => {
      logger.debug('Received event', event);
      await env.push(event);
    },
  };
};
```

## Logger API[​](#logger-api "Direct link to Logger API")

### Methods[​](#methods "Direct link to Methods")

| Method                     | Description                                           |
| -------------------------- | ----------------------------------------------------- |
| `error(message, context?)` | Log an error message                                  |
| `warn(message, context?)`  | Log a warning (degraded state, config issues)         |
| `info(message, context?)`  | Log an informational message                          |
| `debug(message, context?)` | Log a debug message                                   |
| `throw(message, context?)` | Log an error and throw an exception (returns `never`) |
| `json(data)`               | Output structured JSON data                           |
| `scope(name)`              | Create a scoped logger with a prefixed scope          |

### Error handling[​](#error-handling "Direct link to Error handling")

The logger accepts `Error` objects directly and automatically extracts relevant information:

```
try {
  await riskyOperation();
} catch (error) {
  // Error properties (message, name, stack, cause) are extracted automatically
  logger.error(error);

  // Or with additional context
  logger.error('Operation failed', { error, operation: 'sync' });
}
```

### Boundary error logs[​](#boundary-error-logs "Direct link to Boundary error logs")

When an exception escapes the inner pipeline of `collector.push` or `collector.command`, the collector emits a structured error log and increments `collector.status.failed`. The messages are `'push failed'` and `'command failed'` respectively, with the full event/ingest or command/data context as structured fields. See [Status — What `failed` counts](https://www.walkeros.io/docs/collector.md#what-failed-counts) for details and PII implications.

Internal pipeline failures from mapping, source startup, transformer init, and destination init are scoped under `'source'`, `'transformer:<type>'`, or the destination type. User-callback throws from `on` subscriptions are scoped under `'on'`. The full list of error verbs and scopes is documented in [Status — What `failed` counts](https://www.walkeros.io/docs/collector.md#what-failed-counts).

### Throwing errors[​](#throwing-errors "Direct link to Throwing errors")

Use `logger.throw()` to log an error and throw in a single call:

```
function validateConfig(config) {
  if (!config.apiKey) {
    // Logs the error AND throws - returns 'never' type
    logger.throw('API key is required');
  }

  // TypeScript knows apiKey exists here due to 'never' return type
  return config.apiKey;
}
```

## Scoped loggers[​](#scoped-loggers "Direct link to Scoped loggers")

Scoped loggers prefix log messages with context information, making it easier to trace logs to their origin:

```
// In a destination, the logger is already scoped: [google-gtag:myGtag]
push(event, { logger }) {
  logger.info('Processing'); // Output: [google-gtag:myGtag] Processing

  // Create nested scope
  const ga4Logger = logger.scope('ga4');
  ga4Logger.debug('Sending to GA4'); // Output: [google-gtag:myGtag:ga4] Sending to GA4
}
```

## Custom handler[​](#custom-handler "Direct link to Custom handler")

For advanced use cases like sending logs to external services, configure a custom handler:

```
import { startFlow } from '@walkeros/collector';

const { collector, elb } = await startFlow({
  logger: {
    level: 'INFO',
    handler: (level, message, context, scope, originalHandler) => {
      // Send to external logging service
      loggingService.log({
        level,
        message,
        context,
        scope: scope.join(':'),
        timestamp: Date.now(),
      });

      // Optionally call the original console handler
      originalHandler(level, message, context, scope);
    },
  },
});
```

The handler receives:

| Parameter         | Type             | Description                                       |
| ----------------- | ---------------- | ------------------------------------------------- |
| `level`           | `Level`          | Log level enum (ERROR=0, WARN=1, INFO=2, DEBUG=3) |
| `message`         | `string`         | The log message                                   |
| `context`         | `LogContext`     | Additional context object                         |
| `scope`           | `string[]`       | Array of scope names                              |
| `originalHandler` | `DefaultHandler` | The default console handler                       |

## Testing[​](#testing "Direct link to Testing")

Use the mock logger utility for testing destinations and sources:

```
import { createMockLogger } from '@walkeros/core';

describe('MyDestination', () => {
  it('logs events correctly', async () => {
    const mockLogger = createMockLogger();

    await myDestination.push(testEvent, {
      config: {},
      logger: mockLogger,
    });

    expect(mockLogger.info).toHaveBeenCalledWith(
      'Event sent successfully'
    );
  });
});
```

The mock logger provides jest mock functions for all logger methods plus:

* `scopedLoggers`: Array of scoped loggers created via `scope()`
