Creating Transports
To integrate a logging library with LogLayer, you must create a transport. A transport is a class that translates LogLayer's standardized logging format into the format expected by your target logging library or service.
Installation
To implement a transport, you must install the @loglayer/transport
package.
npm install @loglayer/transport
yarn add @loglayer/transport
pnpm add @loglayer/transport
Implementing a Transport
The key requirement for any transport is extending the BaseTransport
or LoggerlessTransport
class and implementing the shipToLogger
method.
This method is called by LogLayer whenever a log needs to be sent, and it's where you transform LogLayer's standardized format into the format your target library or service expects.
Types of Transports
LogLayer supports two types of transports:
Logger-Based Transports
For libraries that follow a common logging interface with methods like info()
, warn()
, error()
, debug()
, etc., extend the BaseTransport
class.
The BaseTransport
class provides a logger
property where users pass in their logging library instance:
import { BaseTransport, LogLayerTransportParams } from "@loglayer/transport";
export class CustomLoggerTransport extends BaseTransport<YourLoggerType> {
shipToLogger({ logLevel, messages, data, hasData }: LogLayerTransportParams) {
if (data && hasData) {
// Most logging libraries expect data as first or last parameter
messages.unshift(data); // or messages.push(data);
}
switch (logLevel) {
case LogLevel.info:
this.logger.info(...messages);
break;
case LogLevel.warn:
this.logger.warn(...messages);
break;
case LogLevel.error:
this.logger.error(...messages);
break;
// ... handle other log levels
}
return messages;
}
}
To use this transport, you must provide a logger instance when creating it:
import { LogLayer } from 'loglayer';
import { YourLogger } from 'your-logger-library';
// Initialize your logging library
const loggerInstance = new YourLogger();
// Create LogLayer instance with the transport
const log = new LogLayer({
transport: new CustomLoggerTransport({
logger: loggerInstance // Required: the logger instance is passed here
})
});
Loggerless Transports
For services or libraries that don't follow the common logging interface (e.g., analytics services, monitoring tools), extend the LoggerlessTransport
class.
Unlike BaseTransport
, LoggerlessTransport
doesn't provide a logger
property since these services typically don't require a logger instance. Instead, you'll usually initialize your service in the constructor:
import { LoggerlessTransport, LogLayerTransportParams } from "@loglayer/transport";
export class CustomServiceTransport extends LoggerlessTransport {
private service: YourServiceType;
constructor(config: YourConfigType) {
super(config);
this.service = new YourServiceType(config);
}
shipToLogger({ logLevel, messages, data, hasData }: LogLayerTransportParams) {
const payload = {
level: logLevel,
message: messages.join(" "),
timestamp: new Date().toISOString(),
...(data && hasData ? data : {})
};
// Send to your service
this.service.send(payload);
return messages;
}
}
To use this transport, you only need to provide the configuration for your service:
import { LogLayer } from 'loglayer';
// Create LogLayer instance with the transport
const log = new LogLayer({
transport: new CustomServiceTransport({
// No logger property needed, just your service configuration
apiKey: 'your-api-key',
endpoint: 'https://api.yourservice.com/logs'
})
});
shipToLogger
Parameters
LogLayer calls the shipToLogger
method of a transport at the end of log processing to send the log to the target logging library.
It receives a LogLayerTransportParams
object with these fields:
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;
}
For example, if a user does the following:
logger.withMetadata({foo: 'bar'}).info('hello world', 'foo');
The parameters passed to shipToLogger
would be:
{
logLevel: LogLevel.info,
messages: ['hello world', 'foo'],
data: {foo: 'bar'},
hasData: true
}
Examples
Logger-Based Example: Console Transport
import { BaseTransport, LogLevel, LogLayerTransportParams } from "@loglayer/transport";
export class ConsoleTransport extends BaseTransport<ConsoleType> {
shipToLogger({ logLevel, messages, data, hasData }: LogLayerTransportParams) {
if (data && hasData) {
// put object data as the first parameter
messages.unshift(data);
}
switch (logLevel) {
case LogLevel.info:
this.logger.info(...messages);
break;
case LogLevel.warn:
this.logger.warn(...messages);
break;
case LogLevel.error:
this.logger.error(...messages);
break;
case LogLevel.trace:
this.logger.trace(...messages);
break;
case LogLevel.debug:
this.logger.debug(...messages);
break;
case LogLevel.fatal:
this.logger.error(...messages);
break;
}
return messages;
}
}
Loggerless Example: DataDog Transport
For an example of a loggerless transport that sends logs to a third-party service, see the Datadog Transport implementation.