3.7.61 • Published 5 months ago

@zitterorg/harum-ratione v3.7.61

Weekly downloads
-
License
MIT
Repository
github
Last release
5 months ago

npm GitHub contributions welcome

@zitterorg/harum-ratione

Performance-aware simple logger for React-Native, Expo (managed and bare) and react-native-web with custom levels and transports (colored console, file writing, etc.).

Each level has its severity: a number that represents its importance in ascending order from the least important to the most important. Eg. debug:0, info:1, warn:2, error:3.

By config the logger with a minium severity level, you will see only the logs that have it highest. Then logs will be managed by transport: the function that will display/save/send log messages.

It is also possible to extend the logger to create namespaced logs. In this way you will be able to see the log messages only for one or some parts of the code of your choice

Demo console transport with custom colored levels and namespaces: console log demo

Why another logging library?

After trying the most known logging libraries, like winston and bunyan, we found that for react-native we needed something simpler, but still flexible, and without dependencies on nodejs. Comments and suggestions are welcome.

Installation

npm install --save @zitterorg/harum-ratione

OR

yarn add @zitterorg/harum-ratione

OR

expo install @zitterorg/harum-ratione

Quick Start

import { logger } from "@zitterorg/harum-ratione";

var log = logger.createLogger();

log.debug("This is a Debug log");
log.info("This is an Info log");
log.warn("This is a Warning log");
log.error("This is an Error log");

By default the createLogger() method (called without arguments) will create a simple console logger with debug, info, warn and error levels.

Configuration

You can customize the logger by passing a config object to the createLogger method (see example below). All params are optional and will take default values if no corresponding argument is passed.

ParameterTypeDescriptionDefault
severitystringInit logs severity (least important level you want to see)debug (or the first custom level)
transportfunction or functionThe transport function/s for logs (see below for presets)The preset transport consoleTransport
transportOptionsObjectSet custom options for transportsnull
levelsObjectSet custom log levels: {name:power}false
asyncbooleanSet to true for async logs (to improve app performance)true
asyncFuncfunctionSet a cutom async function (cb: Function) => {return cb()}setTimeout
stringifyFuncfunctionSet a cutom stringify function (msg: any) => stringa customized JSON.stringify
formatFuncfunctionSet a custom format function (level: string, extension?: string, msg: any) => stringdefault string format function
dateFormatstring or functiontime, local, utc, iso or (date: Date) => stringtime
printLevelbooleanChoose whether to print the log leveltrue
printDatebooleanChoose whether to print the log date/timetrue
fixedExtLvlLengthbooleanEnsure consistent character count alignment when printing extensions and levelsfalse
enabledbooleanEnable or disable loggingtrue
enabledExtensionsstring[]Enable only certain namespacesnull

Example with common configuration

import { logger, consoleTransport } from "@zitterorg/harum-ratione";

const defaultConfig = {
  levels: {
    debug: 0,
    info: 1,
    warn: 2,
    error: 3,
  },
  severity: "debug",
  transport: consoleTransport,
  transportOptions: {
    colors: {
      info: "blueBright",
      warn: "yellowBright",
      error: "redBright",
    },
  },
  async: true,
  dateFormat: "time",
  printLevel: true,
  printDate: true,
  fixedExtLvlLength: false,
  enabled: true,
};

var log = logger.createLogger(defaultConfig);

log.debug("Debug message");
log.info({ message: "hi!" });

Custom levels

Log levels have this format: { name : severity } and you can create your personalized list, Eg:

import { logger } from "@zitterorg/harum-ratione";

const config = {
  levels: {
    trace: 0,
    info: 1,
    silly: 2,
    error: 3,
    mad: 4,
  },
};

var log = logger.createLogger(config);

log.silly("Silly message");

Levels typing

(available only if you use typescript)

To fully type the logger you can specify your custom or default levels when creating the logger.

In this way typescript will be able to know your levels, and you will receive an error if you use a level that does not exist in the configuration.

import { logger } from "@zitterorg/harum-ratione";

const config = {
  levels: {
    trace: 0,
    info: 1,
    error: 2,
  },
};

var log = logger.createLogger<"trace" | "info" | "error">(config);

log.trace("message"); // correct log call

log.silly("message"); // typescript error, "silly" method does not exist

