{"id":13565540,"url":"https://github.com/thanpolas/logality","last_synced_at":"2025-08-20T12:31:04.627Z","repository":{"id":37942553,"uuid":"134273850","full_name":"thanpolas/logality","owner":"thanpolas","description":"Versatile JSON Logger","archived":false,"fork":false,"pushed_at":"2022-06-13T22:01:52.000Z","size":1445,"stargazers_count":124,"open_issues_count":1,"forks_count":1,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-12-11T12:12:03.165Z","etag":null,"topics":["log","logger","logging","logging-framework","logging-library"],"latest_commit_sha":null,"homepage":"https://github.com/thanpolas/logality","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thanpolas.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null},"funding":{"github":["thanpolas"],"custom":["thanpolas.eth","https://gitcoin.co/grants/3393/uniswap-and-daos-library-development"]}},"created_at":"2018-05-21T13:35:35.000Z","updated_at":"2023-05-27T09:04:59.000Z","dependencies_parsed_at":"2022-08-27T11:03:08.466Z","dependency_job_id":null,"html_url":"https://github.com/thanpolas/logality","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thanpolas%2Flogality","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thanpolas%2Flogality/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thanpolas%2Flogality/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thanpolas%2Flogality/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thanpolas","download_url":"https://codeload.github.com/thanpolas/logality/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230423559,"owners_count":18223435,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["log","logger","logging","logging-framework","logging-library"],"created_at":"2024-08-01T13:01:49.680Z","updated_at":"2024-12-19T11:11:37.527Z","avatar_url":"https://github.com/thanpolas.png","language":"JavaScript","readme":"# Logality\n\n\u003e Versatile JSON Logger.\n\n[![NPM Version][npm-image]][npm-url]\n[![CircleCI][circle-image]][circle-url]\n[![codecov](https://codecov.io/gh/thanpolas/logality/branch/master/graph/badge.svg?token=UvpkiCwJHj)](https://codecov.io/gh/thanpolas/logality)\n[![Discord](https://img.shields.io/discord/847075821276758096?label=discord\u0026color=CBE9F0)](https://discord.gg/GkyEqzJWEY)\n[![Twitter Follow](https://img.shields.io/twitter/follow/thanpolas.svg?label=thanpolas\u0026style=social)](https://twitter.com/thanpolas)\n\n![Logality](/assets/logality_preview.png)\n\n## Why Logality\n\n-   JSON and Pretty Print log messages.\n-   Extend or alter logging schema to fit your needs.\n-   Customize built-in serializers by overwriting them to create your\n    own logging schema.\n-   Middleware support.\n-   Allows full manipulation of output.\n-   Use in libraries and compose multiple Logality instances on the root\n    project.\n-   Automatically detects the module filename and path and includes in the log.\n\n[👉 See how Logality compares to other popular loggers.][comparison].\n\n# Install\n\nInstall the module using NPM:\n\n```\nnpm install logality --save\n```\n\n# Documentation\n\n## Quick Start\n\n```js\nconst Logality = require('logality');\n\nconst logality = Logality();\n\nconst log = logality.get();\n\nlog.info('Hello World!');\n```\n\n## Initial Configuration\n\nLogality requires to be initialized and configured once,\nthen use the instance throughout your application.\n\nYou can configure Logality during instantiation, here are the available\nconfiguration options:\n\n-   `appName` {string} An arbitrary string to uniquely identify\n    the service (logger instance).\n-   `prettyPrint` {boolean|Object} If true will format and prettify the event and\n    context, default is `false`. You may define additional options to configure\n    pretty printing, they can be combined:\n    -   `prettyPrint.noTimestamp` {boolean} Do not print timestamp.\n    -   `prettyPrint.noFilename` {boolean} Do not print Log filename source.\n    -   `prettyPrint.onlyMessage` {boolean} Only print the log message (no context).\n-   `minLevel` {number|string} Define the minimum level to be logged and ignore lower log levels. [See log levels for input values][log-levels], accepts both the string or numeric representations of the levels.\n-   `serializers` {Object} You can define custom serializers or overwrite\n    logality's. Read more [about Serializers bellow][serializers].\n-   `async` {boolean} Set to true to enable the asynchronous API for logging,\n    see more bellow. Read more [on the async option bellow][async].\n-   `output` {Function(logContext:Object, isPiped:boolean)} Replace the output\n    process of logality with a custom one. Read more [on the custom output documentation bellow][output].\n\n```js\nconst Logality = require('logality');\n\nconst logality = Logality({\n    appName: 'service-something',\n    prettyPrint: false,\n    serializers: [(logContext) =\u003e {}],\n    async: false,\n    output: (logMessage) =\u003e {\n        process.stdout.write(logMessage);\n    },\n});\n```\n\n### Logality Terminology\n\n-   **Message {string}** The text (string) Log message input from the user.\n-   **Context {Object}** The Context (or bindings) input from the user.\n-   **LogContext {Object}** Log Context (the schema) used internally by logality for\n    processing and ultimately output.\n-   **LogMessage {String}** The serialized `LogContext` into a string for output.\n\n### Logality Execution Flow\n\n[👉 Click here or on the Flow Chart for Full Resolution](/assets/logality_flow_chart.png).\n\n[![Logality Flow Chart](/assets/logality_flow_chart_preview.png)](/assets/logality_flow_chart.png)\n\n### Logality Can be Asynchronous\n\nWhen logging has a transactional requirement, such as storing logs to a database or sending through an API, you can\nenable asynchronous mode.\n\nWhen Async is enabled all logs should be prefixed with the `await` keyword.\n\nBoth the [middleware defined through `use()`][middleware]\nand the [output function if defined][output] will be expected to execute\nasynchronously.\n\nTo enable the async API all you have to do is set the option `async` to true. All logging methods will now return\na promise for you to handle:\n\n```js\nconst Logality = require('logality');\n\nconst logality = Logality({\n    appName: 'service-audit',\n    async: true,\n});\n\n/** ... */\n\nasync function createUser (userData) =\u003e {\n    await log.info('New user creation', {\n        userData,\n    });\n}\n```\n\n### The custom \"output\" Function\n\nThe custom output function will receive two arguments and is the final operation in the [execution flow][logality-flow]. The input arguments are:\n\n-   `logContext` **Object** logContext is a native\n    JS Object representing the entire log message.\n-   `isPiped` **boolean** This argument indicates if the inbound \"logContext\" is the output of a piped instance or not (comes from a library).\n\n#### Importance of Return Value for \"output\"\n\nDepending on what value is returned by your custom output function different actions are performed by Logality.\n\n#### Custom Output: Object Return\n\nThis is what you would typically want to always return. When an object is\nreturned from your custom output function you pass the responsibility of\nserializing the Log Context into a string to Logality.\n\nAs per the [Logality Flow Diagram][logality-flow], there are a few more steps\nthat are done after your custom output returns an Object value:\n\n1. Logality checks your `prettyPrint` setting and:\n    1. If it's true will format your Log Context into a pretty formatted string\n       message.\n    2. If it's false will serialize using `JSON.stringify`.\n2. Logality will then output that serialized stream by writing to the `process.stdout` stream.\n\n#### Custom Output: String Return\n\nWhen you return a string, Logality will skip the serialization of your Log\nMessage and will directly invoke the output by writing to the `process.stdout`\nstream.\n\nThis technique gives you the freedom to implement your own output format and/or\ncreate your pretty output formats.\n\n#### Custom Output: No Return\n\nWhen your custom output does not return anything, Logality will assume that you\nhave handled everything and will not perform any further actions.\n\nIn those cases your custom output function is responsible for serializing\nthe Log Context and outputting it to the medium you see fit (stdout or a\ndatabase).\n\n\u003e **ℹ️ Note**: This is the recommended way to apply filters on what messages you want to be logged.\n\n## Logality Instance Methods\n\n### get() :: Getting a Logger\n\nTo get a logger you have to invoke the `get()` method. That method will detect and use the module filename that it was invoked from so it is advised that you use the `get()` method in each module to have proper log messages.\n\n```js\nconst log = logality.get();\n\nlog(level, message, context);\n```\n\nThe `get()` method will return the `log()` method partialed with arguments.\nThe full argument requirements of `log()`, are:\n\n```js\nlogality.log(filename, level, message, context);`\n```\n\nWhen using `get()` you will receive the logger function with the `filename` argument already filled out. That is why you don't need to input the `filename` argument when you are using `logality.get()`.\n\nThe partialed and returned `log` function will also have level helpers as\nillustrated in [\"Log Levels\"](#log-levels) section.\n\n### Logging Messages\n\nUsing any log level function (e.g. `log.info()`), your first argument is the \"message\". This is any arbitrary string to describe what has happened. It is the second argument, \"context\" that you will need to put any and all data you also want to attach with the logging message.\n\n```js\nlog.info(message, context);\n```\n\nThe `context` argument is an object literal, parsed by what are called \"Serializers\". Serializers\nwill take your data as input and format them in an appropriate, logging schema\ncompliant output.\n\nYou may extend logality with new [serializers][serializers] or you may\noverwrite the existing ones.\n\n### pipe() :: Compose Multiple Logality Instances\n\nUse `pipe()` to link multiple logality instances to the root instance:\n\n```js\nconst Logality = require('logality');\n\nconst parentLogality = Logality();\nconst childLogality = Logality();\n\nparentLogality.pipe(childLogality);\n```\n\nWhat this does is pipe all the output of the piped (child) logality instances to the \"parent\" Logality. This is particularly useful if a library is using Logality and you want to pipe its output or you want to have multiple classes of log streams (i.e. for audit logging purposes).\n\n-   `pipe()` Accepts a single Logality instance or an Array of Logality instances.\n\n\u003e **ℹ️ Note**: The LogContext of the child instance, will go through all the middleware and custom output functions defined in the parent instance.\n\n\u003e **ℹ️ Note**: This is the case when the second argument `isPiped` will have a `true` value.\n\n### use() :: Add Middleware.\n\nYou can add multiple Middleware that will be invoked after all the [serializers][serializers] are applied (built-in and custom defined) and before the \"Write to output\" method is called.\n\nThe middleware will receive the \"Log Message\" as a native Javascript Object and you can mutate or process it.\n\nAll middleware with `use()` are synchronous. To support async middleware you have to enable the [`async` mode][async] when instantiating.\n\n#### use() Synchronous Example\n\n```js\nconst Logality = require('logality');\n\nconst logality = Logality();\n\nlogality.use((context) =\u003e {\n    delete context.user;\n});\n```\n\n#### use() Asynchronous Example\n\n```js\nconst Logality = require('logality');\n\nconst logality = Logality({\n    async: true,\n});\n\nlogality.use(async (context) =\u003e {\n    await db.write(context);\n});\n```\n\n## The Logging Schema\n\nLogality automatically calculates and formats a series of system information\nwhich is then included in the output. When you log using:\n\n```js\nlog.info('Hello World!');\n```\n\nLogality, when on production, will output the following (expanded) JSON string:\n\n```JSON\n{\n    \"severity\": 6,\n    \"level\": \"info\",\n    \"dt\": \"2018-05-18T16:25:57.815Z\",\n    \"message\": \"hello world\",\n    \"event\": {},\n    \"context\": {\n        \"runtime\": {\n            \"application\": \"testLogality\"\n        },\n        \"source\": {\n          \"file_name\": \"/test/spec/surface.test.js\"\n        },\n        \"system\": {\n            \"hostname\": \"localhost\",\n            \"pid\": 36255,\n            \"process_name\": \"node .\"\n        }\n    }\n}\n```\n\n-   `severity` **{number}** Message severity expressed in an integer (7 lowest,\n    0 higher), see bellow fow values.\n-   `level` **{string}** Message severity expressed in a unique string,\n    see bellow fow values.\n-   `dt` **{string}** An [ISO8601][iso8601] date.\n-   `message` **{string}** Any message provided to the logger.\n-   `event` **{Object}** When the log was triggered by an event, the metadata\n    of that event are stored here. Logality supports many kinds of events as\n    explained in the Serializers section.\n-   `context` **{Object}** Context related to the log message.\n-   `context.runtime.application` **{string}** Name of the service, define this\n    when first instantiating the locality service.\n-   `context.source.file_name` **{string}** The module where the log originated.\n-   `context.system.hostname` **{string}** The local system's hostname.\n-   `context.system.pid` **{string}** The local process id.\n-   `context.system.process_name` **{string}** The local process name.\n\n## Log Levels\n\nAs per the [Log Schema](log-schema), the logging levels map to those of Syslog\nRFC 5424:\n\n| Syslog Level | Level Enum  | Description                       |\n| ------------ | ----------- | --------------------------------- |\n| 0            | `emergency` | System is unusable                |\n| 1            | `alert`     | Action must be taken immediately  |\n| 2            | `critical`  | Critical conditions               |\n| 3            | `error`     | Error Conditions                  |\n| 4            | `warn`      | Warning Conditions                |\n| 5            | `notice`    | Normal, but significant condition |\n| 6            | `info`      | Informational messages            |\n| 7            | `debug`     | Debug-level messages              |\n\nEach one of the \"Level Enum\" values is an available function at the logger that is returned using the `get()` method:\n\n```js\nconst Logality = require('logality');\nconst logality = new Logality();\nconst log = logality.get();\n\nlog.debug('This is message of level: Debug');\nlog.info('This is message of level: Info');\nlog.notice('This is message of level: Notice');\nlog.warn('This is message of level: warning');\nlog.error('This is message of level: Error');\nlog.critical('This is message of level: Critical');\nlog.alert('This is message of level: Alert');\nlog.emergency('This is message of level: Emergency');\n```\n\n## Logality Serializers\n\nSerializers are triggered by defined keys in the `context` object.\nEvery serializer is configured to listen to a specific context key, for example\nthe user serializer expects the `user` key in the context:\n\n```js\nlog.info('User Logged in', {\n    user: udo,\n});\n```\n\nIf no serializer is configured for the `user` property, the data will be\nignored. Logality has implemented the following serializers out of the box:\n\n### The User Serializer\n\n\u003e Serializes a User Data Object.\n\n```js\n// a user logged in\nconst user = login(username, password);\n\n// Let log the event\nlog.info('User Logged in', { user: user });\n```\n\n#### Expects\n\n-   `id` The user's id.\n-   `email` The user's email.\n\n#### Outputs\n\n```JSON\n    \"context\": {\n        \"user\": {\n            \"id\": 10,\n            \"email\": \"one@go.com\",\n        }\n    }\n```\n\n### The Error Serializer\n\n\u003e Serializes a Javascript Error Object or an Exception.\n\n```js\nconst err = new Error('Broke');\n\nlog.error('Something broke', { error: err });\n```\n\n#### Expects\n\nA native JS Error Object, or similar:\n\n-   `name` **{string}** Name of the error.\n-   `message` **{string}** The error's message.\n-   `stack` **{string}** The stack trace. Logality will automatically parse the stack trace to a JSON object.\n\n#### Outputs\n\n```JSON\n    \"event\":{\n        \"error\":{\n            \"name\":\"Error\",\n            \"message\":\"Broke\",\n            \"backtrace\": \"Stack Trace...\",\n        }\n    }\n```\n\n### The Request Serializer\n\n\u003e Serializes an Express.JS Request Object.\n\n```js\nfunction index(req, res) {\n    log.info('Index Request', { req: req });\n}\n```\n\n#### Expects\n\nExpress JS Request Object.\n\n#### Outputs\n\n```JSON\n    \"event\":{\n        \"http_request\": {\n            \"headers\": {},\n            \"host\": \"localhost\",\n            \"method\": \"GET\",\n            \"path\": \"/\",\n            \"query_string\": \"\",\n            \"scheme\": \"http\"\n        }\n    }\n```\n\n-   `event.http_request` **{Object}** When the request object is passed the following additional data are stored:\n-   `event.http_request.headers` **{Object}** Key-value pairs of all the HTTP headers, excluding sensitive headers.\n-   `event.http_request.host` **{string}** The hostname.\n-   `event.http_request.method` **{string}** HTTP method used.\n-   `event.http_request.path` **{string}** The request path.\n-   `event.http_request.query_string` **{string}** Quer string used.\n-   `event.http_request.scheme` **{string}** One of \"http\" or \"https\".\n\n### The Custom Serializer\n\n\u003e Serializes any data that is passed as JSON.\n\n```js\n// Custom log\nlog.info('Something happened', {\n    custom: {\n        any: 'value',\n    },\n});\n```\n\n#### Expects\n\nAnything\n\n#### Outputs\n\n```JSON\n    \"context\": {\n        \"custom\": {\n            \"any\": \"value\"\n        }\n    }\n```\n\n## Custom Serializers\n\nYou can define your own serializers or overwrite the existing ones when you first instantiate Logality. There are three parameters when creating a serializer:\n\n-   **Context Name** The name on your `context` object that will trigger the serializer.\n-   **Output Path** The path in the JSON output where you want the serializer's value to be stored. Use dot notation to signify the exact path.\n-   **Value** The serialized value to output on the log message.\n\nThe _Context Name_ is the key on which you define your serializer. So for instance when you set a serializer on the user key like so `mySerializers.user = userSerializer` the keyword `user` will be used.\n\nOutput Path and Value are the output of your serializer function and are\nexpected as separate keys in the object you must return:\n\n-   `path` **{string}** Path to save the value, use dot notation.\n-   `value` **{\\*}** Any value to store on that path.\n\nAn Example:\n\n```js\nconst Logality = require('logality');\n\nmySerializers = {\n    user: function (user) {\n        return {\n            path: 'context.user',\n            value: {\n                id: user.id,\n                email: email.id,\n                type: user.type,\n            },\n        };\n    },\n    order: function (order) {\n        return {\n            path: 'context.order',\n            value: {\n                order_id: order.id,\n                sku_id: order.sku,\n                total_price: order.item_price * order.quantity,\n                quantity: order.quantity,\n            },\n        };\n    },\n};\n\nconst logality = new Logality({\n    appName: 'service-something',\n    serializers: mySerializers,\n});\n```\n\n### Multi Key Custom Serializers\n\nIn some cases you may need to write to more than one keys in the log context.\nTo be able to do that, simply return an Array instead of an Object like so:\n\n```js\nconst Logality = require('logality');\n\nmySerializers = {\n    user: function (user) {\n        return [\n            {\n                path: 'context.user',\n                value: {\n                    id: user.id,\n                    email: email.id,\n                    type: user.type,\n                },\n            },\n            {\n                path: 'context.request',\n                value: {\n                    user_id: user.id,\n                },\n            },\n        ];\n    },\n};\n\nconst logality = new Logality({\n    appName: 'service-something',\n    serializers: mySerializers,\n});\n```\n\n## Example of How To Initialize Logality on Your Project\n\n### /app/services/logger.service.js\n\nThis is the initializing module. During your application bootstrap and before\nyou anything else, you need to require this module and invoke the `init()`\nfunction to initialize logality.\n\n```js\nconst Logality = require('logality');\n\nconst logger = (module.exports = {});\n\n// Will store the logality reference.\nlogger.logality = null;\n\n/**\n * Initialize the logging service.\n *\n * @param {Object} bootOpts boot options. This module will check for:\n * @param {string=} bootOpts.appName Set a custom appname for the logger.\n * @param {WriteStream|null} bootOpts.wstream Optionally define a custom\n *   writable stream.\n */\nlogger.init = function (bootOpts = {}) {\n    // check if already initialized.\n    if (logger.logality) {\n        return;\n    }\n\n    const appName = bootOpts.appName || 'app-name';\n\n    logger.logality = new Logality({\n        prettyPrint: process.env.ENV !== 'production',\n        appName,\n        wstream: bootOpts.wstream,\n    });\n\n    // Create the get method\n    logger.get = logger.logality.get.bind(logger.logality);\n};\n```\n\n### /app/model/user.model.js\n\nThen, in any module you want to log something you fetch the logality instance\nfrom your logger service.\n\n```js\nconst log = require('../services/log.service').get();\n\n/* ... */\n\nfunction register (userData) =\u003e {\n    log.info('New user registration', {\n        userData\n    });\n}\n```\n\n\u003e **ℹ️ Note**: You can view a real-world example of Logality being used in production [in this Discord Bot Project](https://github1s.com/skgtech/skgbot/blob/HEAD/app/services/log.service.js).\n\n## How Logality Compares to Other Loggers\n\nComparison table as of 16th of April 2021.\n\n|                    | Logality | [Winston][winston] | [Bunyan][bunyan] | [Pino][pino] |\n| ------------------ | -------- | ------------------ | ---------------- | ------------ |\n| JSON Output        | ✅       | ✅                 | ✅               | ✅           |\n| Pretty Print       | ✅       | ✅                 | ❌               | ✅           |\n| Custom Log Levels  | ❌       | ✅                 | ✅               | ✅           |\n| Serializers        | ✅       | ❌                 | ✅               | ✅           |\n| Middleware         | ✅       | ✅                 | ❌               | ✅           |\n| Mutate JSON Schema | ✅       | ✅                 | ❌               | ❌           |\n| Output Destination | ✅       | ✅                 | ✅               | ✅           |\n| Mutate Output      | ✅       | ✅                 | ❌               | ❌           |\n| Async Operation    | ✅       | ❌                 | ❌               | ❌           |\n| Filename Detection | ✅       | ❌                 | ❌               | ❌           |\n| Speed Optimised    | ❌       | ❌                 | ❌               | ✅           |\n| Used in Libraries  | ✅       | ❌                 | ❌               | ❌           |\n\n# Project Meta\n\n## Releasing\n\n1. Update the changelog bellow (\"Release History\").\n1. Ensure you are on master and your repository is clean.\n1. Type: `npm run release` for patch version jump.\n    - `npm run release:minor` for minor version jump.\n    - `npm run release:major` for major major jump.\n\n## Release History\n\n-   **v3.1.3**, _19 Nov 2021_\n    -   Will now safely JSON serialize BitInt values. Handles also edge case on pretty print.\n    -   Updated all dependencies to latest.\n-   **v3.1.1**, _26 Sep 2021_\n    -   Removed emojis for UTF-8 chars and corrected formating of pretty print.\n-   **v3.1.0**, _26 Sep 2021_\n    -   Added new options for pretty print (noTimestamp, noFilename, onlyMessage).\n    -   Added log level filtering.\n    -   Added codecoverage report.\n    -   Replaced figures package with emojis.\n    -   Updated all dependencies to latest.\n-   **v3.0.4**, _31 May 2021_\n    -   Updated all dependencies to latest.\n    -   Tweaked eslint and prettier configurations.\n-   **v3.0.3**, _16 Apr 2021_\n    -   Updated all dependencies to latest.\n    -   Tweaked, fixed and updated README, added comparison chart with popular loggers.\n-   **v3.0.2**, _30 Oct 2020_\n    -   Updated all dependencies to latest.\n-   **v3.0.1**, _03 Jul 2020_\n    -   Updated all dependencies to latest.\n-   **v3.0.0**, _04 Apr 2020_\n    -   Introduced [middleware][middleware] for pre-processing log messages.\n    -   Introduced the [pipe()][pipe] method to link multiple Logality\n        instances together, enables using logality in dependencies and libraries.\n    -   **Breaking Change** Replaced \"wstream\" with [\"output\"][output] to\n        customize logality's output.\n-   **v2.1.2**, _24 Feb 2020_\n    -   Removed http serializer when pretty print is enabled.\n    -   Replaced aged grunt with \"release-it\" for automated releasing.\n-   **v2.1.1**, _19 Feb 2020_\n    -   Added the \"objectMode\" configuration.\n    -   Implemented multi-key serializers feature.\n    -   Fixed async logging issues and tests.\n-   **v2.1.0**, _18 Feb 2020_\n    -   Added Async feature.\n-   **v2.0.1**, _18 Feb 2020_\n    -   Fixed issue with null http headers on sanitizer helper.\n-   **v2.0.0**, _29 Jan 2020_ :: Extensible Serializers\n    -   Enables new serializers and allows over-writing the built-in ones.\n    -   Backwards compatible.\n-   **v1.1.0**, _05 Jun 2018_ :: JSON Log Schema Version: 4.1.0\n    -   Added `prettyPrint` option, thank you [Marius](https://github.com/balajmarius).\n-   **v1.0.0**, _21 May 2018_ :: JSON Log Schema Version: 4.1.0\n    -   Big Bang\n\n## License\n\nCopyright Thanasis Polychronakis [Licensed under the ISC license](/LICENSE)\n\n[log-schema]: https://github.com/timberio/log-event-json-schema\n[iso8601]: https://en.wikipedia.org/wiki/ISO_8601\n[npm-image]: https://img.shields.io/npm/v/logality.svg\n[npm-url]: https://npmjs.org/package/logality\n[circle-image]: https://img.shields.io/circleci/build/gh/thanpolas/logality/master?label=Tests\n[circle-url]: https://circleci.com/gh/thanpolas/logality\n[stream-docs]: https://nodejs.org/api/stream.html#stream_object_mode\n[serializers]: #logality-serializers\n[async]: #about-asynchronous-logging\n[logality-flow]: #logality-execution-flow\n[middleware]: #use--add-middleware\n[output]: #the-custom-output-function\n[pipe]: #pipe--compose-multiple-logality-instances\n[comparison]: #how-logality-compares-to-other-loggers\n[winston]: https://github.com/winstonjs/winston\n[bunyan]: https://github.com/trentm/node-bunyan\n[pino]: https://github.com/pinojs/pino\n[log-levels]: #log-levels\n","funding_links":["https://github.com/sponsors/thanpolas","thanpolas.eth","https://gitcoin.co/grants/3393/uniswap-and-daos-library-development"],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthanpolas%2Flogality","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthanpolas%2Flogality","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthanpolas%2Flogality/lists"}