mirror of
https://github.com/goauthentik/authentik.git
synced 2026-06-17 19:09:11 +03:00
web: Packagify Logger (#20541)
* Prep logger for use outside web workspace. * Bump. Prep. * Add to publish list. * Update deps. * Add package directory.
This commit is contained in:
@@ -29,6 +29,7 @@ jobs:
|
||||
- packages/eslint-config
|
||||
- packages/prettier-config
|
||||
- packages/docusaurus-config
|
||||
- packages/logger-js
|
||||
- packages/esbuild-plugin-live-reload
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
|
||||
@@ -34,6 +34,7 @@ packages/docusaurus-config @goauthentik/frontend
|
||||
packages/esbuild-plugin-live-reload @goauthentik/frontend
|
||||
packages/eslint-config @goauthentik/frontend
|
||||
packages/prettier-config @goauthentik/frontend
|
||||
packages/logger-js @goauthentik/frontend
|
||||
packages/tsconfig @goauthentik/frontend
|
||||
# Web
|
||||
web/ @goauthentik/frontend
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
README.md
|
||||
node_modules
|
||||
_media
|
||||
!.github/README.md
|
||||
@@ -0,0 +1,13 @@
|
||||
# Prettier Ignorefile
|
||||
|
||||
## Node
|
||||
node_modules
|
||||
|
||||
## Static Files
|
||||
**/LICENSE
|
||||
./README.md
|
||||
|
||||
## Build asset directories
|
||||
coverage
|
||||
dist
|
||||
out
|
||||
@@ -0,0 +1,18 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2025 Authentik Security, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* @file ESLint Configuration
|
||||
*
|
||||
* @import { Config } from "eslint/config";
|
||||
*/
|
||||
|
||||
import { createESLintPackageConfig } from "@goauthentik/eslint-config";
|
||||
|
||||
import { defineConfig } from "eslint/config";
|
||||
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @type {Config[]}
|
||||
*/
|
||||
const eslintConfig = defineConfig(
|
||||
createESLintPackageConfig({
|
||||
parserOptions: {
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
}),
|
||||
{
|
||||
rules: {
|
||||
"no-console": "off",
|
||||
},
|
||||
files: ["shared/**"],
|
||||
},
|
||||
);
|
||||
|
||||
export default eslintConfig;
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./lib/browser.js";
|
||||
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* @file Console logger for browser environments.
|
||||
*
|
||||
* @remarks
|
||||
* The repetition of log levels, typedefs, and method signatures is intentional
|
||||
* to give IDEs and type checkers a mapping of log methods to the TypeScript
|
||||
* provided JSDoc comments.
|
||||
*
|
||||
* Additionally, no wrapper functions are used to avoid the browser's console
|
||||
* reported call site being the wrapper instead of the actual caller.
|
||||
*
|
||||
* @import { IConsoleLogger } from "./shared.js"
|
||||
* @import { Logger } from "pino"
|
||||
*/
|
||||
|
||||
import { LogLevelLabel, LogLevels } from "./shared.js";
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
//#region Constants
|
||||
|
||||
/**
|
||||
* Colors for log levels in the browser console.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The colors are derived from Carbon Design System's palette to ensure
|
||||
* sufficient contrast and accessibility across light and dark themes.
|
||||
*/
|
||||
const LogLevelColors = /** @type {const} */ ({
|
||||
info: `light-dark(#0043CE, #4589FF)`,
|
||||
warn: `light-dark(#F1C21B, #F1C21B)`,
|
||||
error: `light-dark(#DA1E28, #FA4D56)`,
|
||||
debug: `light-dark(#8A3FFC, #A56EFF)`,
|
||||
trace: `light-dark(#8A3FFC, #A56EFF)`,
|
||||
fatal: `light-dark(#DA1E28, #FA4D56)`,
|
||||
});
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Functions
|
||||
|
||||
/**
|
||||
* Creates a logger with the given prefix.
|
||||
*
|
||||
* @param {string} [prefix]
|
||||
* @param {...string} args
|
||||
* @returns {Logger}
|
||||
*
|
||||
*/
|
||||
export function createLogger(prefix, ...args) {
|
||||
const msgPrefix = prefix ? `(${prefix}):` : ":";
|
||||
|
||||
/**
|
||||
* @type {Partial<Logger>}
|
||||
*/
|
||||
const logger = {
|
||||
msgPrefix,
|
||||
};
|
||||
|
||||
for (const level of LogLevels) {
|
||||
const label = LogLevelLabel[level];
|
||||
const color = LogLevelColors[level];
|
||||
|
||||
// @ts-expect-error Alias the log method to the appropriate console method,
|
||||
// defaulting to console.log if the level is not supported.
|
||||
const method = level in console ? console[level] : console.log;
|
||||
|
||||
logger[level] = method.bind(
|
||||
console,
|
||||
`%c${label}%c ${msgPrefix}%c`,
|
||||
`font-weight: 700; color: ${color};`,
|
||||
`font-weight: 600; color: CanvasText;`,
|
||||
"",
|
||||
...args,
|
||||
);
|
||||
}
|
||||
|
||||
return /** @type {Logger} */ (logger);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Console Logger
|
||||
|
||||
/**
|
||||
* A singleton logger instance for the browser.
|
||||
*
|
||||
* ```js
|
||||
* import { ConsoleLogger } from "#logger/browser";
|
||||
*
|
||||
* ConsoleLogger.info("Hello, world!");
|
||||
* ```
|
||||
*
|
||||
* @implements {IConsoleLogger}
|
||||
* @runtime browser
|
||||
*/
|
||||
// @ts-expect-error Logging properties are dynamically assigned.
|
||||
export class ConsoleLogger {
|
||||
/** @type {typeof console.info} */
|
||||
static info;
|
||||
/** @type {typeof console.warn} */
|
||||
static warn;
|
||||
/** @type {typeof console.error} */
|
||||
static error;
|
||||
/** @type {typeof console.debug} */
|
||||
static debug;
|
||||
/** @type {typeof console.trace} */
|
||||
static trace;
|
||||
|
||||
/**
|
||||
* Creates a logger with the given prefix.
|
||||
* @param {string} logPrefix
|
||||
*/
|
||||
static prefix(logPrefix) {
|
||||
return createLogger(logPrefix);
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(ConsoleLogger, createLogger());
|
||||
|
||||
//#endregion
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Application logger.
|
||||
*
|
||||
* @import { LoggerOptions, Level } from "pino"
|
||||
* @import { PrettyOptions } from "pino-pretty"
|
||||
* @import { IConsoleLogger } from "./shared.js"
|
||||
*/
|
||||
|
||||
/// <reference types="../types/node.js" />
|
||||
|
||||
import { fixture, prefix } from "./shared.js";
|
||||
|
||||
let warnedAboutPino = false;
|
||||
|
||||
const { pino } = await import("pino").catch(() => {
|
||||
if (!warnedAboutPino) {
|
||||
console.warn(
|
||||
`Pino is not available. Falling back to a lightweight console logger.
|
||||
Please install Pino to get the full logging experience: npm install pino`,
|
||||
);
|
||||
warnedAboutPino = true;
|
||||
}
|
||||
|
||||
return import("./shared.js").then((module) => ({ pino: module.pinoLight }));
|
||||
});
|
||||
|
||||
//#region Constants
|
||||
|
||||
/**
|
||||
* Default options for creating a Pino logger.
|
||||
*
|
||||
* @category Logger
|
||||
* @satisfies {LoggerOptions<never, false>}
|
||||
*/
|
||||
export const DEFAULT_PINO_LOGGER_OPTIONS = {
|
||||
enabled: true,
|
||||
level: "info",
|
||||
transport: {
|
||||
target: "./transport.js",
|
||||
options: /** @satisfies {PrettyOptions} */ ({
|
||||
colorize: true,
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Functions
|
||||
|
||||
/**
|
||||
* Read the log level from the environment.
|
||||
* @return {Level}
|
||||
*/
|
||||
export function readLogLevel() {
|
||||
return process.env.AK_LOG_LEVEL || DEFAULT_PINO_LOGGER_OPTIONS.level;
|
||||
}
|
||||
|
||||
/**
|
||||
* A singleton logger instance for Node.js.
|
||||
*
|
||||
* ```js
|
||||
* import { ConsoleLogger } from "#logger/node";
|
||||
*
|
||||
* ConsoleLogger.info("Hello, world!");
|
||||
* ```
|
||||
*
|
||||
* @runtime node
|
||||
* @type {IConsoleLogger}
|
||||
*/
|
||||
export const ConsoleLogger = Object.assign(
|
||||
pino({
|
||||
...DEFAULT_PINO_LOGGER_OPTIONS,
|
||||
level: readLogLevel(),
|
||||
}),
|
||||
{
|
||||
fixture,
|
||||
prefix,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* @file Lightweight logging when Pino is not available
|
||||
* @import { ChildLoggerOptions, LoggerOptions, Logger, BaseLogger, LoggerExtras, LogFnFields, Level, LogFn } from "pino"
|
||||
* @import { PrettyOptions } from "pino-pretty"
|
||||
*/
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
//#region Constants
|
||||
|
||||
/**
|
||||
* Labels log levels in the browser console.
|
||||
* @satisfies {Record<Level, string>}
|
||||
*/
|
||||
export const LogLevelLabel = /** @type {const} */ ({
|
||||
info: "[INFO]",
|
||||
warn: "[WARN]",
|
||||
error: "[ERROR]",
|
||||
debug: "[DEBUG]",
|
||||
trace: "[TRACE]",
|
||||
fatal: "[FATAL]",
|
||||
});
|
||||
|
||||
/**
|
||||
* Predefined log levels.
|
||||
*/
|
||||
export const LogLevels = /** @type {Level[]} */ (Object.keys(LogLevelLabel));
|
||||
|
||||
//#region Functions
|
||||
|
||||
/**
|
||||
* Creates a logger with the given prefix.
|
||||
*
|
||||
* @param {string | null} [prefix]
|
||||
* @param {...string} args
|
||||
* @returns {Logger}
|
||||
*/
|
||||
function createConsoleLogger(prefix, ...args) {
|
||||
const msgPrefix = prefix ? `(${prefix}):` : ":";
|
||||
|
||||
/**
|
||||
* @type {Partial<Logger>}
|
||||
*/
|
||||
const logger = {
|
||||
msgPrefix,
|
||||
};
|
||||
|
||||
for (const level of LogLevels) {
|
||||
const label = LogLevelLabel[level];
|
||||
|
||||
// @ts-expect-error Alias the log method to the appropriate console method,
|
||||
// defaulting to console.log if the level is not supported.
|
||||
const method = level in console ? console[level] : console.log;
|
||||
|
||||
logger[level] = method.bind(console, `${label} ${msgPrefix}`, ...args);
|
||||
}
|
||||
|
||||
return /** @type {Logger} */ (logger);
|
||||
}
|
||||
/**
|
||||
* @typedef {Logger} FixtureLogger
|
||||
*/
|
||||
|
||||
/**
|
||||
* @this {Logger}
|
||||
* @param {string} fixtureName
|
||||
* @param {string} [testName]
|
||||
* @param {ChildLoggerOptions} [options]
|
||||
* @returns {FixtureLogger}
|
||||
*/
|
||||
export function fixture(fixtureName, testName, options) {
|
||||
return this.child(
|
||||
{ name: fixtureName },
|
||||
{
|
||||
msgPrefix: `[${testName}] `,
|
||||
...options,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @this {Logger}
|
||||
* @param {Record<string, unknown>} bindings
|
||||
* @param {ChildLoggerOptions} [_options]
|
||||
* @returns {Logger}
|
||||
*/
|
||||
export function child(bindings, _options) {
|
||||
const prefix = typeof bindings.name === "string" ? bindings.name : null;
|
||||
return Object.assign(createConsoleLogger(prefix), { ...bindings });
|
||||
}
|
||||
|
||||
/**
|
||||
* @this {{ child: typeof child }}
|
||||
* @param {string} label
|
||||
* @returns {IConsoleLogger}
|
||||
*/
|
||||
export function prefix(label) {
|
||||
// @ts-expect-error Create a child logger with the given prefix.
|
||||
return this.child({ name: label });
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {object} CustomLoggerMethods
|
||||
* @property {typeof fixture} fixture
|
||||
* @property {typeof prefix} prefix
|
||||
* @property {typeof child} child
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Record<Level, LogFn>} BaseConsoleLogger
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {BaseConsoleLogger & CustomLoggerMethods} IConsoleLogger
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {CustomLoggerMethods}
|
||||
*/
|
||||
export const customLoggerMethods = {
|
||||
fixture,
|
||||
prefix,
|
||||
child,
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a lightweight logger that mimics the Pino API but falls back to
|
||||
* console methods when Pino is not available.
|
||||
* @param {LoggerOptions<never, false>} options
|
||||
* @return {IConsoleLogger}
|
||||
*/
|
||||
export function pinoLight(options) {
|
||||
const baseLogger = createConsoleLogger(options.name);
|
||||
|
||||
/**
|
||||
* @type {IConsoleLogger}
|
||||
*/
|
||||
const logger = {
|
||||
...baseLogger,
|
||||
fixture,
|
||||
prefix,
|
||||
child,
|
||||
};
|
||||
|
||||
return logger;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @file Pretty transport for Pino
|
||||
*
|
||||
* @import { PrettyOptions } from "pino-pretty"
|
||||
*/
|
||||
|
||||
import PinoPretty from "pino-pretty";
|
||||
|
||||
/**
|
||||
* @param {PrettyOptions} options
|
||||
*/
|
||||
function prettyTransporter(options) {
|
||||
const pretty = PinoPretty({
|
||||
...options,
|
||||
ignore: "pid,hostname",
|
||||
translateTime: "SYS:HH:MM:ss",
|
||||
});
|
||||
|
||||
return pretty;
|
||||
}
|
||||
|
||||
export default prettyTransporter;
|
||||
Generated
+2054
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"name": "@goauthentik/logger-js",
|
||||
"version": "1.0.0",
|
||||
"description": "Pino-based logger configuration for Authentik",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/goauthentik/authentik.git",
|
||||
"directory": "packages/logger-js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc -p .",
|
||||
"lint": "eslint --fix .",
|
||||
"lint-check": "eslint --max-warnings 0 .",
|
||||
"prettier": "prettier --write .",
|
||||
"prettier-check": "prettier --check ."
|
||||
},
|
||||
"type": "module",
|
||||
"types": "./out/index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"types": "./out/index.d.ts",
|
||||
"node": "./lib/node.js",
|
||||
"import": "./index.js",
|
||||
"browser": "./lib/browser.js"
|
||||
},
|
||||
"./browser": {
|
||||
"types": "./out/lib/browser.d.ts",
|
||||
"import": "./lib/browser.js"
|
||||
},
|
||||
"./node": {
|
||||
"types": "./out/lib/node.d.ts",
|
||||
"import": "./lib/node.js"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.3",
|
||||
"@goauthentik/prettier-config": "../prettier-config",
|
||||
"@goauthentik/tsconfig": "../tsconfig",
|
||||
"@types/node": "^25.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.56.1",
|
||||
"@typescript-eslint/parser": "^8.56.1",
|
||||
"eslint": "^9.39.3",
|
||||
"pino": "^10.3.1",
|
||||
"pino-pretty": "^13.1.2",
|
||||
"prettier": "^3.8.1",
|
||||
"prettier-plugin-packagejson": "^3.0.0",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.56.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"pino": "^10.3.1",
|
||||
"pino-pretty": "^13.1.2"
|
||||
},
|
||||
"files": [
|
||||
"./index.js",
|
||||
"lib/**/*",
|
||||
"out/**/*"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"npm": ">=11.10.1"
|
||||
},
|
||||
"devEngines": {
|
||||
"runtime": {
|
||||
"name": "node",
|
||||
"version": ">=24",
|
||||
"onFail": "ignore"
|
||||
},
|
||||
"packageManager": {
|
||||
"name": "npm",
|
||||
"version": ">=11.10.1",
|
||||
"onFail": "ignore"
|
||||
}
|
||||
},
|
||||
"prettier": "@goauthentik/prettier-config",
|
||||
"overrides": {
|
||||
"format-imports": {
|
||||
"eslint": "$eslint"
|
||||
}
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"pino": {
|
||||
"optional": true
|
||||
},
|
||||
"pino-pretty": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": "@goauthentik/tsconfig",
|
||||
"compilerOptions": {
|
||||
"lib": ["ESNext"],
|
||||
"resolveJsonModule": true,
|
||||
"baseUrl": ".",
|
||||
"checkJs": true,
|
||||
"emitDeclarationOnly": true
|
||||
},
|
||||
"exclude": [
|
||||
// ---
|
||||
"**/out/**/*"
|
||||
]
|
||||
}
|
||||
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
declare module "process" {
|
||||
import { Level } from "pino";
|
||||
|
||||
global {
|
||||
namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
/**
|
||||
* An environment variable used to determine
|
||||
* whether Node.js is running in production mode.
|
||||
*
|
||||
* @see {@link https://nodejs.org/en/learn/getting-started/nodejs-the-difference-between-development-and-production | The difference between development and production}
|
||||
*/
|
||||
readonly NODE_ENV?: "development" | "production";
|
||||
|
||||
/**
|
||||
* Whether or not we are running on a CI server.
|
||||
*/
|
||||
readonly CI?: string;
|
||||
|
||||
/**
|
||||
* The application log level.
|
||||
*/
|
||||
readonly AK_LOG_LEVEL?: Level;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user