If you add noUncheckedIndexedAccess: true in your TypeScript configuration file, you MUST define log typing as described above.

Custom transport

You can write your own transport to send logs to a cloud service, save it in to a database, or do whatever you want. The following parameters are received by the function:

  • msg: any: the message formatted by logger "time | namespace | level | msg"
  • rawMsg: any: the message (or array of messages) in its original form
  • level: { severity: number; text: string }: the log level
  • extension?: string | null: its namespace if it is an extended log
  • options?: any: the transportOptions object

You can define your custom transport as follow (example in typescript)§:

import { logger, transportFunctionType } from "@zitterorg/harum-ratione";

const customTransport: transportFunctionType = (props) => {
  // Do here whatever you want with the log message
  // You can use any options setted in config.transportOptions
  // Eg. a console log: console.log(props.level.text, props.msg)
};

const config = {
  transport: customTransport,
};

var log = logger.createLogger(config);

log.debug("Debug message");

Transport Options

By setting the transportOptions parameter you can insert the options that will be passed to transports. For some transports these may be mandatory, as in the case of the FS option for the fileAsyncTransport (see preset transports list for details).

import { logger, fileAsyncTransport } from "@zitterorg/harum-ratione";
import RNFS from "react-native-fs";

const config = {
  transport: fileAsyncTransport,
  transportOptions: {
    FS: RNFS,
    fileName: `log.txt`,
  },
};

var log = logger.createLogger(config);

log.debug("Debug message");

Multiple Arguments

Log messages can be concatenated by adding arguments to the log function:

var errorObject = {
  staus: 404,
  message: "Undefined Error",
};
log.error("New error occured", errorObject);

Preset transports

@zitterorg/harum-ratione includes some preset transports. You can import the one of your choice: import { logger, <transportName> } from '@zitterorg/harum-ratione';

Example

import { logger, mapConsoleTransport } from "@zitterorg/harum-ratione";

const config = {
  transport: mapConsoleTransport,
};

var log = logger.createLogger(config);

log.debug("Debug message");

List of included preset transports

consoleTransport

Print the logs with a formatted console.log output.

If you need a different console or method to be used instead of console.log you can set the consoleFunc option with your custom console.

nametypedescriptiondefault
colorsobjectIf setted you can choose the log colors, defined by level: {level:color}null
extensionColorsobjectIf setted you can choose the extension label colors: {extension:color}null
consoleFunc(msg:any)=>anyIf setted you can choose the console objectnull

Available colors

nameansi codenote
defaultnulldefault console color
black30
red31
green32
yellow33
blue34
magenta35
cyan36
white37
grey90
redBright91
greenBright92
yellowBright93
blueBright94
magentaBright95
cyanBright96
whiteBright97

Example

import { logger, consoleTransport } from "@zitterorg/harum-ratione";

const config = {
  levels: {
    debug: 0,
    info: 1,
    warn: 2,
    error: 3,
  },
  transport: consoleTransport,
  transportOptions: {
    colors: {
      info: "blueBright",
      warn: "yellowBright",
      error: "redBright",
    },
    extensionColors: {
      root: "magenta",
      home: "green",
    },
  },
};

var log = logger.createLogger(config);
var rootLog = log.extend("root");
var homeLog = log.extend("home");

rootLog.info("Magenta Extension and bright blue message");
homeLog.error("Green Extension and bright red message");

mapConsoleTransport

Print the logs with a selected console method (console.log, console.warn, console.error, etc.).

nametypedescriptiondefault
mapLevelsobjectSelect the console method by level: {level:method}null

If mapLevels is not setted, the transport will try to map the console methods with the level name.

Example

import { logger, mapConsoleTransport } from "@zitterorg/harum-ratione";

const config = {
  levels: {
    debug: 0,
    info: 1,
    warn: 2,
    err: 3,
  },
  transport: mapConsoleTransport,
  transportOptions: {
    mapLevels: {
      debug: "log",
      info: "info",
      warn: "warn",
      err: "error",
    },
  },
};

var log = logger.createLogger(config);

log.debug("Print this with console.log");
log.err("Print this with console.error");

fileAsyncTransport

This transport requires the installation of react-native-fs(install tutorial here) or expo-file-system, and allows you to save the logs on the <filePath>/<fileName>.txt file.

