• Stars
    star
    107
  • Rank 313,708 (Top 7 %)
  • Language
    OCaml
  • Created over 5 years ago
  • Updated 8 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Logging implementation for ReScript

rescript-logger

npm version license

Logging implementation for ReScript.

rescript-logger

Features

  • Zero runtime in production builds.
  • Multiple logging levels.
  • Customizable verbosity.
  • [@log] helper.
  • @rescript/react integration.
  • Custom loggers.
  • Logging in libraries.

ShakaCode

If you are looking for help with the development and optimization of your project, ShakaCode can help you to take the reliability and performance of your app to the next level.

If you are a developer interested in working on ReScript / TypeScript / Rust / Ruby on Rails projects, we're hiring!

Installation

Get the package:

# yarn
yarn add rescript-logger
# or npm
npm install --save rescript-logger

Then add it to bsconfig.json:

"bs-dependencies": [
  "rescript-logger"
],
"ppx-flags": ["rescript-logger/ppx"]

Usage

There are 5 log levels:

  • trace
  • debug
  • info
  • warn
  • error

You can log message of specific level using appropriate macros:

// ReScript
%log.info("Info message")
%log.error("Error message")

// Reason
[%log.info "Info message"];
[%log.error "Error message"];

Additional data

You can add data to log entry like this:

// ReScript
%log.info(
  "Info message"
  ("Foo", 42)
)
%log.info(
  "Info message"
  ("Foo", {x: 42})
  ("Bar", [1, 2, 3])
)

// Reason
[%log.info "Info message"; ("Foo", 42)];
[%log.info
  "Info message";
  ("Foo", {x: 42});
  ("Bar", [1, 2, 3]);
];

Currently, logger can accept up to 7 additional entries.

Verbosity customization

Output verbosity can be customized by providing specific log level and/or code locations.

Log level

You can set maximum log level via environment variable RES_LOG.

Let's say you want to log only warnings and errors. To make it happen, run your build like this:

RES_LOG=warn rescript build

Available RES_LOG values:

  • *: log everything
  • trace: basically, the same as *
  • debug: log everything except trace level messages
  • info: log everything except trace & debug level messages
  • warn: log warn & error messages only
  • error: log error messages only
  • off: don't log anything

If RES_LOG is set to off, nothing will be logged and none of the log entries will appear in your JS assets.

In case if RES_LOG environment variable is not set, log level warn will be used.

Also, see Usage in libraries.

Code location

If you want to focus on logging from specific part(s) of your code, you can use RES_LOG_ONLY environment variable.

For example, if you want to see logs only from module Test, run the build as following:

RES_LOG_ONLY=Test rescript build

You can pass submodules and functions to it as well. If you want to log from multiple locations, separate them by ,.

Consider the following source:

// Test.res
%log.warn("Top level message")

module Submodule1 = {
  %log.warn("Message from Submodule1")
}

module Submodule2 = {
  %log.warn("Message from Submodule2")

  let fn = () => %log.warn("Message from function within Submodule2")
  fn()
}

Here is what will be logged with different build configurations:

# build
RES_LOG_ONLY=Test rescript build

# output
WARNING  [Test] Top level message
WARNING  [Test.Submodule1] Message from Submodule1
WARNING  [Test.Submodule2] Message from Submodule2
WARNING  [Test.Submodule2.fn] Message from function within Submodule2

# build
RES_LOG_ONLY=Test.Submodule2 rescript build

# output
WARNING  [Test.Submodule2] Message from Submodule2
WARNING  [Test.Submodule2.fn] Message from function within Submodule2

# build
RES_LOG_ONLY=Test.Submodule1,Test.Submodule2.fn rescript build

# output
WARNING  [Test.Submodule1] Message from Submodule1
WARNING  [Test.Submodule2.fn] Message from function within Submodule2

[@log] helper

This helper can be placed in front of any switch expression with constructor patterns and it will inject debug expressions into each branch.

// ReScript
let _ =
  x =>
    @log
    switch x {
    | A => "A"
    | B(b) => b
    }

