From 06bd4a02192e4f76969c93bbef282c754e1c0e0a Mon Sep 17 00:00:00 2001 From: taskylizard <75871323+taskylizard@users.noreply.github.com> Date: Thu, 19 Dec 2024 18:21:55 +0000 Subject: [PATCH] chore: init --- .cargo/config.toml | 2 + .cargo/rustfmt.toml | 25 ++++ .github/workflows/CI.yml | 144 +++++++++++++++++++++++ .gitignore | 199 ++++++++++++++++++++++++++++++++ .npmignore | 13 +++ Cargo.toml | 29 +++++ build.rs | 5 + npm/darwin-x64/README.md | 3 + npm/darwin-x64/package.json | 20 ++++ npm/linux-x64-gnu/README.md | 3 + npm/linux-x64-gnu/package.json | 23 ++++ npm/win32-x64-msvc/README.md | 3 + npm/win32-x64-msvc/package.json | 20 ++++ package.json | 28 +++++ playground/test.ts | 12 ++ pnpm-lock.yaml | 24 ++++ src/lib.rs | 152 ++++++++++++++++++++++++ 17 files changed, 705 insertions(+) create mode 100644 .cargo/config.toml create mode 100644 .cargo/rustfmt.toml create mode 100644 .github/workflows/CI.yml create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 Cargo.toml create mode 100644 build.rs create mode 100644 npm/darwin-x64/README.md create mode 100644 npm/darwin-x64/package.json create mode 100644 npm/linux-x64-gnu/README.md create mode 100644 npm/linux-x64-gnu/package.json create mode 100644 npm/win32-x64-msvc/README.md create mode 100644 npm/win32-x64-msvc/package.json create mode 100644 package.json create mode 100644 playground/test.ts create mode 100644 pnpm-lock.yaml create mode 100644 src/lib.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..0c17df0 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[target.x86_64-pc-windows-msvc] +rustflags = ["-C", "target-feature=+crt-static"] \ No newline at end of file diff --git a/.cargo/rustfmt.toml b/.cargo/rustfmt.toml new file mode 100644 index 0000000..9cac707 --- /dev/null +++ b/.cargo/rustfmt.toml @@ -0,0 +1,25 @@ +# require the shorthand instead of it being optional +use_field_init_shorthand = true +newline_style = "unix" +max_width = 140 +chain_width = 100 +fn_call_width = 100 +# Format string literals where necessary +format_strings = true +# Format macro invocations +format_macro_matchers = true +# Reorder impl items +reorder_impl_items = true +# Format code inside macro declarations +format_macro_bodies = true +# Merge imports with the same prefix +imports_granularity = "Crate" +# Group imports +group_imports = "StdExternalCrate" +# Format type annotations +format_code_in_doc_comments = true +# Format doc comments +doc_comment_code_block_width = 100 +# Format comments +comment_width = 100 +wrap_comments = true diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..6746f11 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,144 @@ +name: CI +env: + DEBUG: napi:* + APP_NAME: logger + MACOSX_DEPLOYMENT_TARGET: "10.13" +permissions: + contents: write + id-token: write +"on": + push: + branches: + - main + tags-ignore: + - "**" + paths-ignore: + - "**/*.md" + - LICENSE + - "**/*.gitignore" + - .editorconfig + - docs/** + pull_request: null +jobs: + build: + strategy: + fail-fast: false + matrix: + settings: + - host: macos-latest + target: x86_64-apple-darwin + build: pnpm build --target x86_64-apple-darwin + - host: windows-latest + build: pnpm build --target x86_64-pc-windows-msvc + target: x86_64-pc-windows-msvc + - host: ubuntu-latest + target: x86_64-unknown-linux-gnu + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian + build: pnpm build --target x86_64-unknown-linux-gnu + name: stable - ${{ matrix.settings.target }} - node@20 + runs-on: ${{ matrix.settings.host }} + steps: + - uses: actions/checkout@v4 + - name: Install pnpm + uses: pnpm/action-setup@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + if: ${{ !matrix.settings.docker }} + with: + node-version: ${{ matrix.node-version }} + cache: "pnpm" + - name: Install + uses: dtolnay/rust-toolchain@stable + if: ${{ !matrix.settings.docker }} + with: + toolchain: stable + targets: ${{ matrix.settings.target }} + - name: Cache cargo + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + .cargo-cache + target/ + key: ${{ matrix.settings.target }}-cargo-${{ matrix.settings.host }} + - uses: goto-bus-stop/setup-zig@v2 + if: ${{ matrix.settings.target == 'armv7-unknown-linux-gnueabihf' || matrix.settings.target == 'armv7-unknown-linux-musleabihf' }} + with: + version: 0.13.0 + - name: Setup toolchain + run: ${{ matrix.settings.setup }} + if: ${{ matrix.settings.setup }} + shell: bash + - name: Setup node x86 + if: matrix.settings.target == 'i686-pc-windows-msvc' + run: pnpm config set supportedArchitectures.cpu "ia32" + shell: bash + - name: Install dependencies + run: pnpm install + - name: Setup node x86 + uses: actions/setup-node@v4 + if: matrix.settings.target == 'i686-pc-windows-msvc' + with: + node-version: 20 + cache: pnpm + architecture: x86 + - name: Build in docker + uses: addnab/docker-run-action@v3 + if: ${{ matrix.settings.docker }} + with: + image: ${{ matrix.settings.docker }} + options: "--user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache -v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}:/build -w /build" + run: ${{ matrix.settings.build }} + - name: Build + run: ${{ matrix.settings.build }} + if: ${{ !matrix.settings.docker }} + shell: bash + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: bindings-${{ matrix.settings.target }} + path: ${{ env.APP_NAME }}.*.node + if-no-files-found: error + publish: + name: Publish + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install pnpm + uses: pnpm/action-setup@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + if: ${{ !matrix.settings.docker }} + with: + node-version: ${{ matrix.node-version }} + cache: "pnpm" + - name: Install dependencies + run: pnpm install + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + - name: Move artifacts + run: pnpm artifacts + - name: List packages + run: ls -R ./npm + shell: bash + - name: Publish + run: | + npm config set provenance true + if git log -1 --pretty=%B | grep "^[0-9]\+\.[0-9]\+\.[0-9]\+$"; + then + echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc + npm publish --access public + elif git log -1 --pretty=%B | grep "^[0-9]\+\.[0-9]\+\.[0-9]\+"; + then + echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc + npm publish --tag next --access public + else + echo "Not a release, skipping publish" + fi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2a0ce1f --- /dev/null +++ b/.gitignore @@ -0,0 +1,199 @@ +# Created by https://www.toptal.com/developers/gitignore/api/node +# Edit at https://www.toptal.com/developers/gitignore?templates=node + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# End of https://www.toptal.com/developers/gitignore/api/node + +# Created by https://www.toptal.com/developers/gitignore/api/macos +# Edit at https://www.toptal.com/developers/gitignore?templates=macos + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +# End of https://www.toptal.com/developers/gitignore/api/macos + +# Created by https://www.toptal.com/developers/gitignore/api/windows +# Edit at https://www.toptal.com/developers/gitignore?templates=windows + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/windows + +#Added by cargo + +/target +Cargo.lock + +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions + +*.node +index.js +index.d.ts diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..ec144db --- /dev/null +++ b/.npmignore @@ -0,0 +1,13 @@ +target +Cargo.lock +.cargo +.github +npm +.eslintrc +.prettierignore +rustfmt.toml +yarn.lock +*.node +.yarn +__test__ +renovate.json diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..33b241e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,29 @@ +[package] +edition = "2021" +name = "tracesix" +version = "0.0.0" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix +napi = { version = "2.12.2", default-features = false, features = ["napi4"] } +napi-derive = "2.12.2" +once_cell = "1.20.2" +owo-colors = "4.1.0" +time = { version = "0.3.37", features = ["macros"] } +tracing = "0.1.41" +tracing-serde = "0.2.0" +tracing-subscriber = { version = "0.3.19", features = [ + "env-filter", + "json", + "time", +] } + +[build-dependencies] +napi-build = "2.0.1" + +[profile.release] +lto = true +strip = "symbols" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..9fc2367 --- /dev/null +++ b/build.rs @@ -0,0 +1,5 @@ +extern crate napi_build; + +fn main() { + napi_build::setup(); +} diff --git a/npm/darwin-x64/README.md b/npm/darwin-x64/README.md new file mode 100644 index 0000000..912667b --- /dev/null +++ b/npm/darwin-x64/README.md @@ -0,0 +1,3 @@ +# `@tracesix/logger-darwin-x64` + +This is the **x86_64-apple-darwin** binary for `@tracesix/logger` diff --git a/npm/darwin-x64/package.json b/npm/darwin-x64/package.json new file mode 100644 index 0000000..9ec0f4a --- /dev/null +++ b/npm/darwin-x64/package.json @@ -0,0 +1,20 @@ +{ + "name": "@tracesix/logger-darwin-x64", + "version": "0.0.1", + "os": [ + "darwin" + ], + "cpu": [ + "x64" + ], + "main": "logger.darwin-x64.node", + "files": [ + "logger.darwin-x64.node" + ], + "description": "Fastest native logging for services", + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "repository": "https://github.com/taskyland/tracesix" +} \ No newline at end of file diff --git a/npm/linux-x64-gnu/README.md b/npm/linux-x64-gnu/README.md new file mode 100644 index 0000000..7135c59 --- /dev/null +++ b/npm/linux-x64-gnu/README.md @@ -0,0 +1,3 @@ +# `@tracesix/logger-linux-x64-gnu` + +This is the **x86_64-unknown-linux-gnu** binary for `@tracesix/logger` diff --git a/npm/linux-x64-gnu/package.json b/npm/linux-x64-gnu/package.json new file mode 100644 index 0000000..6b27885 --- /dev/null +++ b/npm/linux-x64-gnu/package.json @@ -0,0 +1,23 @@ +{ + "name": "@tracesix/logger-linux-x64-gnu", + "version": "0.0.1", + "os": [ + "linux" + ], + "cpu": [ + "x64" + ], + "main": "logger.linux-x64-gnu.node", + "files": [ + "logger.linux-x64-gnu.node" + ], + "description": "Fastest native logging for services", + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "repository": "https://github.com/taskyland/tracesix", + "libc": [ + "glibc" + ] +} \ No newline at end of file diff --git a/npm/win32-x64-msvc/README.md b/npm/win32-x64-msvc/README.md new file mode 100644 index 0000000..87cd001 --- /dev/null +++ b/npm/win32-x64-msvc/README.md @@ -0,0 +1,3 @@ +# `@tracesix/logger-win32-x64-msvc` + +This is the **x86_64-pc-windows-msvc** binary for `@tracesix/logger` diff --git a/npm/win32-x64-msvc/package.json b/npm/win32-x64-msvc/package.json new file mode 100644 index 0000000..fcfa03f --- /dev/null +++ b/npm/win32-x64-msvc/package.json @@ -0,0 +1,20 @@ +{ + "name": "@tracesix/logger-win32-x64-msvc", + "version": "0.0.1", + "os": [ + "win32" + ], + "cpu": [ + "x64" + ], + "main": "logger.win32-x64-msvc.node", + "files": [ + "logger.win32-x64-msvc.node" + ], + "description": "Fastest native logging for services", + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "repository": "https://github.com/taskyland/tracesix" +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..d0068e2 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "@tracesix/logger", + "version": "0.0.1", + "main": "index.js", + "types": "index.d.ts", + "napi": { + "name": "logger", + "triples": {} + }, + "license": "MIT", + "devDependencies": { + "@napi-rs/cli": "^2.18.4" + }, + "engines": { + "node": ">= 10" + }, + "scripts": { + "artifacts": "napi artifacts", + "build": "napi build --platform --release", + "build:debug": "napi build --platform", + "prepublishOnly": "napi prepublish -t npm", + "universal": "napi universal", + "version": "napi version" + }, + "repository": "https://github.com/taskyland/tracesix", + "description": "Fastest native logging for services", + "packageManager": "pnpm@9.15.0+sha512.76e2379760a4328ec4415815bcd6628dee727af3779aaa4c914e3944156c4299921a89f976381ee107d41f12cfa4b66681ca9c718f0668fa0831ed4c6d8ba56c" +} diff --git a/playground/test.ts b/playground/test.ts new file mode 100644 index 0000000..a26ddfb --- /dev/null +++ b/playground/test.ts @@ -0,0 +1,12 @@ +import { Logger } from "./index"; + +const logger = new Logger("new", { + level: "debug", +}); + +for (let i = 0; i < 10; i++) { + logger.info("info"); + logger.warn("warn"); + logger.error(new Error("error").toString()); + logger.debug("debug"); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..3296290 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,24 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@napi-rs/cli': + specifier: ^2.18.4 + version: 2.18.4 + +packages: + + '@napi-rs/cli@2.18.4': + resolution: {integrity: sha512-SgJeA4df9DE2iAEpr3M2H0OKl/yjtg1BnRI5/JyowS71tUWhrfSu2LT0V3vlHET+g1hBVlrO60PmEXwUEKp8Mg==} + engines: {node: '>= 10'} + hasBin: true + +snapshots: + + '@napi-rs/cli@2.18.4': {} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..85314df --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,152 @@ +use napi_derive::napi; +use once_cell::sync::OnceCell; +use owo_colors::OwoColorize; +use time::macros::format_description; +use tracing::Level; +use tracing_subscriber::{ + fmt::{format::Writer, FmtContext, FormatEvent, FormatFields}, + prelude::*, + registry::LookupSpan, + EnvFilter, +}; + +static LOGGER_INIT: OnceCell<()> = OnceCell::new(); + +/// Configuration options for the logger +/// @param {string} [level] - Log level (trace, debug, info, warn, error). Defaults to "info" +/// @param {boolean} [json] - Whether to output logs in JSON format. Defaults to false +#[napi(object)] +pub struct LoggerOptions { + pub level: Option, + pub json: Option, +} + +#[napi] +pub struct Logger {} + +struct CustomFormat { + service_name: String, +} + +impl FormatEvent for CustomFormat +where + S: tracing::Subscriber + for<'a> LookupSpan<'a>, + N: for<'a> FormatFields<'a> + 'static, +{ + fn format_event( + &self, + ctx: &FmtContext<'_, S, N>, + mut writer: Writer<'_>, + event: &tracing::Event<'_>, + ) -> std::fmt::Result { + let now = time::OffsetDateTime::now_utc(); + let date = now + .format(format_description!( + "[day]-[month]-[year] [hour]:[minute]:[second]" + )) + .unwrap(); + + let level = event.metadata().level(); + + write!( + writer, + "{}{}{}", + "[".bright_black(), + date.bright_blue(), + "]".bright_black() + )?; + + write!( + writer, + "{}{}{}", + "(".bright_black(), + self.service_name.bright_magenta(), + ")".bright_black() + )?; + + write!( + writer, + "{}{}{}", + "[".bright_black(), + match *level { + Level::ERROR => "ERROR".to_string().red().to_string(), + Level::WARN => "WARN".to_string().yellow().to_string(), + Level::INFO => "INFO".to_string().cyan().to_string(), + Level::DEBUG => "DEBUG".to_string().green().to_string(), + Level::TRACE => "TRACE".to_string().purple().to_string(), + }, + "]".bright_black() + )?; + + writer.write_char(' ')?; + ctx.field_format().format_fields(writer.by_ref(), event)?; + writeln!(writer) + } +} + +#[napi] +impl Logger { + /// Creates a new logger instance + /// @param {string} name - The name of the service that will be displayed in logs + /// @param {LoggerOptions} [options] - Configuration options for the logger + /// @returns {Logger} A new Logger instance + #[napi(constructor)] + pub fn new(name: String, options: Option) -> Self { + // Initialize the global logger only once + LOGGER_INIT.get_or_init(|| { + let opts = options.unwrap_or(LoggerOptions { + level: Some("info".to_string()), + json: Some(false), + }); + + let level = opts.level.unwrap_or_else(|| "info".to_string()); + + let format = CustomFormat { + service_name: name.clone(), + }; + + let filter_layer = EnvFilter::try_from_default_env() + .or_else(|_| EnvFilter::try_new(&level)) + .unwrap(); + + let fmt_layer = tracing_subscriber::fmt::layer().event_format(format); + + let subscriber = tracing_subscriber::registry() + .with(filter_layer) + .with(fmt_layer); + + tracing::subscriber::set_global_default(subscriber) + .expect("Failed to set tracing subscriber"); + }); + + Self {} + } + + /// Logs a debug message + /// @param {string} message - The message to log at debug level + #[napi] + pub fn debug(&self, message: String) { + tracing::debug!("{}", message); + } + + /// Logs an info message + /// @param {string} message - The message to log at info level + #[napi] + pub fn info(&self, message: String) { + tracing::info!("{}", message); + } + + /// Logs a warning message + /// @param {string} message - The message to log at warn level + #[napi] + pub fn warn(&self, message: String) { + tracing::warn!("{}", message); + } + + /// Logs an error message + /// @param {string} message - The message to log at error level + #[napi] + pub fn error(&self, message: String) { + tracing::error!("{}", message); + } +}