If you want a new file to be created every day you can use {date-today} in the fileName: app_logs_{date-today}.log -> app_logs_D-M-YYYY.log.

Accepted Options:

nametypedescriptiondefault
FSObjectMANDATORY, filesystem instance for the transport (RNFS or expo FileSystem)null
fileNamestringset logs file name (insert {date-today} for current date)log
filePathstringset logs file pathRNFS.DocumentDirectoryPath or expo FileSystem.documentDirectory

Example:

import { logger, fileAsyncTransport } from "@zitterorg/harum-ratione";
import RNFS from "react-native-fs";
/* EXPO:
 * import * as FileSystem from 'expo-file-system';
 */

let today = new Date();
let date = today.getDate();
let month = today.getMonth() + 1;
let year = today.getFullYear();

const config = {
  severity: "debug",
  transport: fileAsyncTransport,
  transportOptions: {
    FS: RNFS,
    /* EXPO:
     * FS: FileSystem,
     */
    fileName: `logs_{date-today}`, // Create a new file every day
  },
};

var log = logger.createLogger(config);

log.info("Print this string to a file");

NOTE: Following this example it will be possible to upload the file to your remote server

sentryTransport

Send logs to Sentry. The transport allows setting which log levels are errors, so that all others are treated as breadcrumbs, meaning log messages related to the next error that will occur. Otherwise, if not set, all messages will be treated as errors.

Accepted Options:

nametypedescriptiondefault
SENTRYObjectMANDATORY, sentry instance for the transportnull
errorLevelsArray or stringSpecify witch log levels are errors (If null, all msg will be treated as errors)null

Example:

import { logger, sentryTransport } from "@zitterorg/harum-ratione";
import * as Sentry from "@sentry/react-native";

/*
 * Configure sentry
 */

const config = {
  severity: "debug",
  transport: sentryTransport,
  transportOptions: {
    SENTRY: Sentry,
    errorLeves: "error",
  },
};

var log = logger.createLogger(config);

log.warn("Send this log to Sentry as breadcumb");
log.error("Send this log to Sentry as error");

Extensions (Namespaced loggers)

To enable logging only for certain parts of the app, you can extend the logger to different namespaces using the "extend" method. You can enable these extensions from the configuration (config.enabledExtensions) or by using the enable/disable methods.

Example:

import { logger, consoleTransport } from "@zitterorg/harum-ratione";

const config = {
  transport: consoleTransport,
  enabledExtensions: ["ROOT", "HOME"],
};

var log = logger.createLogger(config);
var rootLog = log.extend("ROOT");
var homeLog = log.extend("HOME");
var profileLog = log.extend("PROFILE");

log.debug("print this"); // this will print "<time> | DEBUG | print this"
rootLog.debug("print this"); // this will print "<time> | ROOT | DEBUG | print this"
homeLog.debug("print this"); // this will print "<time> | HOME | DEBUG | print this"
profileLog.debug("not print this"); // this extension is not enabled

Methods

enable/disable

Dynamically enable/disable loggers and extensions, if it is called without parameters then it will disable/enable the whole logger:

import { logger, consoleTransport } from "@zitterorg/harum-ratione";

const config = {
  transport: consoleTransport,
  enabledExtensions: ["ROOT", "HOME"],
};

var log = logger.createLogger(config);
var rootLog = log.extend("ROOT");
var homeLog = log.extend("HOME");

log.info("print this"); // this will print "<time> | ROOT | INFO | print this"
homeLog.info("print this"); // extension is enabled

log.disable("HOME");

homeLog.info("not print this"); // extension is not enabled
rootLog.info("print this"); // extension is enabled

log.disable();

homeLog.info("not print this"); // logger is not enabled
rootLog.info("not print this"); // logger is not enabled
log.info("not print this"); // logger is not enabled

getExtensions

Get an array of currently created extensions.

setSeverity

You can set the severity level by passing the name(string) of the least important level you want to see. This method will overwrite any config.severity option set in logger creation.

var log = logger.createLogger();

log.setSeverity("info");
log.debug("This log will not be printed");
log.info("This log will be printed correctly");
log.error("This log will be printed correctly");

getSeverity

You can get the current severity level setted.

var log = logger.createLogger();