// Reason
let _ =
  x =>
    [@log]
    switch (x) {
    | A => "A"
    | B(b) => b
    }

Without a @log helper, an equivalent would be:

// ReScript
let _ =
  x =>
    switch (x) {
    | A =>
      %log.debug("A")
      "A"
    | B(b) =>
      %log.debug("B with payload" ("b", b))
      b
    }

// Reason
let _ =
  x =>
    switch (x) {
    | A =>
      [%log.debug "A"];
      "A";
    | B(b) =>
      [%log.debug "B with payload"; ("b", b)];
      b;
    }

You can pass optional custom namespace to helper like this: @log("MyNamespace").

[@log] helper works only for switch expressions with constructor patterns, for now. Let us know in the issues if you need to handle more cases.

@rescript/react integration

Using @log helper, you can log dispatched actions in your components.

Annotate reducer function like this:

// ReScript
let reducer =
  (state, action) =>
    @log
    switch action {
      ...
    }

// Reason
let reducer =
  (state, action) =>
    [@log]
    switch (action) {
      ...
    }

These entries are logged on the debug level so none of it will appear in your production builds.

Custom loggers

rescript-logger ships with 3 loggers:

  • ReScriptLogger.Browser (default)
  • ReScriptLogger.Node
  • ReScriptLogger.Universal (picks either Browser or Node logger at runtime depending on an environment, useful for apps with SSR)

And you can easily plug your own.

For example, in development, you want to log everything to console using default logger, but in production, you want to disable console logging and send error level events to bug tracker.

To implement your own logger, you need to create a module (e.g. BugTracker.re) and set the following environment variables for production build.

RES_LOG=error
RES_LOGGER=BugTracker

Considering that you want to log only error level messages, you need to create functions only for errors logging.

// BugTracker.res

let error = (loc, msg) => BugTrackerSDK.notify(`${msg} in ${loc.rootModule}`)

let error1 =
  (
    loc,
    msg,
    (label, payload),
  ) =>
    BugTrackerSDK.notify(
      `${msg} in ${loc.rootModule}`,
      [|(label, payload)|],
    );

let error2 =
  (
    loc,
    msg,
    (label1, payload1),
    (label2, payload2),
  ) =>
    BugTrackerSDK.notify(
      `${msg} in ${loc.rootModule}`,
      [|
        (label1, payload1),
        (label2, payload2),
      |],
    );

// Up to 7

The first argument loc is a ReScriptLogger.Location.t record. It's passed by PPX and contains the location data.

type t = {
  rootModule: string,
  subModulePath: list<string>,
  value: option<string>,
  fullPath: string,
  filePath: string,
}

If Test.Submodule.fn gets called, logger would receive the following location:

// Test.res
module Submodule = {
  let fn = () => %log.warn("Warn!")
}

// Location
{
  rootModule: "Test",
  subModulePath: list{"Submodule"},
  value: Some("fn"),
  fullPath: "Test.Submodule.fn",
  filePath: "/absolute/path/to/project/src/Test.res",
}

Note, you don't have to re-implement all functions from the default logger, only the ones you actually use. Don't worry to forget to implement something. If later on, you will attempt to use unimplemented method it will be compile time error.

Usage in libraries

If you develop a library and want to use rescript-logger during development process, you can do so without spamming output of consumers of your library.

rescript-logger/ppx accepts --lib flag:

"ppx-flags": [
  ["rescript-logger/ppx", "--lib=my-lib"]
]

Once this flag is passed, you need to provide special value of RES_LOG to log your entries:

RES_LOG=my-lib=* rescript build

If consumers of your lib would like to see log output from your lib, they can do so too by extending a value of RES_LOG variable:

RES_LOG=*,my-lib=error rescript build

Few more examples to illustrate how it works:

# log everything from application code only
RES_LOG=* rescript build

# log everything from application code
# log errors from `my-lib`
RES_LOG=*,my-lib=error rescript build

# log everything from application code
# log errors from `my-lib-1`
# log warnings and errors from `my-lib-2`
RES_LOG=*,my-lib-1=error,my-lib-2=warn rescript build

