mr-log
v0.1.1
Published
Type-safe client for sending project logs to the MR Logs service.
Readme
mr-log
mr-log is a type-safe client for sending application logs to the MR Logs service.
It sets the log severity from the method you call, so consumers cannot send a mismatched type value. Configure a project ID and application ID once; then send Fatal, Error, Warning, or Success logs with the event details and the user who caused the event.
Install
After the package has been published to your npm registry:
npm i mr-logFor local development in this workspace:
npm i ../mr-logmr-log requires Node.js 18 or newer because it uses the built-in fetch API.
Quick Start
Configure the logger once when your application starts. The package uses the public ingestion gateway internally, so neither the gateway nor the private backend address belongs in application configuration. Copy the project ID and application ID from MR Logs, then pass actor with each log because the person or service performing the action can change during the lifetime of an application.
import { log, LogEnvironment } from 'mr-log';
log.configure({
projectId: 12,
applicationId: 34,
environment: LogEnvironment.NonProd,
});Write logs anywhere after configuration:
await log.Success({
actor: 'S9107544',
message: 'Inventory import completed',
location: '/api/imports/inventory',
action: 'POST',
});Severity Methods
Each method chooses the only allowed severity for its log record. Do not pass a type field; the package sets it for you.
LogType is exported as an immutable runtime object and as a TypeScript union derived from that object. Use the same types in application contracts instead of maintaining separate message-type constants:
import { LogType, isLogType } from 'mr-log';
const severity: LogType = LogType.Error;The named values are LogType.Fatal, LogType.Error, LogType.Warning, and LogType.Success.
isLogType(value) validates unknown data and narrows it to LogType. Only fatal, error, warning, and success are accepted. Legacy values such as Failure are not log types. This does not change severity methods: log.Success(details) still sets the severity itself.
import { log } from 'mr-log';
await log.Fatal({
actor: 'S9107544',
message: 'The inventory database is unavailable',
location: '/api/inventory/sync',
action: 'POST',
});
await log.Error({
actor: 'S9107544',
message: 'Unable to save stock adjustment',
location: '/api/inventory/adjustments/42',
action: 'PATCH',
});
await log.Warning({
actor: 'S9107544',
message: 'Supplier response exceeded the expected duration',
location: '/api/suppliers',
action: 'GET',
});
await log.Success({
actor: 'S9107544',
message: 'Stock adjustment saved',
location: '/api/inventory/adjustments/42',
action: 'PATCH',
});These calls create the following values for the Logs table:
| Table field | Source |
| --- | --- |
| project_id | projectId from log.configure() |
| service_domain_id | applicationId from log.configure() |
| application | Current title of the application ID |
| actor | Required actor supplied to the severity method |
| occurred_at | Current UTC time, or occurredAt supplied to the method |
| message | Required message supplied to the method |
| type | Determined by Fatal, Error, Warning, or Success |
| location | Optional location supplied to the method; it can be a route or file path. |
| action | Optional typed action supplied to the method |
Configuration
import { log, LogEnvironment } from 'mr-log';
log.configure({
projectId: 12,
applicationId: 34,
environment: LogEnvironment.Prod,
timeoutMs: 3_000,
});| Option | Required | Description |
| --- | --- | --- |
| projectId | Yes | Positive integer ID of the destination project. |
| applicationId | Yes | Positive integer ID of the application in that project. |
| environment | Yes | Service environment: LogEnvironment.NonProd, LogEnvironment.PreProd, or LogEnvironment.Prod. |
| timeoutMs | No | Request timeout in milliseconds. Defaults to 5000. |
LogEnvironment is exported for both TypeScript and JavaScript consumers, with values nonprod, preprod, and prod. Missing or unsupported environments are rejected at configuration time. The environment is currently stored in client configuration only; it does not change the ingestion URL or add a field to the log payload.
Log Details
Every severity method accepts the same type-safe object:
await log.Warning({
actor: 'S9107544',
message: 'The source system was slow',
location: '/api/suppliers',
action: 'GET',
occurredAt: new Date('2026-08-31T10:15:00.000Z'),
});| Field | Required | Description |
| --- | --- | --- |
| actor | Yes | Person or service that performed the action. Maximum 120 characters. |
| message | Yes | Log message. |
| location | No | Related request route or file path. Maximum 2048 characters. |
| action | No | One of GET, POST, PUT, PATCH, DELETE, HEAD, or OPTIONS. |
| occurredAt | No | Date or parseable ISO date string. Defaults to the current UTC time. |
TypeScript prevents unsupported action values and does not expose a type field in the details object:
await log.Error({
actor: 'S9107544',
message: 'Invalid payload',
action: 'POST',
// type: 'success', // TypeScript error: the package controls log type.
});Error Handling
Logging methods return a Promise<void>. Await a log when the caller needs to detect delivery failures; use void when logging must not interrupt the calling workflow.
import { log, MrLogConfigurationError, MrLogRequestError } from 'mr-log';
try {
await log.Error({
actor: 'S9107544',
message: 'Could not retrieve inventory items',
location: '/api/inventory/items',
action: 'GET',
});
} catch (error) {
if (error instanceof MrLogConfigurationError) {
// The logger was not configured or received invalid data.
} else if (error instanceof MrLogRequestError) {
// The Logs service returned a non-2xx response.
console.error(error.status);
}
}For non-blocking logging:
void log.Success({
actor: 'S9107544',
message: 'Background task completed',
}).catch(() => undefined);Multiple Configurations
The exported log value is a singleton. For isolated configurations, such as a multi-tenant worker or a test, create individual loggers:
import { createLogger, LogEnvironment } from 'mr-log';
const partnerLogger = createLogger();
partnerLogger.configure({
projectId: 27,
applicationId: 45,
environment: LogEnvironment.PreProd,
});
await partnerLogger.Success({
actor: 'PARTNER-WORKER',
message: 'Partner import completed',
});Project Examples
Create one logger for each service domain. Each logger keeps its own project and application IDs, while actor remains specific to the action being logged.
import { createLogger, LogEnvironment } from 'mr-log';
const inventoryLogger = createLogger();
inventoryLogger.configure({
projectId: 12,
applicationId: 34,
environment: LogEnvironment.Prod,
});
await inventoryLogger.Success({
actor: 'S9107544',
message: 'Inventory import completed',
location: '/api/imports/inventory',
action: 'POST',
});
const ordersLogger = createLogger();
ordersLogger.configure({
projectId: 27,
applicationId: 56,
environment: LogEnvironment.Prod,
});
await ordersLogger.Error({
actor: 'S1234567',
message: 'Unable to create order',
location: '/api/orders',
action: 'POST',
});Browser Use
Use mr-log from a trusted backend service. Browser requests can reveal the public ingestion route and should be sent through your own backend instead.
