girok is a lightweight and highly customizable logger library.
Using npm:
npm install girok
Using bun:
bun add girok
import { logger } from "girok"
import { formatText } from "girok/formatters"
let log = logger({
level: "info",
formatter: formatText("[%lvl%] %ctx% %args%"),
outputs: ["stdout", "file://./logs"],
})
log.info("Hello World!") // [INFO] Hello World!
log = log.context({ key: "value" })
log.info("Hello again!") // [INFO] key="value" Hello again!
To instanciate a logger you can use the logger
function:
import { logger } from "girok"
const log = logger()
log.info("Hello World!")
The level
option allows you to set the minimum level that will be logged. By default, the level
option is set to info
.
import { logger } from "girok"
const log = logger({
level: "warn",
})
log.trace("trace") // Not printed
log.debug("debug") // Not printed
log.info("info") // Not printed
log.warn("warn") // prints "warn"
log.error("error") // prints "error"
log.fatal("fatal") // prints "fatal" and exit process
The following levels are available:
trace < debug < info < warn < error < fatal
To add context properties to your logs, you can use the logger context
function. This function takes a new contex in entry and returns a new logger with the defined context.
import { logger } from "girok"
let log = logger()
log.info("Message without context") // [INFO] Message without context
log = log.context({ foo: "bar" })
log.info("Message with context") // [INFO] foo="bar" Message with context
log = log.context({ test: 42 })
log.info("Message with context") // [INFO] foo="bar", test=42 Message with context
You can use the formatter
option to define the format of your logs.
There are two predefined formatters, text
and json
. The text
formatter is the default one.
import { logger } from "girok"
let logTxt = logger({ formatter: "text" })
let logJson = logger({ formatter: "json" })
logTxt.info("Hello World")
// [INFO] 1970-01-01 00:00:00.000 (index.ts:6:8) Hello World
logJson.info("Hello World")
// {"level":"INFO","caller":"index.ts:7:9","ts":0,"message":"Hello World"}
You can use the formatter
option to define your own formatter function. Here is the signature of the formatter function:
type LogMeta = {
level: Level
caller?: string
ts: number
context: Record<string, any>
}
type Formatter = (meta: LogMeta, ...args: any[]) => string
There are two predefined formatters functions, formatText
and formatJson
. Those are the formatters used by the default text
and json
formatters, respectively.
Each of these can be customized.
import { logger } from "girok"
import { formatText, formatJson } from "girok/formatters"
const logTxt = logger({
formatter: formatText(), // alias for 'text'
})
const logJson = logger({
formatter: formatJson(), // alias for 'json'
})
Format log message as text.
Signature:
type FormatText = (
fmt: string,
meta: { ansi?: boolean; inline?: boolean; depth?: number | null }
) => Formatter
Example:
let log = formatText("[%lvl%] %date% (%caller%) %args%", {
depth: 4,
ansi: false,
inline: true,
})
log.info("Hello World!")
// [INFO] 2022-01-01 00:00:00.000 (index.js) Hello World!
log = formatText(
"[%lvl%] %date.format(YYYY-MM-DD HH:mm:ss.SSS).utc% %ctx% %args%"
)
log.warn("Hello World!")
// [WARN] 2022-01-01 00:00:00.000 Hello World!
Format log as JSON object. Each argument is treated as a sequence of alternating keys and values. If there is only one argument passed, it gets the default "message" key.
Signature:
type FormatJson = (
filter?: string[], // default is ['level', 'caller', 'ts', 'context', 'args']
opt?: { tsFormat?: string; utc?: boolean }
) => Formatter
Example:
const log = formatJson(["level", "ts"])
log.info("key1", "val1", "key2", 42, "key3", { foo: "bar" })
// {"level":"info","ts":123456789,"key1":"val1","key2":42,"key3":{"foo":"bar"}}
log.info("Hey!")
// {"level":"info","ts":123456790,"message":"Hey!"}
The outputs option allows you to define a list of target outputs for your logs. The default output is ["stdout"]
.
Acceptable values are:
-
stdout
- write logs to standard output -
file://<filePath>
- appends logs to a file located at<filePath>
- Transporter function - see next section for more details
import { logger } from "girok"
const log = logger({
outputs: ["stdout", "file://./logs"],
})
You can alson define a Transporter function as an output. This allows you to send logs to custom destinations.
The signature of a Transporter function is:
type Transporter = (buffer: string[]) => void
Girok provides the following default Transporter functions:
This is the default transporter. It writes logs to the standard output. This is the transporter used by the "stdout"
alias.
import { logger } from "girok"
import { stdout } from "girok/transporters"
const log = logger({
outputs: [stdout], // same as ["stdout"]
})
This transporter write logs to a file. This is the transporter used by the "file"
alias.
-
path
- path to the file -
flag
- flag to open the file. The default is"a"
, which means append.
import { logger } from "girok"
import { file } from "girok/transporters"
const log = logger({
outputs: [file({ path: "./logs", flag: "a" })], // same as ["file://./logs"]
})
This transporter sends logs to an HTTP server. By default the logs in the buffer are concatened and written in the body of the request. You can configure the parser
option to parse the response before sending it.
Signature:
type HttpTransporter = (config: {
url: string
method?: string // default is POST
headers?: Record<string, string>
onError?: (error: any) => void
parser?: (data: string[]) => any // tr
}) => Transporter
here is an example of using the http transporter:
import { logger } from "girok"
import { http } from "girok/transporters"
const log = logger({
outputs: [http({ url: "http://localhost:7357" })],
})
This transporter sends logs to a Logstash server.
Signature:
type LogstashTransporter = (config: {
url?: string // default is http://localhost:5044
onError?: (error: any) => void
})
Example:
import { logger } from "girok"
import { logstash } from "girok/transporters"
const log = logger({
outputs: [logstash()],
})
This transporter sends logs to a Splunk server.
Signature:
type SplunkTransporter = (config: {
url?: string // default is http://localhost:8088/services/collector
token?: string
metadata?: {
time?: number
host?: string
source?: string
sourcetype?: string
index?: string
}
onError?: (e: Error) => void
})
Example:
import { logger } from "girok"
import { splunk } from "girok/transporters"
const log = logger({
outputs: [splunk()],
})
The bufferSize
option defines the maximum number of logs in the buffer before flushing. The default is 1, which means logs will be flushed after each log.
import { logger } from "girok"
const log = logger({
bufferSize: 5,
})
log.info("1") // No output
log.info("2") // No output
log.info("3") // No output
log.info("4") // No output
log.info("5") // output: 1\n2\n3\n4\n5\n
The flushInterval
option defines the interval in milliseconds between two flushes. The default is undefined
. Meaning that logs will be flushed after each buffer completion.
import { logger } from "girok"
const log = logger({
flushInterval: 10_000, // logs are flushed every 10 seconds
})
The overrideConsole
option defines whether the console
object should be overridden by the current logger. The default is false
.
import { logger } from "girok"
const log = logger({
overrideConsole: true,
})
console.log("Hello, World!") // alias for log.info("Hello World")
// [INFO] 1970-01-01 00:00:00.000 (index.ts:7:9) Hello World!
console.warn("Test", 42)
// [WARN] 1970-01-01 00:00:00.000 (index.ts:9:9) Test 42