@sillowww/lily
v0.2.0
Published
a small logging library for javascript.
Downloads
6
Readme
lily
a small logging library for javascript.
installation
your local package manager's method of installation.
why lily?
it has a pretty name.
lily is simple, designed for my very simple mind and has, i think? everything a simpler project would need.
all the other logging libs seemed very old and complex to me (i just didn't want to read the docs actually but same thing).
quick start
import logger from "lily";
logger.info("hello world");
logger.error("something went wrong", { userId: 123 });
// create child loggers with scope
const dbLogger = logger.child("database");
dbLogger.info("connected to postgres");
const userLogger = dbLogger.child("users");
userLogger.debug("user query executed", { query: "SELECT * FROM users" });core concepts
transports
transports are where your logs go. console, files, http endpoints, databases -
anywhere you want, lily supports a ConsoleTransport and FileTransport out of
the box, but you can create your own.
import { Logger, ConsoleTransport, FileTransport } from "lily";
const logger = new Logger("myapp");
logger.addTransport(new ConsoleTransport());
logger.addTransport(new FileTransport({ filename: "app.log" }));
// this goes to both console and file
logger.info("user logged in");formatters
formatters control how your logs look. pretty colours for dev, & structured json for prod.
import { ConsoleFormatter, JsonFormatter } from "lily";
// pretty colours for humans
const prettyFormatter = new ConsoleFormatter({
colourize: true,
timeFormat: "locale",
});
// structured data for machines
const jsonFormatter = new JsonFormatter();scopes
scopes give your logs context, they're like breadcrumbs through your application.
const apiLogger = logger.child("api");
const authLogger = apiLogger.child("auth");
// scope: [app/api/auth]
authLogger.info("login attempt", { email: "[email protected]" });log levels
logger.trace("detailed debugging info");
logger.debug("debug information");
logger.info("general information");
logger.warn("warning messages");
logger.error("error messages");
logger.fatal("critical errors");set log level globally:
import { Logger, LogLevel } from "lily";
Logger.setGlobalLevel(LogLevel.WARN); // only warnings and aboveor via environment:
LOG_LEVEL=DEBUG node app.jscustom transports
creating a custom transport is simple - just impl the Transport interface and
wow your logs are going to space or idk wherever you want them to go.
import { Transport, LogEntry } from "lily";
class DiscordTransport implements Transport {
constructor(private webhookUrl: string) {}
async log(entry: LogEntry): Promise<void> {
if (entry.level < LogLevel.ERROR) return; // errors only
await fetch(this.webhookUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
content: `(${entry.scope.join("/")}) ${entry.message}`,
}),
});
}
}
class DiscordTransport implements Transport {
constructor(private webhookUrl: string) {}
async log(entry: LogEntry): Promise<void> {
if (entry.level < LogLevel.ERROR) return; // errors only
await fetch(this.webhookUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
content: `[${entry.scope.join("/")}] ${entry.message}`,
}),
});
}
}
logger.addTransport(
new DiscordTransport("https://discord.com/api/webhooks/..."),
);custom formatters
formatters convert LogEntry objects to strings:
import { LogEntry, LogLevel } from "lily";
class SimpleFormatter {
format(entry: LogEntry): string {
const level = LogLevel[entry.level];
const scope = entry.scope.length > 0 ? `[${entry.scope.join("/")}] ` : "";
return `${level}: ${scope}${entry.message}`;
}
}
// use it
const transport = new ConsoleTransport(new SimpleFormatter());
logger.addTransport(transport);custom logger setup
build your perfect logger:
import {
Logger,
LogLevel,
ConsoleTransport,
FileTransport,
ConsoleFormatter,
JsonFormatter,
} from "lily";
const logger = new Logger("myapp", {
level: LogLevel.DEBUG,
colourize: process.env.NODE_ENV !== "production",
});
// development: pretty console output
if (process.env.NODE_ENV === "development") {
logger.addTransport(
new ConsoleTransport(
new ConsoleFormatter({
colourize: true,
timeFormat: "time",
}),
),
);
}
// production: structured file logs
if (process.env.NODE_ENV === "production") {
logger.addTransport(
new FileTransport({
filename: "app.log",
formatter: new JsonFormatter(),
}),
);
}
export default logger;environment variables
LOG_LEVEL- set log level (TRACE, DEBUG, INFO, WARN, ERROR, FATAL)NO_COLOUR- disable colours in outputNODE_ENV=test- automatically disables colours
metadata and context
add structured data to your logs:
// add metadata to individual logs
logger.info("user action", {
userId: 123,
action: "login",
ip: "192.168.1.1",
});
// create logger with persistent metadata
const requestLogger = logger.withMetadata({
requestId: "req-abc123",
userId: 456,
});
requestLogger.info("processing request"); // includes metadata
requestLogger.error("request failed"); // includes metadataapi reference
logger methods
logger.trace(message: string, ...args: unknown[])
logger.debug(message: string, ...args: unknown[])
logger.info(message: string, ...args: unknown[])
logger.warn(message: string, ...args: unknown[])
logger.error(message: string, ...args: unknown[])
logger.fatal(message: string, ...args: unknown[])
logger.child(scope: string | string[], options?: LoggerOptions): Logger
logger.withMetadata(metadata: Record<string, unknown>): Logger
logger.addTransport(transport: Transport): void
logger.removeTransport(transport: Transport): void
logger.clearTransports(): voidstatic methods
Logger.setGlobalLevel(level: LogLevel): void
Logger.getGlobalLevel(): LogLevelexamples
basic file logging
import { Logger, FileTransport, JsonFormatter } from "lily";
const logger = new Logger("app");
logger.addTransport(
new FileTransport({
filename: "application.log",
formatter: new JsonFormatter(),
}),
);
logger.info("application started");multiple transports
import {
Logger,
ConsoleTransport,
FileTransport,
ConsoleFormatter,
JsonFormatter,
} from "lily";
const logger = new Logger("api");
// pretty console output
logger.addTransport(
new ConsoleTransport(new ConsoleFormatter({ colourize: true })),
);
// structured file output
logger.addTransport(
new FileTransport({
filename: "api.log",
formatter: new JsonFormatter(),
}),
);
logger.info("server starting on port 3000");request logging middleware
import { logger } from "./logger";
app.use((req, res, next) => {
const requestLogger = logger.child("http").withMetadata({
requestId: crypto.randomUUID(),
method: req.method,
url: req.url,
});
requestLogger.info("request started");
res.on("finish", () => {
requestLogger.info("request completed", {
statusCode: res.statusCode,
});
});
req.logger = requestLogger;
next();
});license
gpl-3.0 - see [LICENSE.GPL-3.0] for details.
