@probitas/logger
Unified logging interface for Probitas packages.
This package provides a centralized logging system built on LogTape. It offers consistent logging across all Probitas packages with configurable log levels, pretty formatting, and hierarchical categories.
Links
- GitHub Repository
- @probitas/probitas - Main package (recommended for most users)
- LogTape - Underlying logging library
Related Packages
All Probitas packages use this logger internally: @probitas/runner, @probitas/reporter, @probitas/discover, @probitas/cli
Core Functions
configureLogging- Initialize the logging system with a log levelgetLogger- Get a logger instance for a categoryresetLogging- Reset logging configuration (for testing)
Types
Log Levels
From least to most verbose:
"fatal"- Critical errors that stop execution"error"- Errors that don't stop execution"warning"- Potential issues (default level)"info"- Informational messages about execution"debug"- Detailed debugging information
Category Hierarchy
Loggers use hierarchical categories following the package structure:
["probitas"]- Root category for all Probitas logs["probitas", "runner"]- Runner package logs["probitas", "cli", "run"]- Specific command logs
Installation
deno add jsr:@probitas/loggerInterfaces
#Logger
interface LoggerA logger interface. It provides methods to log messages at different severity levels.
const logger = getLogger("category");
logger.trace `A trace message with ${value}`
logger.debug `A debug message with ${value}.`;
logger.info `An info message with ${value}.`;
logger.warn `A warning message with ${value}.`;
logger.error `An error message with ${value}.`;
logger.fatal `A fatal error message with ${value}.`;
| Name | Description |
|---|---|
category | The category of the logger. |
parent | The logger with the supercategory of the current logger. |
getChild() | Get a child logger with the given subcategory. |
with() | Get a logger with contextual properties. |
trace() | Log a trace message. |
trace() | Log a trace message with properties. |
trace() | Log a trace values with no message. |
trace() | Lazily log a trace message. |
debug() | Log a debug message. |
debug() | Log a debug message with properties. |
debug() | Log a debug values with no message. |
debug() | Lazily log a debug message. |
info() | Log an informational message. |
info() | Log an informational message with properties. |
info() | Log an informational values with no message. |
info() | Lazily log an informational message. |
warn() | Log a warning message. |
warn() | Log a warning message with properties. |
warn() | Log a warning values with no message. |
warn() | Lazily log a warning message. |
warning() | Log a warning message. |
warning() | Log a warning message with properties. |
warning() | Log a warning values with no message. |
warning() | Lazily log a warning message. |
error() | Log an error message. |
error() | Log an error message with properties. |
error() | Log an error values with no message. |
error() | Lazily log an error message. |
fatal() | Log a fatal error message. |
fatal() | Log a fatal error message with properties. |
fatal() | Log a fatal error values with no message. |
fatal() | Lazily log a fatal error message. |
emit() | Emits a log record with custom fields while using this logger's category. |
Properties
- readonly
categoryreadonly string[]The category of the logger. It is an array of strings.
The logger with the supercategory of the current logger. If the current logger is the root logger, this is
null.
Methods
getChild(subcategory: string | readonly [string] | readonly [string, unknown]): LoggerGet a child logger with the given subcategory.
const logger = getLogger("category");
const subLogger = logger.getChild("sub-category");
The above code is equivalent to:
const logger = getLogger("category");
const subLogger = getLogger(["category", "sub-category"]);
Parameters
subcategorystring | readonly [string] | readonly [string, unknown]The subcategory.
with(properties: Record<string, unknown>): LoggerGet a logger with contextual properties. This is useful for log multiple messages with the shared set of properties.
const logger = getLogger("category");
const ctx = logger.with({ foo: 123, bar: "abc" });
ctx.info("A message with {foo} and {bar}.");
ctx.warn("Another message with {foo}, {bar}, and {baz}.", { baz: true });
The above code is equivalent to:
const logger = getLogger("category");
logger.info("A message with {foo} and {bar}.", { foo: 123, bar: "abc" });
logger.warn(
"Another message with {foo}, {bar}, and {baz}.",
{ foo: 123, bar: "abc", baz: true },
);
Parameters
propertiesRecord<string, unknown>
trace(message: TemplateStringsArray, _: readonly unknown[]): voidLog a trace message. Use this as a template string prefix.
logger.trace `A trace message with ${value}.`;
Parameters
messageTemplateStringsArrayThe message template strings array.
_readonly unknown[]
trace(message: string, properties?: Record<string, unknown> | unknown): voidLog a trace message with properties.
logger.trace('A trace message with {value}.', { value });
If the properties are expensive to compute, you can pass a callback that returns the properties:
logger.trace(
'A trace message with {value}.',
() => ({ value: expensiveComputation() })
);
Parameters
messagestringThe message template. Placeholders to be replaced with
valuesare indicated by keys in curly braces (e.g.,{value}).properties?Record<string, unknown> | unknownThe values to replace placeholders with. For lazy evaluation, this can be a callback that returns the properties.
trace(properties: Record<string, unknown>): voidLog a trace values with no message. This is useful when you want to log properties without a message, e.g., when you want to log the context of a request or an operation.
logger.trace({ method: 'GET', url: '/api/v1/resource' });
Note that this is a shorthand for:
logger.trace('{*}', { method: 'GET', url: '/api/v1/resource' });
If the properties are expensive to compute, you cannot use this shorthand and should use the following syntax instead:
logger.trace('{*}', () => ({
method: expensiveMethod(),
url: expensiveUrl(),
}));
Parameters
propertiesRecord<string, unknown>The values to log. Note that this does not take a callback.
trace(callback: LogCallback): voidLazily log a trace message. Use this when the message values are expensive to compute and should only be computed if the message is actually logged.
logger.trace(l => l`A trace message with ${expensiveValue()}.`);
Parameters
callbackLogCallbackA callback that returns the message template prefix.
debug(message: TemplateStringsArray, _: readonly unknown[]): voidLog a debug message. Use this as a template string prefix.
logger.debug `A debug message with ${value}.`;
Parameters
messageTemplateStringsArrayThe message template strings array.
_readonly unknown[]
debug(message: string, properties?: Record<string, unknown> | unknown): voidLog a debug message with properties.
logger.debug('A debug message with {value}.', { value });
If the properties are expensive to compute, you can pass a callback that returns the properties:
logger.debug(
'A debug message with {value}.',
() => ({ value: expensiveComputation() })
);
Parameters
messagestringThe message template. Placeholders to be replaced with
valuesare indicated by keys in curly braces (e.g.,{value}).properties?Record<string, unknown> | unknownThe values to replace placeholders with. For lazy evaluation, this can be a callback that returns the properties.
debug(properties: Record<string, unknown>): voidLog a debug values with no message. This is useful when you want to log properties without a message, e.g., when you want to log the context of a request or an operation.
logger.debug({ method: 'GET', url: '/api/v1/resource' });
Note that this is a shorthand for:
logger.debug('{*}', { method: 'GET', url: '/api/v1/resource' });
If the properties are expensive to compute, you cannot use this shorthand and should use the following syntax instead:
logger.debug('{*}', () => ({
method: expensiveMethod(),
url: expensiveUrl(),
}));
Parameters
propertiesRecord<string, unknown>The values to log. Note that this does not take a callback.
debug(callback: LogCallback): voidLazily log a debug message. Use this when the message values are expensive to compute and should only be computed if the message is actually logged.
logger.debug(l => l`A debug message with ${expensiveValue()}.`);
Parameters
callbackLogCallbackA callback that returns the message template prefix.
info(message: TemplateStringsArray, _: readonly unknown[]): voidLog an informational message. Use this as a template string prefix.
logger.info `An info message with ${value}.`;
Parameters
messageTemplateStringsArrayThe message template strings array.
_readonly unknown[]
info(message: string, properties?: Record<string, unknown> | unknown): voidLog an informational message with properties.
logger.info('An info message with {value}.', { value });
If the properties are expensive to compute, you can pass a callback that returns the properties:
logger.info(
'An info message with {value}.',
() => ({ value: expensiveComputation() })
);
Parameters
messagestringThe message template. Placeholders to be replaced with
valuesare indicated by keys in curly braces (e.g.,{value}).properties?Record<string, unknown> | unknownThe values to replace placeholders with. For lazy evaluation, this can be a callback that returns the properties.
info(properties: Record<string, unknown>): voidLog an informational values with no message. This is useful when you want to log properties without a message, e.g., when you want to log the context of a request or an operation.
logger.info({ method: 'GET', url: '/api/v1/resource' });
Note that this is a shorthand for:
logger.info('{*}', { method: 'GET', url: '/api/v1/resource' });
If the properties are expensive to compute, you cannot use this shorthand and should use the following syntax instead:
logger.info('{*}', () => ({
method: expensiveMethod(),
url: expensiveUrl(),
}));
Parameters
propertiesRecord<string, unknown>The values to log. Note that this does not take a callback.
info(callback: LogCallback): voidLazily log an informational message. Use this when the message values are expensive to compute and should only be computed if the message is actually logged.
logger.info(l => l`An info message with ${expensiveValue()}.`);
Parameters
callbackLogCallbackA callback that returns the message template prefix.
warn(message: TemplateStringsArray, _: readonly unknown[]): voidLog a warning message. Use this as a template string prefix.
logger.warn `A warning message with ${value}.`;
Parameters
messageTemplateStringsArrayThe message template strings array.
_readonly unknown[]
warn(message: string, properties?: Record<string, unknown> | unknown): voidLog a warning message with properties.
logger.warn('A warning message with {value}.', { value });
If the properties are expensive to compute, you can pass a callback that returns the properties:
logger.warn(
'A warning message with {value}.',
() => ({ value: expensiveComputation() })
);
Parameters
messagestringThe message template. Placeholders to be replaced with
valuesare indicated by keys in curly braces (e.g.,{value}).properties?Record<string, unknown> | unknownThe values to replace placeholders with. For lazy evaluation, this can be a callback that returns the properties.
warn(properties: Record<string, unknown>): voidLog a warning values with no message. This is useful when you want to log properties without a message, e.g., when you want to log the context of a request or an operation.
logger.warn({ method: 'GET', url: '/api/v1/resource' });
Note that this is a shorthand for:
logger.warn('{*}', { method: 'GET', url: '/api/v1/resource' });
If the properties are expensive to compute, you cannot use this shorthand and should use the following syntax instead:
logger.warn('{*}', () => ({
method: expensiveMethod(),
url: expensiveUrl(),
}));
Parameters
propertiesRecord<string, unknown>The values to log. Note that this does not take a callback.
warn(callback: LogCallback): voidLazily log a warning message. Use this when the message values are expensive to compute and should only be computed if the message is actually logged.
logger.warn(l => l`A warning message with ${expensiveValue()}.`);
Parameters
callbackLogCallbackA callback that returns the message template prefix.
warning(message: TemplateStringsArray, _: readonly unknown[]): voidLog a warning message. Use this as a template string prefix.
logger.warning `A warning message with ${value}.`;
Parameters
messageTemplateStringsArrayThe message template strings array.
_readonly unknown[]
warning(message: string, properties?: Record<string, unknown> | unknown): voidLog a warning message with properties.
logger.warning('A warning message with {value}.', { value });
If the properties are expensive to compute, you can pass a callback that returns the properties:
logger.warning(
'A warning message with {value}.',
() => ({ value: expensiveComputation() })
);
Parameters
messagestringThe message template. Placeholders to be replaced with
valuesare indicated by keys in curly braces (e.g.,{value}).properties?Record<string, unknown> | unknownThe values to replace placeholders with. For lazy evaluation, this can be a callback that returns the properties.
warning(properties: Record<string, unknown>): voidLog a warning values with no message. This is useful when you want to log properties without a message, e.g., when you want to log the context of a request or an operation.
logger.warning({ method: 'GET', url: '/api/v1/resource' });
Note that this is a shorthand for:
logger.warning('{*}', { method: 'GET', url: '/api/v1/resource' });
If the properties are expensive to compute, you cannot use this shorthand and should use the following syntax instead:
logger.warning('{*}', () => ({
method: expensiveMethod(),
url: expensiveUrl(),
}));
Parameters
propertiesRecord<string, unknown>The values to log. Note that this does not take a callback.
warning(callback: LogCallback): voidLazily log a warning message. Use this when the message values are expensive to compute and should only be computed if the message is actually logged.
logger.warning(l => l`A warning message with ${expensiveValue()}.`);
Parameters
callbackLogCallbackA callback that returns the message template prefix.
error(message: TemplateStringsArray, _: readonly unknown[]): voidLog an error message. Use this as a template string prefix.
logger.error `An error message with ${value}.`;
Parameters
messageTemplateStringsArrayThe message template strings array.
_readonly unknown[]
error(message: string, properties?: Record<string, unknown> | unknown): voidLog an error message with properties.
logger.warn('An error message with {value}.', { value });
If the properties are expensive to compute, you can pass a callback that returns the properties:
logger.error(
'An error message with {value}.',
() => ({ value: expensiveComputation() })
);
Parameters
messagestringThe message template. Placeholders to be replaced with
valuesare indicated by keys in curly braces (e.g.,{value}).properties?Record<string, unknown> | unknownThe values to replace placeholders with. For lazy evaluation, this can be a callback that returns the properties.
error(properties: Record<string, unknown>): voidLog an error values with no message. This is useful when you want to log properties without a message, e.g., when you want to log the context of a request or an operation.
logger.error({ method: 'GET', url: '/api/v1/resource' });
Note that this is a shorthand for:
logger.error('{*}', { method: 'GET', url: '/api/v1/resource' });
If the properties are expensive to compute, you cannot use this shorthand and should use the following syntax instead:
logger.error('{*}', () => ({
method: expensiveMethod(),
url: expensiveUrl(),
}));
Parameters
propertiesRecord<string, unknown>The values to log. Note that this does not take a callback.
error(callback: LogCallback): voidLazily log an error message. Use this when the message values are expensive to compute and should only be computed if the message is actually logged.
logger.error(l => l`An error message with ${expensiveValue()}.`);
Parameters
callbackLogCallbackA callback that returns the message template prefix.
fatal(message: TemplateStringsArray, _: readonly unknown[]): voidLog a fatal error message. Use this as a template string prefix.
logger.fatal `A fatal error message with ${value}.`;
Parameters
messageTemplateStringsArrayThe message template strings array.
_readonly unknown[]
fatal(message: string, properties?: Record<string, unknown> | unknown): voidLog a fatal error message with properties.
logger.warn('A fatal error message with {value}.', { value });
If the properties are expensive to compute, you can pass a callback that returns the properties:
logger.fatal(
'A fatal error message with {value}.',
() => ({ value: expensiveComputation() })
);
Parameters
messagestringThe message template. Placeholders to be replaced with
valuesare indicated by keys in curly braces (e.g.,{value}).properties?Record<string, unknown> | unknownThe values to replace placeholders with. For lazy evaluation, this can be a callback that returns the properties.
fatal(properties: Record<string, unknown>): voidLog a fatal error values with no message. This is useful when you want to log properties without a message, e.g., when you want to log the context of a request or an operation.
logger.fatal({ method: 'GET', url: '/api/v1/resource' });
Note that this is a shorthand for:
logger.fatal('{*}', { method: 'GET', url: '/api/v1/resource' });
If the properties are expensive to compute, you cannot use this shorthand and should use the following syntax instead:
logger.fatal('{*}', () => ({
method: expensiveMethod(),
url: expensiveUrl(),
}));
Parameters
propertiesRecord<string, unknown>The values to log. Note that this does not take a callback.
fatal(callback: LogCallback): voidLazily log a fatal error message. Use this when the message values are expensive to compute and should only be computed if the message is actually logged.
logger.fatal(l => l`A fatal error message with ${expensiveValue()}.`);
Parameters
callbackLogCallbackA callback that returns the message template prefix.
emit(record: Omit<LogRecord, "category">): voidEmits a log record with custom fields while using this logger's category.
This is a low-level API for integration scenarios where you need full control over the log record, particularly for preserving timestamps from external systems.
const logger = getLogger(["my-app", "integration"]);
// Emit a log with a custom timestamp
logger.emit({
timestamp: kafkaLog.originalTimestamp,
level: "info",
message: [kafkaLog.message],
rawMessage: kafkaLog.message,
properties: {
source: "kafka",
partition: kafkaLog.partition,
offset: kafkaLog.offset,
},
});
Parameters
recordOmit<LogRecord, "category">Log record without category field (category comes from the logger instance)
Functions
#configureLogging
async function configureLogging(_: unknown): Promise<void>Configure the logging system with the specified log level.
Initializes LogTape with pretty-printed console output. Can be called multiple times to change the log level during execution.
Parameters
_unknown
Examples
Basic configuration
import { configureLogging } from "@probitas/logger";
// Enable debug logging for troubleshooting
await configureLogging("debug");
In test setup
// Suppress logs during tests
await configureLogging("fatal");
// ... run tests ...
await resetLogging();
#getLogger
function getLogger(_: string[]): LoggerGet a logger instance for the specified category.
Categories follow a hierarchical structure matching the package organization. Child loggers inherit configuration from their parents.
Parameters
_string[]
Returns
Logger — Logger instance with `debug`, `info`, `warning`, `error`, `fatal` methods
Examples
Basic usage
import { getLogger } from "@probitas/logger";
const logger = getLogger("probitas", "cli", "run");
logger.info("Starting run command");
logger.debug("Processing options", { maxConcurrency: 4 });
logger.error("Failed to load scenario", { file, error });
In a module
// At module top level
const logger = getLogger("probitas", "runner");
export function runScenario(scenario: ScenarioDefinition) {
logger.info("Running scenario", { name: scenario.name });
// ...
}
#resetLogging
async function resetLogging(): Promise<void>Reset the logging configuration to its initial state.
Clears all configured sinks and loggers. Primarily used in tests to ensure clean state between test cases.
Examples
Test cleanup
import { configureLogging, resetLogging } from "@probitas/logger";
Deno.test("my test", async () => {
await configureLogging("fatal"); // Suppress logs during test
// ... test code ...
await resetLogging(); // Clean up
});