var defaultseverity = log.getSeverity(); // severity = debug
log.setSeverity("info");
var severity = log.getSeverity(); // severity = info
log.setSeverity("error");
var newseverity = log.getSeverity(); // newseverity = error

patchConsole

(Experimental)

With this method you are going to overwrite the default console, which is useful in case you are installing this package in an existing software where you use the default console.

Any levels you specify in configuration, if they exist, will be mapped to the console methods (console.log, console.info, console.error, etc...).

If you do not specify a log level in configuration then your first level will be mapped to it.

All calls to console.* will then be handled by your @zitterorg/harum-ratione logger and then you can manage their visibility via severity, or change their transports.

This method may have undesirable effects, so I recommend using it only if necessary.

import { logger, consoleTransport } from "@zitterorg/harum-ratione";

const config = {
  levels: {
    debug: 0,
    log: 1,
    warn: 2,
    error: 3,
  },
  severity: "log",
  printLevel: true,
  printDate: true,
};

var log = logger.createLogger(defaultConfig);

// this call will use default console
console.log("This method use console");

log.patchConsole();

// all of the following console calls will use your @zitterorg/harum-ratione logger
console.log("This method use your logger");
console.warn("This method use your logger too");
console.debug("this message will not be shown"); // severity is set to 'log'

Usage Tips

Logs only in development mode

In reacly-native, after you have create your logger, you can set to log only in development using the __DEV__ as follows:

import {
  logger,
  consoleTransport,
  fileAsyncTransport,
} from "@zitterorg/harum-ratione";
import RNFS from "react-native-fs";

const config = {
  transport: __DEV__ ? consoleTransport : fileAsyncTransport,
  severity: __DEV__ ? "debug" : "error",
  transportOptions: {
    colors
    FS: RNFS,
  },
};

var log = logger.createLogger();

This will block all the logs in production, but not the errors, so the app performance will not be affected. This will also change the transport: print to console in development and save to file in production.

Global logger in react-native

In order to have a global logger throughout the app, i recommend using a config.js file to initialize the logger so it can be imported wherever it is needed. Example:

//config.js
import {
  logger,
  consoleTransport,
  fileAsyncTransport,
} from "@zitterorg/harum-ratione";
import RNFS from "react-native-fs";

const config = {
  transport: __DEV__ ? consoleTransport : fileAsyncTransport,
  severity: __DEV__ ? "debug" : "error",
  transportOptions: {
    colors: {
      info: "blueBright",
      warn: "yellowBright",
      error: "redBright",
    },
    FS: RNFS,
  },
};

var LOG = logger.createLogger(config);

export { LOG };
//index.js and other app files
import { LOG } from "./config";

LOG.info("app log test");

To use extended loggers in all files you can also re-declare them:

//root.js
import { LOG } from "./config";
var log = LOG.extend("ROOT");

log.info("root log test");
//root2.js
import { LOG } from "./config";
var log = LOG.extend("ROOT");

log.info("root log test");
//home.js
import { LOG } from "./config";
var log = LOG.extend("HOME");

log.info("home log test");

Use multiple transports

To use multiple transports by passing it as an Array:

import {
  logger,
  consoleTransport,
  fileAsyncTransport,
  sentryTransport,
  transportFunctionType,
} from "@zitterorg/harum-ratione";
import RNFS from "react-native-fs";
import * as Sentry from "@sentry/react-native";

var customTransport: transportFunctionType = (props) => {
  // Do here whatever you want with the log message
  // Eg. a console log: console.log(props.level.text, props.msg)
};

const log = logger.createLogger({
  transport: [
    consoleTransport,
    fileAsyncTransport,
    sentryTransport,
    customTransport,
  ],
  transportOptions: {
    FS: RNFS,
    SENTRY: Sentry,
    colors: {
      info: "blueBright",
      warn: "yellowBright",
      error: "redBright",
    },
  },
});

Improve performance

In react-native you can improve performance by setting the InteractionManager.runAfterInteractions async function:

import { logger } from "@zitterorg/harum-ratione";

const InteractionManager = require("react-native").InteractionManager;

