diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..ed7f481 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,92 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + schedule: + - cron: '30 6 * * 1' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: javascript-typescript + build-mode: none + # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 81056d8..d7ee200 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,16 +1,33 @@ -# Reusable workflow for releases; to eject, you can replace this file with -# https://github.com/ryansonshine/ryansonshine/blob/main/.github/workflows/release.yml name: Release on: - push: - branches: - - main + workflow_call: + secrets: + NPM_TOKEN: + required: true + jobs: release: - permissions: - contents: write - issues: write - pull-requests: write - uses: ryansonshine/ryansonshine/.github/workflows/release.yml@main - secrets: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: lts/* + - name: Install dependencies + run: npm ci + - name: Build + run: npm run build + - name: Test + run: npm test + - name: Release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + run: npx semantic-release + - name: Upload Code Coverage + uses: codecov/codecov-action@v3.1.0 diff --git a/README.md b/README.md index bd6b832..d6fcad0 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # @tincre/logger -[![npm package][npm-img]][npm-url] -[![Build Status][build-img]][build-url] -[![Downloads][downloads-img]][downloads-url] -[![Issues][issues-img]][issues-url] -[![Code Coverage][codecov-img]][codecov-url] -[![Commitizen Friendly][commitizen-img]][commitizen-url] +[![npm package][npm-img]][npm-url] +[![Build Status][build-img]][build-url] +[![Downloads][downloads-img]][downloads-url] +[![Issues][issues-img]][issues-url] +[![Code Coverage][codecov-img]][codecov-url] +[![Commitizen Friendly][commitizen-img]][commitizen-url] [![Semantic Release][semantic-release-img]][semantic-release-url] ## Install @@ -19,42 +19,107 @@ npm install @tincre/logger ```ts import { logger } from '@tincre/logger'; -logger.debug('hello'); -//=> 'hello' in logging output +logger.debug('Debugging message'); +logger.log('Application started'); +logger.warn('This is a warning'); +logger.error('Unexpected error occurred', new Error('Something went wrong')); ``` ## API -### myPackage(input, options?) +### `logger.log(message, data?)` -#### input +Logs a standard message to the console if `NODE_ENV` is **not** set to `production`. -Type: `string` +#### Parameters: -Lorem ipsum. +- `message`: **string** - The main log message. +- `data` (optional): **unknown** - Additional data (object, array, string, etc.). -#### options +#### Example: -Type: `object` +```ts +logger.log('User logged in', { userId: 123 }); +``` + +--- + +### `logger.error(message, data?)` + +Logs an error message to the console if `NODE_ENV` is **not** set to `production`. + +#### Parameters: + +- `message`: **string** - The error message. +- `data` (optional): **unknown** - Additional error details. + +#### Example: + +```ts +logger.error('Database connection failed', new Error('Connection timeout')); +``` + +--- + +### `logger.warn(message, data?)` + +Logs a warning message to the console if `NODE_ENV` is **not** set to `production`. + +#### Parameters: + +- `message`: **string** - The warning message. +- `data` (optional): **unknown** - Additional details. + +#### Example: + +```ts +logger.warn('Low disk space', { availableGB: 2 }); +``` + +--- + +### `logger.debug(message, data?)` + +Logs a debug message to the console if `NODE_ENV` is **not** set to `production`. + +#### Parameters: + +- `message`: **string** - The debug message. +- `data` (optional): **unknown** - Additional debug info. + +#### Example: + +```ts +logger.debug('Fetching API data', { endpoint: '/users', method: 'GET' }); +``` + +--- + +## Environment Variables + +| Variable | Description | +| ---------- | -------------------------------------------------------------------- | +| `NODE_ENV` | Set to `"production"` to disable logging in production environments. | + +--- -##### postfix +## Contributing -Type: `string` -Default: `rainbows` +We welcome contributions! Please follow our commit guidelines and open issues if you encounter any problems. -Lorem ipsum. +--- -[build-img]:https://github.com/ryansonshine/typescript-npm-package-template/actions/workflows/release.yml/badge.svg -[build-url]:https://github.com/ryansonshine/typescript-npm-package-template/actions/workflows/release.yml -[downloads-img]:https://img.shields.io/npm/dt/typescript-npm-package-template -[downloads-url]:https://www.npmtrends.com/typescript-npm-package-template -[npm-img]:https://img.shields.io/npm/v/typescript-npm-package-template -[npm-url]:https://www.npmjs.com/package/typescript-npm-package-template -[issues-img]:https://img.shields.io/github/issues/ryansonshine/typescript-npm-package-template -[issues-url]:https://github.com/ryansonshine/typescript-npm-package-template/issues -[codecov-img]:https://codecov.io/gh/ryansonshine/typescript-npm-package-template/branch/main/graph/badge.svg -[codecov-url]:https://codecov.io/gh/ryansonshine/typescript-npm-package-template -[semantic-release-img]:https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg -[semantic-release-url]:https://github.com/semantic-release/semantic-release -[commitizen-img]:https://img.shields.io/badge/commitizen-friendly-brightgreen.svg -[commitizen-url]:http://commitizen.github.io/cz-cli/ +[build-img]: https://github.com/Tincre/logger/actions/workflows/release.yml/badge.svg +[build-url]: https://github.com/Tincre/logger/actions/workflows/release.yml +[downloads-img]: https://img.shields.io/npm/dt/@tincre/logger +[downloads-url]: https://www.npmtrends.com/@tincre/logger +[npm-img]: https://img.shields.io/npm/v/@tincre/logger +[npm-url]: https://www.npmjs.com/package/@tincre/logger +[issues-img]: https://img.shields.io/github/issues/Tincre/logger +[issues-url]: https://github.com/Tincre/logger/issues +[codecov-img]: https://codecov.io/gh/Tincre/logger/branch/main/graph/badge.svg +[codecov-url]: https://codecov.io/gh/Tincre/logger +[semantic-release-img]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg +[semantic-release-url]: https://github.com/semantic-release/semantic-release +[commitizen-img]: https://img.shields.io/badge/commitizen-friendly-brightgreen.svg +[commitizen-url]: http://commitizen.github.io/cz-cli/ diff --git a/package.json b/package.json index 0a4f57f..18eb960 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@tincre/logger", - "version": "0.0.0-development", + "version": "0.0.5", "description": "A template for creating npm packages using TypeScript and VSCode", "main": "./lib/index.js", "files": [ diff --git a/src/index.ts b/src/index.ts index 6c6c283..5ac5709 100644 --- a/src/index.ts +++ b/src/index.ts @@ -28,67 +28,56 @@ * Each log message is prefixed with a timestamp and a label indicating the log level. * * Functions: - * - `logMessage(message: string): void`: Logs a standard message. - * - `errorMessage(message: string): void`: Logs an error message. - * - `warnMessage(message: string): void`: Logs a warning message. - * - `debugMessage(message: string): void`: Logs a debug message. + * - `logMessage(message: string, data?: unknown): void`: Logs a standard message. + * - `errorMessage(message: string, data?: unknown): void`: Logs an error message. + * - `warnMessage(message: string, data?: unknown): void`: Logs a warning message. + * - `debugMessage(message: string, data?: unknown): void`: Logs a debug message. * * The logger object aggregates these functions for easy access. */ +type LoggerFunction = (message: string, data?: unknown) => void; -export const logger = { +export const logger: Record< + 'debug' | 'log' | 'error' | 'warn', + LoggerFunction +> = { debug: debugMessage, log: logMessage, error: errorMessage, warn: warnMessage, }; -/** - * Logs a message to the console if the NODE_ENV is not set to "production". - * - * @param message - The message to be logged. - * @returns {void} - */ -function logMessage(message: string): void { + +function formatMessage(level: string, message: string, data?: unknown): string { + const timestamp = new Date().toISOString(); + const formattedData = + data !== undefined + ? typeof data === 'object' + ? ` ${JSON.stringify(data)}` + : ` ${String(data)}` + : ''; + return `[${level.toUpperCase()}] ${timestamp}: ${message}${formattedData}`; +} + +function logMessage(message: string, data?: unknown): void { if (process.env.NODE_ENV !== 'production') { - console.log(`[LOG] ${new Date().toISOString()}: ${message}`); + console.log(formatMessage('LOG', message, data)); } } -/** - * Logs an error message to the console if the NODE_ENV is not set to "production". - * - * @param message - The error message to be logged. - * @returns {void} - * - * @example - * errorMessage("Failed to connect to the database"); - */ -function errorMessage(message: string): void { +function errorMessage(message: string, data?: unknown): void { if (process.env.NODE_ENV !== 'production') { - console.error(`[ERROR] ${new Date().toISOString()}: ${message}`); + console.error(formatMessage('ERROR', message, data)); } } -/** - * Logs a warning message to the console if the NODE_ENV is not set to "production". - * - * @param message - The warning message to be logged. - * @returns {void} - */ -function warnMessage(message: string): void { +function warnMessage(message: string, data?: unknown): void { if (process.env.NODE_ENV !== 'production') { - console.warn(`[WARN] ${new Date().toISOString()}: ${message}`); + console.warn(formatMessage('WARN', message, data)); } } -/** - * Logs a debug message to the console if the NODE_ENV is not set to "production". - * - * @param message - The debug message to be logged. - * @returns {void} - */ -function debugMessage(message: string): void { +function debugMessage(message: string, data?: unknown): void { if (process.env.NODE_ENV !== 'production') { - console.debug(`[DEBUG] ${new Date().toISOString()}: ${message}`); + console.debug(formatMessage('DEBUG', message, data)); } } diff --git a/test/index.spec.ts b/test/index.spec.ts index f69d7a2..95e9595 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -2,16 +2,37 @@ import { logger } from '../src/index'; describe('Logger', () => { let consoleLogSpy: jest.SpyInstance; + let consoleWarnSpy: jest.SpyInstance; + let consoleErrorSpy: jest.SpyInstance; + let consoleDebugSpy: jest.SpyInstance; beforeEach(() => { consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(); + consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(); + consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); + consoleDebugSpy = jest.spyOn(console, 'debug').mockImplementation(); }); afterEach(() => { consoleLogSpy.mockRestore(); + consoleWarnSpy.mockRestore(); + consoleErrorSpy.mockRestore(); + consoleDebugSpy.mockRestore(); }); - it("should log a message when NODE_ENV is not 'production'", () => { + it("should log a message with an optional parameter when NODE_ENV is not 'production'", () => { + process.env.NODE_ENV = 'development'; + const message = 'Test log message'; + const additionalParam = { key: 'value' }; + + logger.log(message, additionalParam); + + expect(consoleLogSpy).toHaveBeenCalledWith( + expect.stringMatching(/\[LOG\] .*: Test log message \{"key":"value"\}/) + ); + }); + + it("should log a message without an optional parameter when NODE_ENV is not 'production'", () => { process.env.NODE_ENV = 'development'; const message = 'Test log message'; @@ -22,12 +43,55 @@ describe('Logger', () => { ); }); - it("should not log a message when NODE_ENV is 'production'", () => { + it("should warn a message with an optional parameter when NODE_ENV is not 'production'", () => { + process.env.NODE_ENV = 'development'; + const message = 'Test warn message'; + const additionalParam = [1, 2, 3]; + + logger.warn(message, additionalParam); + + expect(consoleWarnSpy).toHaveBeenCalledWith( + expect.stringMatching(/\[WARN\] .*: Test warn message \[1,2,3\]/) + ); + }); + + it("should error a message with an optional parameter when NODE_ENV is not 'production'", () => { + process.env.NODE_ENV = 'development'; + const message = 'Test error message'; + const additionalParam = 'extra details'; + + logger.error(message, additionalParam); + + expect(consoleErrorSpy).toHaveBeenCalledWith( + expect.stringMatching(/\[ERROR\] .*: Test error message extra details/) + ); + }); + + it("should debug a message with an optional parameter when NODE_ENV is not 'production'", () => { + process.env.NODE_ENV = 'development'; + const message = 'Test debug message'; + const additionalParam = { debug: true }; + + logger.debug(message, additionalParam); + + expect(consoleDebugSpy).toHaveBeenCalledWith( + expect.stringMatching(/\[DEBUG\] .*: Test debug message \{"debug":true\}/) + ); + }); + + it("should not log, warn, error, or debug a message when NODE_ENV is 'production'", () => { process.env.NODE_ENV = 'production'; - const message = 'Test log message'; + const message = 'Test message'; + const additionalParam = { ignored: true }; - logger.log(message); + logger.log(message, additionalParam); + logger.warn(message, additionalParam); + logger.error(message, additionalParam); + logger.debug(message, additionalParam); expect(consoleLogSpy).not.toHaveBeenCalled(); + expect(consoleWarnSpy).not.toHaveBeenCalled(); + expect(consoleErrorSpy).not.toHaveBeenCalled(); + expect(consoleDebugSpy).not.toHaveBeenCalled(); }); });