[READ-ONLY] Mirror of https://github.com/FoxxMD/logging. A typed, opinionated, batteries-included, Pino-based logging solution for backend TS/JS projects foxxmd.github.io/logging
child-logger logging logging-library nodejs pinojs typescript-library
0

Configure Feed

Select the types of activity you want to include in your feed.

feat: Add env for coercing stdout/err to colorize

FoxxMD (Mar 21, 2024, 5:03 PM EDT) 75f846d4 dd4c8d89

+41 -6
+8
README.md
··· 172 172 173 173 See [Building A Logger](#building-a-logger) for more information. 174 174 175 + #### Colorizing Docker Logs 176 + 177 + Color output to STD out/err is normally automatically detected by [colorette](https://github.com/jorgebucaran/colorette) or can manually be set using `colorize` anywhere [PrettyOptions](https://foxxmd.github.io/logging/interfaces/factory._internal_.PrettyOptions_.html) are accepted. However docker output can be hard to detect as supporting colorizing, or the output may not be TTY at the container interface but is viewed by a terminal or web app that does support colorizing. 178 + 179 + Therefore `@foxxmd/logging` will look for a `COLORED_STD` environmental variable and, if no other `colorize` option is set _and the ENV is not empty_, will use the truthy value of this variable to set `colorize` **for any `buildDestinationStdout` or `buildDestinationStderr` transports.** This includes the built-in stdout transports for `loggerApp` and `loggerAppRolling`. 180 + 181 + Thus you could set `COLORED_STD=true` in your Dockerfile to coerce colored output to docker logs. If a user does not want colored output for any reason they can simply override the environmental variable like `COLORED_STD=false` 182 + 175 183 ## Usage 176 184 177 185 ### Child Loggers
+2 -2
package-lock.json
··· 1 1 { 2 2 "name": "@foxxmd/logging", 3 - "version": "0.1.12", 3 + "version": "0.1.13", 4 4 "lockfileVersion": 3, 5 5 "requires": true, 6 6 "packages": { 7 7 "": { 8 8 "name": "@foxxmd/logging", 9 - "version": "0.1.12", 9 + "version": "0.1.13", 10 10 "license": "MIT", 11 11 "dependencies": { 12 12 "pino": "^8.19.0",
+1 -1
package.json
··· 1 1 { 2 2 "name": "@foxxmd/logging", 3 3 "type": "module", 4 - "version": "0.1.12", 4 + "version": "0.1.13", 5 5 "repository": "https://github.com/foxxmd/logging", 6 6 "description": "A typed, opinionated, batteries-included, Pino-based logging solution for backend TS/JS projects", 7 7 "scripts": {
+18 -3
src/destinations.ts
··· 8 8 import {destination, DestinationStream} from "pino"; 9 9 import {build, prettyFactory} from "pino-pretty" 10 10 import {PRETTY_OPTS_FILE, prettyOptsConsoleFactory, prettyOptsFileFactory} from "./pretty.js"; 11 - import {fileOrDirectoryIsWriteable} from "./util.js"; 11 + import {fileOrDirectoryIsWriteable, parseBool} from "./util.js"; 12 12 import path from "path"; 13 13 import {Transform, TransformCallback} from "node:stream"; 14 14 import pump from 'pump'; ··· 185 185 } 186 186 } 187 187 188 + // docker stdout/err only colorizes if run with `-it` flag or `tty: true` in docker-compose 189 + // but most common outputs and web log viewers (portainer, dozzle) support colors and using those flags/options is not common for most users 190 + // so 191 + // can use COLORED_STD=true hint in Dockerfile to coerce colorizing output when running in a docker container. 192 + // and using this instead of FORCE_COLOR (used by colorette) so that we only affect console output instead of all streams 193 + // and value must evaluate to a truthy value to allow users to disable easily 194 + const coloredEnv = process.env.COLORED_STD; 195 + const coloredConsole = (coloredEnv === undefined || coloredEnv === '') ? undefined : parseBool(coloredEnv); 196 + const prettyColorizeEnv: {colorize?: boolean} = {}; 197 + // colorette only does autodetection if `colorize` prop is not present *at all*, rather than just being undefined 198 + // so need to use default object and only add if we detect there is a non-empty value 199 + if(coloredConsole !== undefined) { 200 + prettyColorizeEnv.colorize = coloredConsole; 201 + } 202 + 188 203 /** 189 204 * Creates a `LogLevelStreamEntry` stream that writes to STDOUT at or above the minimum `level` 190 205 * ··· 192 207 * @see buildDestinationStream 193 208 * */ 194 209 export const buildDestinationStdout = (level: LogLevel, options: Omit<StreamDestination, 'destination'> = {}): LogLevelStreamEntry => { 195 - const opts = {...options, destination: destination({dest: 1, sync: true})} 210 + const opts = {...prettyColorizeEnv, ...options, destination: destination({dest: 1, sync: true})} 196 211 return buildDestinationStream(level, opts); 197 212 } 198 213 ··· 203 218 * @see buildDestinationStream 204 219 * */ 205 220 export const buildDestinationStderr = (level: LogLevel, options: Omit<StreamDestination, 'destination'> = {}): LogLevelStreamEntry => { 206 - const opts = {...options, destination: destination({dest: 2, sync: true})}; 221 + const opts = {...prettyColorizeEnv, ...options, destination: destination({dest: 2, sync: true})}; 207 222 return buildDestinationStream(level, opts); 208 223 }
+12
src/util.ts
··· 60 60 } 61 61 return longest; 62 62 } 63 + 64 + export function parseBool<T = undefined>(value: any, defaultVal: T = undefined): boolean | T { 65 + if (value === undefined || value === '') { 66 + return defaultVal 67 + } 68 + if (typeof value === 'string') { 69 + return ['1','true','yes'].includes(value.toLocaleLowerCase().trim()); 70 + } else if (typeof value === 'boolean') { 71 + return value; 72 + } 73 + throw new Error(`'${value.toString()}' is not a boolean value.`); 74 + }