Skip to content

Blank Transport

The built-in BlankTransport allows you to quickly create custom transports by providing your own shipToLogger function. This is perfect for simple custom logging logic, prototyping new transport ideas, or quick integrations with custom services.

Transport Source

TIP

If you want to create more advanced / complex transports, it is recommended you read the Creating Transports guide.

Installation

No additional packages needed beyond the core loglayer package:

sh
npm i loglayer
sh
pnpm add loglayer
sh
yarn add loglayer

Setup

typescript
import { LogLayer, BlankTransport } from 'loglayer'

const log = new LogLayer({
  transport: new BlankTransport({
    shipToLogger: ({ logLevel, messages, data, hasData }) => {
      // Your custom logging logic here
      console.log(`[${logLevel}]`, ...messages, data && hasData ? data : '');
      
      // Return value is used for debugging when consoleDebug is enabled
      return messages;
    }
  })
})

Configuration Options

shipToLogger (Required)

The function that will be called to handle log shipping. This is the only required parameter for creating a custom transport.

  • Type: (params: LogLayerTransportParams) => any[]
  • Required: true

Return Value: The function must return an array (any[]). This return value is used for debugging purposes when consoleDebug is enabled - it will be logged to the console using the appropriate console method based on the log level.

The function receives a LogLayerTransportParams object with these fields:

typescript
interface LogLayerTransportParams {
  /**
   * The log level of the message
   */
  logLevel: LogLevel;
  /**
   * The parameters that were passed to the log message method (eg: info / warn / debug / error)
   */
  messages: any[];
  /**
   * Object data such as metadata, context, and / or error data
   */
  data?: Record<string, any>;
  /**
   * If true, the data object is included in the message parameters
   */
  hasData?: boolean;
}

Message Parameters

The messages parameter is an array because LogLayer supports multiple parameters for formatting. See the Basic Logging section for more details.

For example, if a user does the following:

typescript
logger.withMetadata({foo: 'bar'}).info('hello world', 'foo');

The parameters passed to shipToLogger would be:

typescript
{
  logLevel: 'info',
  messages: ['hello world', 'foo'],
  data: {foo: 'bar'},
  hasData: true
}

level

Sets the minimum log level to process. Messages with a lower priority level will be ignored.

  • Type: "trace" | "debug" | "info" | "warn" | "error" | "fatal"
  • Default: "trace" (processes all log levels)

enabled

If false, the transport will not send logs to the logger.

  • Type: boolean
  • Default: true

consoleDebug

If true, the transport will log to the console for debugging purposes.

  • Type: boolean
  • Default: false

When consoleDebug is enabled, the return value from your shipToLogger function will be logged to the console using the appropriate console method based on the log level (e.g., console.info() for info logs, console.error() for error logs, etc.).

This is useful for debugging your custom transport logic and seeing exactly what data is being processed.

Error Serialization

When using the BlankTransport, it's recommended to configure LogLayer with an errorSerializer to ensure errors are properly serialized before being passed to your shipToLogger function. The serialize-error package is the recommended choice for consistent error serialization.

Installation

sh
npm install serialize-error
sh
yarn add serialize-error
sh
pnpm add serialize-error

Usage

typescript
import { LogLayer, BlankTransport } from 'loglayer'
import serializeError from 'serialize-error'

const log = new LogLayer({
  errorSerializer: serializeError,
  transport: new BlankTransport({
    shipToLogger: ({ logLevel, messages, data, hasData }) => {
      console.log(`[${logLevel}]`, ...messages, data && hasData ? data : '');
      return messages;
    }
  })
})

Before and After Example

Error Field Name

The error appears in data.err by default, but this field name can be customized using the errorFieldName configuration option. See the Error Handling Configuration section for more details.

Without errorSerializer:

typescript
const log = new LogLayer({
  transport: new BlankTransport({
    shipToLogger: ({ logLevel, messages, data, hasData }) => {
      console.log(`[${logLevel}]`, ...messages, data && hasData ? data : '');
      return messages;
    }
  })
})

log.withError(new Error('Database connection failed')).error('Failed to connect');
// Output: [error] Failed to connect { err: [Error: Database connection failed] }

With errorSerializer:

typescript
const log = new LogLayer({
  errorSerializer: serializeError,
  transport: new BlankTransport({
    shipToLogger: ({ logLevel, messages, data, hasData }) => {
      console.log(`[${logLevel}]`, ...messages, data && hasData ? data : '');
      return messages;
    }
  })
})

log.withError(new Error('Database connection failed')).error('Failed to connect');
// Output: [error] Failed to connect {
//   err: {
//     name: 'Error',
//     message: 'Database connection failed',
//     stack: 'Error: Database connection failed\n    at ...'
//   }
// }

Examples

Simple Console Logging

typescript
import { LogLayer, BlankTransport } from 'loglayer'

const log = new LogLayer({
  transport: new BlankTransport({
    shipToLogger: ({ logLevel, messages, data, hasData }) => {
      const timestamp = new Date().toISOString();
      const message = messages.join(" ");
      const dataStr = data && hasData ? ` | ${JSON.stringify(data)}` : '';
      
      console.log(`[${timestamp}] [${logLevel.toUpperCase()}] ${message}${dataStr}`);
      
      // Return value is used for debugging when consoleDebug is enabled
      return messages;
    }
  })
})
log.withMetadata({ user: 'john' }).info('User logged in');
// Output: [2023-12-01T10:30:00.000Z] [INFO] User logged in | {"user":"john"}

Custom API Integration

typescript
import { LogLayer, BlankTransport } from 'loglayer'

const log = new LogLayer({
  transport: new BlankTransport({
    shipToLogger: ({ logLevel, messages, data, hasData }) => {
      const payload = {
        level: logLevel,
        message: messages.join(" "),
        timestamp: new Date().toISOString(),
        ...(data && hasData ? data : {})
      };

      // Send to your custom API
      fetch('/api/logs', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload)
      }).catch(err => {
        console.error('Failed to send log to API:', err);
      });

      // Return value is used for debugging when consoleDebug is enabled
      return messages;
    }
  })
})

Debug Mode

typescript
import { LogLayer, BlankTransport } from 'loglayer'

const log = new LogLayer({
  transport: new BlankTransport({
    consoleDebug: true // This will also log to console for debugging
    shipToLogger: ({ logLevel, messages, data, hasData }) => {
      // Your custom logic here
      const payload = {
        level: logLevel,
        message: messages.join(" "),
        ...(data && hasData ? data : {})
      };

      // Send to external service
      fetch('/api/logs', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload)
      });

      // Return value is used for debugging when consoleDebug is enabled
      return messages;
    },
  })
})