Skip to content

Commit

Permalink
Logging example
Browse files Browse the repository at this point in the history
  • Loading branch information
kjvalencik committed Jan 19, 2022
1 parent 06c505d commit 9a7ef6f
Show file tree
Hide file tree
Showing 11 changed files with 458 additions and 0 deletions.
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ members = [
"examples/cpu-count",
"examples/gzip-stream",
"examples/hello-world",
"examples/logging",
"examples/tokio-fetch",
]

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ All examples are for [`napi-backend`][napi-migration]. For examples using `legac
| [`cpu-count`][cpu-count] | Return the number of CPUs |
| [`gzip-stream`][gzip-stream] | Asynchronously compress a stream of data |
| [`hello-world`][hello-world] | Return a JS String with a greeting message |
| [`logging`][logging] | Connects Rust logging to Node.js logging |
| [`tokio-fetch`][tokio-fetch] | Asynchronously fetch a node release date |

[async-sqlite]: examples/async-sqlite
[cpu-count]: examples/cpu-count
[gzip-stream]: examples/gzip-stream
[hello-world]: examples/hello-world
[logging]: examples/logging
[tokio-fetch]: examples/tokio-fetch

## Contributing
Expand Down
18 changes: 18 additions & 0 deletions examples/logging/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "logging"
version = "0.1.0"
description = "Neon Logging Example"
license = "MIT"
edition = "2018"
exclude = ["index.node"]

[lib]
crate-type = ["cdylib"]

[dependencies]
log = "0.4"

[dependencies.neon]
version = "0.10.0-alpha.3"
default-features = false
features = ["napi-6", "channel-api"]
62 changes: 62 additions & 0 deletions examples/logging/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Logging

The logging example connects the Rust [`log`][log] crate to the Node [`debug`][debug] module. Neon modules can write logs directly to `stdout` with typical crates like [`env_logger`][env-logger], but connecting logging to [`debug`][debug] more seamlessly integrates with the Node ecosystem.

## Usage

```sh
# Only `INFO` logs on the top level crate
DEBUG="INFO:logging" npm start

# All logs on the top level crate
DEBUG="*:logging" npm start

# All `INFO` logs on any Rust crate
DEBUG="INFO:*" npm start

# All `WARN` and higher logs in our crate
DEBUG="WARN:logging,ERROR:logging"
```

## Libraries

### [`log`][log] crate

The [`log`][log] crate provides a logging facade used throughout the Rust ecosystem. It provides convenient macros for logging, but does not provide any facility to write or display logs.

### [`debug`][debug] module

the [`debug`][debug] node module provides a decorated version of `console.error` and is used throughout the Node library ecosystem, including in the [`express`][express] HTTP framework. It allows configurable log filtering with the `DEBUG` environment variable.

## Design

Rust code uses the typical logging facilities, but in order for it to be used, the [`Log`][log-trait] must be implemented. This example provides a simple [`Log`][log-trait] implementation that delegates to the [`debug`][debug] node module.

### Initialization

At initialization, the module calls `global.require("debug")` to get a copy of the function used to create logger instances. This function, as well as `enabled` and a [`Channel`][channel] are used to create a `Logger` instance and initialize the `log` crate.

### Loggers

The `Logger` struct maintains a map of logger names to logger instances that are lazily created as needed. Each logger is in an `Option` with `None` representing the disabled state. If an entry is missing, it is assumed to be in the `enabled` state until it can be further evaluated.

## Limitations

### Levels

The provided implementation does not understand logger levels. Each level needs to be enabled individually. As an improvement, the logger level could be determined by checking `debug.enabled(...)` for each level from lowest to highest.

### Multiple Contexts

The `log` crate only supports a single global logger instance in a process. If the module is initialized multiple times with Web Workers, all logs will be sent to the instance that initialized.

### Runtime level changes

The `debug` module supports enabling and disabling logging at runtime, but for efficiency, our `Logging` implementation assumes that the result of `debug.enabled(..)` never changes.

[log]: https://crates.io/crates/log
[log-trait]: https://docs.rs/log/latest/log/trait.Log.html
[debug]: https://www.npmjs.com/package/debug
[env-logger]: https://crates.io/crates/env-logger
[express]: https://www.npmjs.com/package/express
[channel]: https://docs.rs/neon/latest/neon/event/struct.Channel.html
71 changes: 71 additions & 0 deletions examples/logging/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 31 additions & 0 deletions examples/logging/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "logging",
"version": "0.1.0",
"description": "Neon Logging Example",
"main": "index.node",
"scripts": {
"build": "cargo-cp-artifact -nc index.node -- cargo build --message-format=json-render-diagnostics",
"install": "npm run build",
"start": "node run",
"test": "cargo test"
},
"license": "MIT",
"devDependencies": {
"cargo-cp-artifact": "^0.1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/neon-bindings/examples.git"
},
"keywords": [
"Neon",
"Examples"
],
"bugs": {
"url": "https://github.com/neon-bindings/examples/issues"
},
"homepage": "https://github.com/neon-bindings/examples#readme",
"dependencies": {
"debug": "^4.3.3"
}
}
13 changes: 13 additions & 0 deletions examples/logging/run.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"use strict";

const debug = require("debug");
const { hello, init } = require(".");

// Must be called to initialized logging
init(debug);

// Call an example function
hello();

// Give logs a chance to flush
setTimeout(() => {}, 500);
35 changes: 35 additions & 0 deletions examples/logging/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use neon::prelude::*;

use crate::logger::Logger;

mod logger;

// `init(debug)` must be called before using any other functionality.
//
// An exported initialization function is a common pattern in Neon. Since
// Node-API does not expose `require`, a JavaScript wrapper requires the
// `debug` module and passes it to `init` where logging is initialized.
fn init(mut cx: FunctionContext) -> JsResult<JsUndefined> {
let debug = cx.argument::<JsFunction>(0)?;

Logger::init(&mut cx, debug)?;

log::info!("Module initialized");

Ok(cx.undefined())
}

// Example function with logging
fn hello(mut cx: FunctionContext) -> JsResult<JsString> {
log::trace!("Called `hello` function");

Ok(cx.string("hello node"))
}

#[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> {
cx.export_function("init", init)?;
cx.export_function("hello", hello)?;

Ok(())
}
Loading

0 comments on commit 9a7ef6f

Please sign in to comment.