Reforge provides integrations with popular Node.js logging frameworks to enable dynamic log level control.
- Dynamic Log Levels: Change log levels in real-time without restarting your application
- Optional Dependencies: Logger integrations are optional peer dependencies
- Flexible Version Support: Compatible with pino >=7.0.0 and winston >=3.0.0
Install Reforge along with your preferred logging framework:
# With Pino
npm install @reforge-com/node pino
# With Winston
npm install @reforge-com/node winstonimport { Reforge } from '@reforge-com/node';
import { createPinoLogger } from '@reforge-com/node/integrations/pino';
const reforge = new Reforge({
sdkKey: process.env.REFORGE_SDK_KEY,
loggerKey: 'my.log.config', // Config key for LOG_LEVEL_V2 (defaults to 'log-levels.default')
});
await reforge.init();
// Create a logger that dynamically gets its level from Reforge
const logger = await createPinoLogger(reforge, 'my.app.component', {
// Optional: any pino options
transport: { target: 'pino-pretty' }
});
if (logger) {
logger.info('Application started');
logger.debug('Debug information'); // Only logs if level permits
}If you already have a Pino logger, you can use the createPinoHook to add Reforge log level information:
import pino from 'pino';
import { createPinoHook } from '@reforge-com/node/integrations/pino';
const logger = pino({
mixin: createPinoHook(reforge, 'my.app.component')
});
// Log entries will include reforgeLogLevel field
logger.info('test'); // { reforgeLogLevel: 'info', msg: 'test', ... }import { Reforge } from '@reforge-com/node';
import { createWinstonLogger } from '@reforge-com/node/integrations/winston';
const reforge = new Reforge({
sdkKey: process.env.REFORGE_SDK_KEY,
loggerKey: 'my.log.config',
});
await reforge.init();
// Create a logger that dynamically gets its level from Reforge
const logger = await createWinstonLogger(reforge, 'my.app.component', {
// Optional: any winston options
transports: [new winston.transports.Console()]
});
if (logger) {
logger.info('Application started');
logger.debug('Debug information'); // Only logs if level permits
}import winston from 'winston';
import { createWinstonFormat } from '@reforge-com/node/integrations/winston';
const logger = winston.createLogger({
format: winston.format.combine(
await createWinstonFormat(reforge, 'my.app.component'),
winston.format.json()
),
transports: [new winston.transports.Console()]
});
// Log entries will include reforgeLogLevel field
logger.info('test'); // { reforgeLogLevel: 'info', message: 'test', ... }- In the Reforge UI, create a new config with type
LOG_LEVEL_V2 - Set your logger key (e.g.,
my.log.config) - Add rules based on the
reforge-sdk-logging.logger-pathcontext property
// Rule 1: DEBUG for specific component
{
criteria: [
{
propertyName: "reforge-sdk-logging.logger-path",
operator: "PROP_IS_ONE_OF",
valueToMatch: { stringList: { values: ["my.app.auth"] } }
}
],
value: { logLevel: "DEBUG" }
}
// Rule 2: INFO as default
{
criteria: [],
value: { logLevel: "INFO" }
}You can also use getLogLevel() directly with any logging framework:
import { Reforge, LogLevel } from '@reforge-com/node';
const reforge = new Reforge({
sdkKey: process.env.REFORGE_SDK_KEY,
loggerKey: 'my.log.config',
});
await reforge.init();
// Get the log level for a specific logger
const level = reforge.getLogLevel('my.app.component');
// Map to your logger's level system
if (level === LogLevel.Debug) {
myLogger.level = 'debug';
} else if (level === LogLevel.Info) {
myLogger.level = 'info';
}
// ... etcTRACE→traceDEBUG→debugINFO→infoWARN→warnERROR→errorFATAL→fatal
TRACE→debug(Winston doesn't have trace)DEBUG→debugINFO→infoWARN→warnERROR→errorFATAL→error(Winston doesn't have fatal)
-
No Hierarchy Traversal: Unlike the previous LOG_LEVEL implementation, LOG_LEVEL_V2 does NOT traverse logger name hierarchies. Each logger name is evaluated independently.
-
Default Value:
- The default
loggerKeyis"log-levels.default" - If no config is found with that key,
getLogLevel()returnsDEBUG
- The default
-
Dynamic Updates: Log levels update automatically when your Reforge config changes (via SSE or polling).
-
Context Evaluation: The integrations pass the logger name in the context as:
{ "reforge-sdk-logging": { "lang": "javascript", "logger-path": loggerName } }
If pino or winston is not installed, the integration functions will return undefined and log a warning to the console. Your application will continue to work, but dynamic log levels won't be available.
const logger = await createPinoLogger(reforge, 'my.app');
if (!logger) {
console.warn('Pino not available, falling back to console');
// Use console or another fallback
}