Caveats

Logging is disabled after file save
If you run bsb via editor integration, make sure editor picked up RES_LOG variable. E.g. if you use Atom run it like this:

RES_LOG=info atom .

If your editor is telling you, variables used in ppx are unused, you can either:

  1. prefix such variables with _
  2. or open editor with RES_LOG variable set to appropriate level.

Changing value of RES_LOG/RES_LOGGER/RES_LOG_ONLY doesn't make any effect
When you change a value of environment variable, rescript clean before the next build.

Developing

Repo consists of 2 parts:

  • ReScript lib: dependencies are managed by yarn
  • OCaml PPX: dependencies are managed either by nix (in development) or esy (in development and/or on CI)

Nix flow

Clone repo and either enter the Nix shell:

nix-shell

Or use direnv and create .envrc file in the root directory of the project with the folowing content:

use nix

Then install deps:

yarn install

Build loggers and ppx:

dune build
cd lib && yarn run build
cd ../examples && yarn run build

Esy flow

Clone repo and install deps:

esy install
yarn install

Build loggers and ppx:

esy build
cd lib && yarn run build
cd ../examples && yarn run build

Auto-formatting

Note, this project doesn't use auto-formatting in OCaml files (*.ml), so if you're intended to contribute, please, turn off auto-formatting in the editor while editing such files.

Supporters

JetBrains ScoutAPM
BrowserStack Rails Autoscale Honeybadger

The following companies support our open source projects, and ShakaCode uses their products!

More Repositories

1

react_on_rails

Integration of React + Webpack + Rails + rails/webpacker including server-side rendering of React, enabling a better developer experience and faster client performance.
Ruby
5,026
star
2

react-webpack-rails-tutorial

Example of integration of Rails, react, redux, using the react_on_rails gem, webpack, enabling the es7 and jsx transpilers, and node integration. And React Native! Live Demo:
Ruby
1,710
star
3

bootstrap-loader

Load Bootstrap styles and scripts in your Webpack bundle
JavaScript
1,024
star
4

sass-resources-loader

SASS resources (e.g. variables, mixins etc.) loader for Webpack. Also works with less, post-css, etc.
JavaScript
978
star
5

cypress-on-rails

Use cypress.io or playwright.dev with your rails application
HTML
402
star
6

shakapacker

Use Webpack to manage app-like JavaScript modules in Rails
Ruby
375
star
7

fat-code-refactoring-techniques

Code samples for RailsConf 2014 on Fat Code Refactoring
JavaScript
288
star
8

re-formality

Form validation tool for reason-react
Reason
245
star
9

bootstrap-sass-loader

Webpack Loader for the Sass version Twitter Bootstrap
JavaScript
118
star
10

rescript-dnd

Drag-n-drop for @rescript/react
ReScript
115
star
11

rescript-classnames

Reimplementation of classnames in ReScript
ReScript
110
star
12

react_on_rails_demo_ssr_hmr

react_on_rails tutorial demonstrating SSR, HMR fast refresh, and Typescript based on the rails/webpacker webpack setup
Ruby
86
star
13

font-awesome-loader

Webpack Loader for Font Awesome Using Sass
JavaScript
47
star
14

webpacker_lite

Slimmed down version of Webpacker with only the asset helpers optimized for, but not requiring, React on Rails
Ruby
46
star
15

mirror-creator

One more way to create an object with values equal to its key names
JavaScript
43
star
16

rescript-debounce

Debounce for ReScript
ReScript
42
star
17

steward

Task runner and process manager for Rust
Rust
40
star
18

heroku-to-control-plane

The power of Kubernetes with the ease of Heroku! Our playbook for migrating from Heroku to Control Plane, controlplane.com, and CPL CLI source
Ruby
35
star
19

reactrails-react-native-client

This repository is for a react-native client to the https://www.reactrails.com/, source at https://github.com/shakacode/react-webpack-rails-tutorial/.
JavaScript
30
star
20

redux-tree