const log = logger.createLogger({
  async: true,
  asyncFunc: InteractionManager.runAfterInteractions,
});
efficientsliceMapcommand-linecodesoptimistfpsparentsapivarweakmapsnscallargsexecuteObject.definePropertyrdsspringcloudsearchpinovisualtransportcolumndeletetimebytegitignoreyuphasloadingasciiinternal slotproxyloggingcallbindcss-in-jsmkdirsfeedfilefunctionasyncUnderscorecommanderuser-streamssymbolreact posesomeupArray.prototype.containsjsonschemaFunction.prototype.namedeepstructuredCloneemrslotscheme-validationfunction.lengthclientresolvequeryYAMLdatestreamsutilitystoragegatewayUint8Arrayasteriskspackagestylesstableecmascripthigher-ordergesturesgenericsString.prototype.matchAlltypedarraysymbolscloudtrailestreeglobal object_.extendkeyscloudformationprogressimportquerystringtypeofdebugjson-schema-validationURLES3propertyconcatio-tsemitbeanstalkEStermajaxcall-boundansiomitsetPrototypeOfparserutilsobjmovedirectoryponyfillinstrumentationchailintreal-timefrombrowserlistincludesreact-hook-formschemeObject.assignvalues3dreversevarscircularwafdeepcloneexpressionpyyamlspeedexecwaapirgbfull-widthgetintrinsicperformantString.prototype.trimtrimLeftES7ramdamodulesroute53lookAsyncIteratorexpressconstbindcheckrulespackage managerArray.prototype.flattenfile systemreadrmdirrouteES2016toArraynativeoffsetSettoReversedTypeScripthasOwnPropertykarmanamesvaluedependency managertouchelectrondescriptorprototyperegexsetelbenderfullwidthtc39takecreateweaksetfindupglobalsObject.entriesclassnamesreactdefinePropertypushObject.valuesstyleframeworkreusemrutoobjectlastbyteLengthsetImmediateartassertionstreambannerBigUint64ArraycomparesimpledbequalECMAScript 2018windowjsdomeventDispatchersharedarraybuffertraverseenumerableMicrosoftmkdirpsymlinksmatchAllhelpersforEachkeytranspilersameValueZeroclicolordataViewCSSlruCSSStyleDeclarationrobustpicomatchistoolscensorclassnamesetterletextendposecloneprotobufjQuerydirworkspace:*subprocessdeepcopyenv
3.7.61

5 months ago

3.7.59

5 months ago

3.7.58

5 months ago

3.7.60

5 months ago

3.7.57

5 months ago

2.7.55

5 months ago

2.7.57

5 months ago

2.7.56

5 months ago

2.6.55

5 months ago

2.5.54

5 months ago

2.5.55

5 months ago

2.5.50

5 months ago

2.5.51

5 months ago

2.5.52

5 months ago

2.5.53

5 months ago

2.2.17

6 months ago

2.3.27

6 months ago

2.2.18

6 months ago

2.2.15

6 months ago

2.2.16

6 months ago

2.5.47

5 months ago

2.3.24

6 months ago

2.5.48

5 months ago

2.4.35

6 months ago

2.3.23

6 months ago

2.2.14

6 months ago

2.5.49

5 months ago

2.3.26

6 months ago

2.3.25

6 months ago

2.5.43

5 months ago

2.4.32

6 months ago

2.5.44

5 months ago

2.4.31

6 months ago

2.5.45

5 months ago

2.4.34

6 months ago

2.3.22

6 months ago

2.5.46

5 months ago

2.4.33

6 months ago

2.5.40

6 months ago

2.5.41

5 months ago

2.4.30

6 months ago

2.5.42

5 months ago

2.2.19

6 months ago

2.2.22

6 months ago

2.2.20

6 months ago

2.2.21

6 months ago

2.4.29

6 months ago

2.4.28

6 months ago

2.5.36

6 months ago

2.5.37

6 months ago

2.5.38

6 months ago

2.4.27

6 months ago

2.5.39

6 months ago

2.5.35

6 months ago

2.2.13

7 months ago

2.1.12

7 months ago

2.1.13

7 months ago

2.1.10

7 months ago

2.1.11

7 months ago

1.1.9

7 months ago

1.0.9

7 months ago

1.1.10

7 months ago

1.0.8

7 months ago

1.0.7

7 months ago

1.0.6

7 months ago

1.0.5

7 months ago

1.0.4

7 months ago

1.0.3

7 months ago

1.0.2

7 months ago

1.0.1

7 months ago

1.0.0

7 months ago