This package provides a transport that transforms standard pino logs to logfmt.
A standard Pino log line like:
{"level":30,"time":1522431328992,"msg":"hello world","pid":42,"hostname":"foo","v":1}
Will format to:
level=30 time=1522431328992 msg="hello world" pid=42 hostname=foo v=1
npm install pino-logfmt
Using the example script from the Pino module, we can see what the prettified logs will look like:
node examples/basic.js | pino-logfmt --flatten-nested --snake-case
level=30 time=1710427304769 pid=78824 hostname=MacBook-Pro-de-Victor-2.local msg="hello world"
level=50 time=1710427304769 pid=78824 hostname=MacBook-Pro-de-Victor-2.local msg="this is at error level"
level=30 time=1710427304769 pid=78824 hostname=MacBook-Pro-de-Victor-2.local msg="the answer is 42"
level=30 time=1710427304769 pid=78824 hostname=MacBook-Pro-de-Victor-2.local obj=42 msg="hello world"
level=30 time=1710427304769 pid=78824 hostname=MacBook-Pro-de-Victor-2.local obj=42 b=2 msg="hello world"
level=30 time=1710427304769 pid=78824 hostname=MacBook-Pro-de-Victor-2.local nested_obj=42 msg=nested
level=50 time=1710427304770 pid=78824 hostname=MacBook-Pro-de-Victor-2.local err_type=Error err_message="an error" err_stack="Error: an error
at Object.<anonymous> (/Users/victor/Documents/projects/pino-logfmt/node_modules/pino/examples/basic.js:21:12)
at Module._compile (node:internal/modules/cjs/loader:1256:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)
at Module.load (node:internal/modules/cjs/loader:1119:32)
at Module._load (node:internal/modules/cjs/loader:960:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
at node:internal/main/run_main_module:23:47" msg="an error"
level=30 time=1710427304770 pid=78824 hostname=MacBook-Pro-de-Victor-2.local a=property msg="hello child!"
level=30 time=1710427304770 pid=78824 hostname=MacBook-Pro-de-Victor-2.local a=property another=property msg="hello baby.."
level=20 time=1710427304770 pid=78824 hostname=MacBook-Pro-de-Victor-2.local msg="this is a debug statement"
level=20 time=1710427304770 pid=78824 hostname=MacBook-Pro-de-Victor-2.local another=property msg="this is a debug statement via child"
level=10 time=1710427304770 pid=78824 hostname=MacBook-Pro-de-Victor-2.local msg="this is a trace statement"
level=20 time=1710427304770 pid=78824 hostname=MacBook-Pro-de-Victor-2.local msg="this is a \"debug\" statement with \""
level=30 time=1710427304770 pid=78824 hostname=MacBook-Pro-de-Victor-2.local err_type=Error err_message=kaboom err_stack="Error: kaboom
at Object.<anonymous> (/Users/victor/Documents/projects/pino-logfmt/node_modules/pino/examples/basic.js:40:11)
at Module._compile (node:internal/modules/cjs/loader:1256:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)
at Module.load (node:internal/modules/cjs/loader:1119:32)
at Module._load (node:internal/modules/cjs/loader:960:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
at node:internal/main/run_main_module:23:47" msg=kaboom
level=30 time=1710427304770 pid=78824 hostname=MacBook-Pro-de-Victor-2.local
level=30 time=1710427304770 pid=78824 hostname=MacBook-Pro-de-Victor-2.local err_type=Error err_message=kaboom err_stack="Error: kaboom
at Object.<anonymous> (/Users/victor/Documents/projects/pino-logfmt/node_modules/pino/examples/basic.js:43:11)
at Module._compile (node:internal/modules/cjs/loader:1256:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)
at Module.load (node:internal/modules/cjs/loader:1119:32)
at Module._load (node:internal/modules/cjs/loader:960:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
at node:internal/main/run_main_module:23:47" msg=with
level=30 time=1710427304771 pid=78824 hostname=MacBook-Pro-de-Victor-2.local msg="after setImmediate"
const pino = require("pino")
const logger = pino({
transport: {
target: "pino-logfmt",
options: {
flattenNestedObjects: true,
convertToSnakeCase: true,
}
}
})
logger.info("Hello world")
Name | Type | Default | Description |
---|---|---|---|
includeLevelLabel |
boolean |
false |
Add the level as a label in each log entries |
levelLabelKey |
string |
"level_label" |
The name of the level label metadata. Only used if includeLevelLabel is enabled. |
formatTime |
boolean |
false |
Format pino's time field using Date.toISOString(). The time field will be overriden with the formatted date. |
timeFormat |
string |
"isoDateTime" |
A time format compatible with dateformat ' formats. |
timeKey |
string |
"time" |
The name of the key that holds the log timestamp. |
convertToSnakeCase |
boolean |
false |
Convert log field names to snake case. |
flattenNestedObjects |
boolean |
false |
Flatten nested metadata (e.g. { error: { type: "Error", message: "Something went wrong" } } becomes error_type=Error error_message="Something went wrong" |
flattenNestedSeparator |
string |
"_" |
The character that is used to merge keys when flattenNestedObjects is enabled. |
escapeMultilineStrings |
boolean |
false |
Escape multi-line strings in the log output, including deeply nested values |
destination |
string | number |
1 |
The destination where the transport will write to. By default, it logs to stdout but you can also provide a file name. |
The transport is also available using a CLI through. Almost all the options are available as CLI arguments.
Like other CLIs, you can print the help message using --help
as followed:
npx pino-logfmt --help
Usage: pino-logfmt [options]
Logfmt transport for pino
Options:
-V, --version output the version number
--include-level-label add the level as a label (default: false)
--level-label-key <string> the key of the level label (default: "level_label")
--format-time format the timestamp into an ISO date (default: false)
--time-key <string> the key that holds the timestamp (default: "time")
--snake-case convert the keys to snake case (default: false)
--flatten-nested flatten nested metadata (default: false)
--flatten-separator <string> the separator used when flattening nested metadata (default: ".")
--custom-levels, -x <string> the levels associated to their labels in the format "10:trace,20:debug" (default: "10:trace,20:debug,30:info,40:warn,50:error,60:fatal")
--time-format <string> the time format to use if time formatting is enabled (default: "isoDateTime")
--escape-multiline-strings escape multi-line strings in the log output, including deeply nested values (default: false)
-h, --help display help for command
node process-that-emits-logs.js | pino-logfmt
Name | Type | Default | Description |
---|---|---|---|
--include-level-label |
boolean |
false |
Add the level as a label in each log entries |
--level-label-key |
string |
"level_label" |
The name of the level label metadata. Only used if includeLevelLabel is enabled. |
--format-time |
boolean |
false |
Format pino's time field using Date.toISOString(). The time field will be overriden with the formatted date. |
--time-format |
string |
"isoDateTime" |
A time format compatible with dateformat ' formats. |
--time-key |
string |
"time" |
The name of the key that holds the log timestamp. |
--snake-case |
boolean |
false |
Convert log field names to snake case. |
--escape-multiline-strings |
boolean |
false |
Escape multi-line strings in the log output, including deeply nested values |
--flatten-nested |
boolean |
false |
Flatten nested metadata (e.g. { error: { type: "Error", message: "Something went wrong" } } becomes error_type=Error error_message="Something went wrong" |
--flatten-separator |
string |
"_" |
The character that is used to merge keys when flattenNestedObjects is enabled. |
To get started, you must clone the project and install dependencies.
Then, you can use devbox
to start the default environment.
git clone [email protected]:botflux/pino-logfmt.git
npm ci
devbox shell
Code is linted in commit using lint-staged
. The linter is standard
.
You can run the linter before commiting using npm run lint
.
The project is tested using node:test
. Run npm run test
to run the tests.
No API changed from v0 to v1, only support for Node.js 14, 16 and 18 was dropped.
Node.js 14 and 16 were dropped because the package now uses node:util
's parseArgs
function instead of commander
(thus reducing the package's size).
On the other hand, Node.js 18 was dropped because it reached its end of life. This means that the project can still work under this major, but there is no automated test ensuring it fully works.