An alternative way to compose Redux reducers
JavaScript
23
star
21

rust-rescript-demo

Rust
22
star
22

messagebus

Message bus - enables async communication between different parts of application using messages
Rust
22
star
23

redux-interactions

Composing UI as a set of interactions
21
star
24

ssr-rs

Server-Side Rendering for Rust web servers using Node.js
Rust
20
star
25

bootstrap-sass-loader-example

Example of using the bootstrap-sass-loder to load the Sass version of Twitter Bootstrap using Webpack
JavaScript
16
star
26

style-guide-javascript

ShakaCode's JavaScript Style Guide (Also see our Ruby one: https://github.com/shakacode/style-guide-ruby)
JavaScript
15
star
27

jstreamer

Ruby
12
star
28

justerror

Extension to `thiserror` that helps reduce the amount of handwriting
Rust
12
star
29

react-validation-layer

An opinionated form validation tool for React apps
JavaScript
11
star
30

rescript-react-on-rails-example

Example of https://rescript-lang.org/ with React on Rails
Ruby
8
star
31

rescript-react-on-rails

BuckleScript bindings to react-on-rails
ReScript
8
star
32

todos-react-native-redux

Todos app with React Native and Redux
JavaScript
7
star
33

conform

Macro to transform struct string fields in place
Rust
7
star
34

rescript-first-class-modules-usage

CSS
6
star
35

rescript-throttle

Throttle for ReScript
ReScript
5
star
36

webpacker-examples

Ruby
5
star
37

package_json

Ruby
4
star
38

react_on_rails-with-webpacker

Prototype of webpacker integration
Ruby
4
star
39

use-ssr-computation.macro

A babel macro for reducing initial bundle-size of Apps with React SSR. Uses conditional compilation to make computations server-side and pass the results to the client.
TypeScript
4
star
40

react_on_rails-generator-results

Results of running different react_on_rails generators. See https://github.com/shakacode/react_on_rails
4
star
41

vosk-rs

Rust
3
star
42

react-starter-kit-with-bootstrap-loader

JavaScript
3
star
43

react_on_rails-generator-results-pre-0

Ruby
2
star
44

js-promises-testing

JavaScript
2
star
45

react-on-rails-demo

Example of using the generator for React on Rails
Ruby
2
star
46

uber_task

Ruby
2
star
47

node-webpack-async-example

Simple example of setting up node with webpack
JavaScript
2
star
48

egghead-add-redux-component-to-react-on-rails

Source for Egghead lesson: Add Redux State Management to a React on Rails Project
Ruby
1
star
49

reactrails-in-reagent

Test of converting reactrails example to reagent
1
star
50

rails-tutorial-with-react-on-rails

Adding React on Rails to the Rails Tutorial
Ruby
1
star
51

.github

1
star
52

react_on_rails-test-new-redux-generation

Testing out a different redux generation. See https://github.com/shakacode/react_on_rails/pull/584
Ruby
1
star
53

yardi

Yet Another Rust Dependency Injector
Rust
1
star
54

old-react-on-rails-examples

Various example apps for React on Rails, outdated
JavaScript
1
star
55

v8-demo

React on Rails v8 Usage of the basic generator and a "generator function example"
Ruby
1
star
56

docker-ci

docker container that runs all test and linting against app
Ruby
1
star
57

react-rails-webpacker-resources

1
star
58

sass-rel-paths-issue

JavaScript
1
star
59

reactrails-in-om-next-example

Test of converting reactrails example to om-next
1
star
60

homebrew-brew

Ruby
1
star
61

zone-helper

Swift
1
star
62

react_on_rails-update-webpack-v2

Updating the default React on Rails 6.6.0 redux installation to use Webpack v2 plus adding eslint.
Ruby
1
star
63

shakacode-website

Public website and blog
HTML
1
star
64

react-native-image-zoom-slider

JavaScript
1
star
65

derive-from-one

Automatically generates `From` impls so you don't have to
Rust
1
star
66

rescript-on-rails-example

A sample application using Rescript and React on Rails.
Ruby
1
star