my-logs
Logging Standards
When writing or reviewing logging code, follow these principles.
Library
- TypeScript/Node.js: use
pino
Log Format
Show only three fields in the visible line: time, module, and message.
HH:mm:ss MODULE_NAME message text here
- Time: hours, minutes, seconds only — no milliseconds
- Module: fixed-width string (pad shorter names with spaces) so messages align vertically
- Message: human-readable text
Pino Configuration Example
import pino from "pino";
const MODULE_WIDTH = 12;
function createLogger(module: string) {
return pino({
transport: {
target: "pino-pretty",
options: {
ignore: "pid,hostname",
translateTime: "HH:MM:ss",
messageFormat: `${module.padEnd(MODULE_WIDTH)} {msg}`,
customColors: getModuleColor(module),
},
},
});
}
Module Colors
Each module gets a deterministic color derived from its name via MurmurHash3. No manual color assignment — the hash picks from a fixed palette automatically. Results are cached so each module hashes only once.
// ANSI 256-color palette — 28 visually distinct colors
const COLORS = [
196, // red
202, // orange
208, // dark orange
214, // orange-gold
220, // gold
226, // yellow
190, // yellow-green
154, // chartreuse
118, // bright green
82, // green
48, // spring green
50, // teal
51, // cyan
45, // turquoise
39, // deep sky blue
33, // dodger blue
27, // blue
21, // deep blue
57, // blue-violet
93, // purple
129, // medium purple
165, // magenta
201, // hot pink
213, // pink
177, // violet
141, // light purple
105, // slate blue
69, // cornflower blue
] as const;
const colorCache = new Map<string, string>();
function murmurhash3(key: string, seed = 0): number {
let h = seed ^ key.length;
for (let i = 0; i < key.length; i++) {
h ^= key.charCodeAt(i);
h = Math.imul(h ^ (h >>> 16), 0x85ebca6b);
h = Math.imul(h ^ (h >>> 13), 0xc2b2ae35);
h ^= h >>> 16;
}
return h >>> 0;
}
function getModuleColor(module: string): number {
let color = colorCache.get(module);
if (!color) {
color = `\x1b[38;5;${COLORS[murmurhash3(module) % COLORS.length]}m`;
colorCache.set(module, color);
}
return color;
}
Self-Contained Messages
Every log message must be understandable on its own — assume only the text part is visible (no structured fields are shown). Include all relevant identifiers directly in the message string.
Good — all IDs are in the text:
log.info(`order:create orderId=${orderId} userId=${userId} items=${items.length}`);
log.error(`payment:charge orderId=${orderId} stripeId=${stripeId} failed amount=${amount}`);
log.info(`user:login userId=${userId} email=${email} method=oauth`);
Bad — IDs hidden in structured fields that won't appear in the console:
log.info({ orderId, userId }, "order created");
log.error({ orderId, stripeId }, "payment failed");
Prefix Notation
Structure messages with prefix notation: place the most common/general word on the left, narrowing to the specific action on the right. This groups related messages visually when scanning logs.
domain:action detail
Good — common prefix groups related lines together:
order:create orderId=abc-123 userId=u-456 items=3
order:pay orderId=abc-123 amount=59.99 method=card
order:ship orderId=abc-123 carrier=ups tracking=1Z999
order:cancel orderId=abc-123 reason=user-request
user:login userId=u-456 email=j@example.com method=oauth
user:logout userId=u-456 sessionDuration=3600s
user:update userId=u-456 field=email
cache:hit key=user:u-456 ttl=120s
cache:miss key=product:p-789
cache:evict key=session:s-012 reason=expired
Bad — specific word first scatters related entries:
createOrder ...
payOrder ...
loginUser ...
shipOrder ...
Summary
- Use
pinofor TypeScript - Display only
HH:mm:ss, fixed-width module, and message - Assign distinct colors per module
- Put all IDs in the message text — assume no structured fields are visible
- Use
domain:actionprefix notation with the general word first