Skip to main content

Custom Logging

The feature described in this guide is also demonstrated in the "Custom Logging" demo.

By default, the Feature Hub uses the console for all log statements. This behaviour can be customized by providing a custom logger.

In the following examples, pino is used as a custom logger:

import pino from 'pino';

const logger = pino();

@feature-hub/core

To provide a custom logger to the FeatureAppManager and FeatureServiceRegistry, the integrator can set the logger option of the createFeatureHub function:

import {createFeatureHub} from '@feature-hub/core';
const {featureAppManager} = createFeatureHub('acme:integrator', {logger});

@feature-hub/react

To provide a custom logger to the React FeatureAppLoader and React FeatureAppContainer, the integrator can use the FeatureHubContextProvider:

import {FeatureHubContextProvider} from '@feature-hub/react';
<FeatureHubContextProvider value={{featureAppManager, logger}}>
{/* render Feature Apps here */}
</FeatureHubContextProvider>

@feature-hub/dom

To provide a custom logger to the feature-app-loader and feature-app-container custom elements, the integrator can set the logger option of the defineFeatureAppContainer and defineFeatureAppLoader functions:

import {
defineFeatureAppContainer,
defineFeatureAppLoader,
} from '@feature-hub/dom';
defineFeatureAppContainer(featureAppManager, {logger});
defineFeatureAppLoader(featureAppManager, {logger});

Feature Apps & Feature Services

To provide a custom logger to Feature Apps and Feature Services, the integrator can provide the Logger Feature Service from the @feature-hub/logger package. It must be defined with a function that takes the consumerId and optional consumerName of the consuming Feature App or Feature Service, and returns a Logger instance. This makes it possible to augment the consumer logs with the consumerId and/or consumerName, e.g. using pino's child logger:

import {defineLogger} from '@feature-hub/logger';
const {featureAppManager} = createFeatureHub('acme:integrator', {
logger,
featureServiceDefinitions: [
defineLogger((consumerId, consumerName) =>
logger.child({consumerId, consumerName}),
),
],
});

A Feature App could then use the Logger Feature Service like this:

const myFeatureAppDefinition = {
id: 'acme:my-feature-app',

dependencies: {
featureServices: {
's2:logger': '^1.0.0',
},
},

create(env) {
const logger = env.featureServices['s2:logger'];

logger.debug('foo');

// ...
},
};

Similarly, a Feature Service could use the Logger Feature Service like this:

const myFeatureServiceDefinition = {
id: 'acme:my-feature-service',

dependencies: {
featureServices: {
's2:logger': '^1.0.0',
},
},

create(env) {
const logger = env.featureServices['s2:logger'];

logger.debug('foo');

// ...
},
};

The History Service and the Async SSR Manager have the Logger Feature Service defined as an optional dependency. If the integrator has provided the Logger Service, they use it for their logging. Otherwise the console is used as a fallback.