@elysiumjs/hermes 0.4.0 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/dist/decorators/logged.d.ts +16 -0
- package/dist/hermes.module.d.ts +24 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1 -1
- package/dist/services/logger.service.d.ts +29 -0
- package/dist/transports/http.transport.d.ts +22 -0
- package/dist/types.d.ts +13 -0
- package/package.json +16 -3
- package/index.ts +0 -15
- package/src/decorators/logged.ts +0 -86
- package/src/hermes.module.ts +0 -37
- package/src/index.ts +0 -21
- package/src/services/logger.service.ts +0 -98
- package/src/transports/http.transport.ts +0 -79
- package/src/types.ts +0 -34
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# @elysiumjs/hermes
|
|
2
2
|
|
|
3
|
+
## 0.4.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`04773f2`](https://github.com/na2axl/elysiumjs/commit/04773f2acd4ab1c762a80ee903715d90f1c08e21) Thanks [@na2axl](https://github.com/na2axl)! - Ship the type declarations. The packages pointed `types` at `dist/index.d.ts` but their build never wrote it, so TypeScript projects got no types for them.
|
|
8
|
+
|
|
9
|
+
## 0.4.2
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [`68a0cdd`](https://github.com/na2axl/elysiumjs/commit/68a0cdd8b2e4a003d6e0af2d78985623cae40428) Thanks [@na2axl](https://github.com/na2axl)! - Route the framework's own diagnostics through the logger service.
|
|
14
|
+
|
|
15
|
+
Breaking changes:
|
|
16
|
+
|
|
17
|
+
- `@Event.listen()` with a mode other than `on` or `once` now throws an `ElysiumError` when the class is defined, instead of logging to the console and ignoring the listener.
|
|
18
|
+
|
|
19
|
+
Fixes and additions:
|
|
20
|
+
|
|
21
|
+
- `getLogger(context?)` and `LOGGER_SERVICE` (`'logger'`) are exported by `@elysiumjs/core`. `getLogger()` returns the `logger` service when one is registered (such as the one `HermesModule` provides), and a `ConsoleLogger` otherwise or when the service cannot be built or fails to create a child logger. It is resolved at call time from the global scope only, so a module's own `logger` service does not replace it, and returns a child logger bound to `context` when one is given.
|
|
22
|
+
- `Middleware.logger` (used by `RequestLoggerMiddleware`) falls back to a `ConsoleLogger` when no `logger` service is registered. It threw before.
|
|
23
|
+
- Core diagnostics go through the logger with structured context instead of `console.*`: the debug-mode request log (`debug`, with `method` and `url`), `onDestroy` failures on shutdown (`error`, with `target` and `error`), WAMP connection attempts (`warn` while retrying, `error` when giving up, with `connection`, `attempt`/`attempts`, `maxRetries` and `error`), and WAMP registrations and subscriptions (`info`, with `connection`, `topic` and the registration or subscription id).
|
|
24
|
+
- In debug mode, incoming requests are logged through the logger at `debug` level instead of printed with `console.log`. With `HermesModule`, they show only when its `level` is `debug` or `trace`.
|
|
25
|
+
- A failed WAMP registration or subscription is logged at `error` level and emitted as `elysium:error` with an error naming the topic and the connection, instead of becoming an unhandled rejection.
|
|
26
|
+
- The `elysium:error` emitted for a WAMP client error names the connection and its URL and includes the client's message (or the value it reported, when it is not an `Error`), with that value as `cause` when there is one.
|
|
27
|
+
- `HermesLogger` serialises an `error` field of the log context like pino's `err` (type, message and stack), so logged errors keep their stack trace. Elysium logs errors under `error`.
|
|
28
|
+
- `@Logged` resolves its fallback logger with `getLogger()`, and logs a thrown error whole under `error` (it logged only its message), so its stack trace is kept.
|
|
29
|
+
- The `elysium:error` emitted when a WAMP connection gives up states the number of attempts actually made (the first one plus the retries), as the logged `attempts` does. It stated `maxRetries` before.
|
|
30
|
+
|
|
31
|
+
- Updated dependencies [[`68a0cdd`](https://github.com/na2axl/elysiumjs/commit/68a0cdd8b2e4a003d6e0af2d78985623cae40428), [`2c49acd`](https://github.com/na2axl/elysiumjs/commit/2c49acd3b552aa43765d70339fa216961499e1b5), [`76ba0ff`](https://github.com/na2axl/elysiumjs/commit/76ba0ff18f3c196cebc2189426328f67cb699f46), [`4ebf029`](https://github.com/na2axl/elysiumjs/commit/4ebf029611e6d753a4f1bfdbe247a75d954d9ded), [`bacce71`](https://github.com/na2axl/elysiumjs/commit/bacce7146f667a1a46bf474dc0344fb0111ae12a)]:
|
|
32
|
+
- @elysiumjs/core@0.10.0
|
|
33
|
+
|
|
34
|
+
## 0.4.1
|
|
35
|
+
|
|
36
|
+
### Patch Changes
|
|
37
|
+
|
|
38
|
+
- Updated dependencies [[`5fef4a7`](https://github.com/na2axl/elysiumjs/commit/5fef4a7199f502fc4fe035299e7670739459a108)]:
|
|
39
|
+
- @elysiumjs/core@0.9.0
|
|
40
|
+
|
|
3
41
|
## 0.4.0
|
|
4
42
|
|
|
5
43
|
### Minor Changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { LogLevel } from '../types';
|
|
2
|
+
export interface LoggedOptions {
|
|
3
|
+
level?: LogLevel;
|
|
4
|
+
includeArgs?: boolean;
|
|
5
|
+
includeResult?: boolean;
|
|
6
|
+
includeTiming?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Decorator that logs method entry/exit with optional timing.
|
|
10
|
+
*
|
|
11
|
+
* Logger is resolved at CALL TIME (not decorator definition time)
|
|
12
|
+
* to avoid capturing a stale logger reference.
|
|
13
|
+
*
|
|
14
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
15
|
+
*/
|
|
16
|
+
export declare function Logged(options?: LoggedOptions): MethodDecorator;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type HermesConfig } from './types';
|
|
2
|
+
declare const HermesModule_base: (abstract new () => {
|
|
3
|
+
readonly config: HermesConfig;
|
|
4
|
+
onInit(): Promise<void> | void;
|
|
5
|
+
onDestroy(): Promise<void> | void;
|
|
6
|
+
onServe(_app: import("elysia").default<any>): Promise<void> | void;
|
|
7
|
+
}) & {
|
|
8
|
+
readonly config: string;
|
|
9
|
+
with(config: Partial<HermesConfig> | (() => Partial<HermesConfig> | Promise<Partial<HermesConfig>>)): import("@elysiumjs/core").ConfiguredModule<HermesConfig>;
|
|
10
|
+
resolveConfig(input?: Partial<HermesConfig> | undefined): HermesConfig;
|
|
11
|
+
register<TConfig_1 = any>(props?: import("@elysiumjs/core").ModuleProps<TConfig_1>): (target: import("@elysiumjs/core").ModuleClass) => void;
|
|
12
|
+
configurable<TConfig extends object>(options?: {
|
|
13
|
+
defaults?: Partial<TConfig>;
|
|
14
|
+
required?: (keyof TConfig)[];
|
|
15
|
+
}): any;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Provides the {@link HermesLogger} as the global services `HermesLogger` and `logger`
|
|
19
|
+
* (the same instance), built from the module config.
|
|
20
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
21
|
+
*/
|
|
22
|
+
export declare class HermesModule extends HermesModule_base {
|
|
23
|
+
}
|
|
24
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { HermesModule } from './hermes.module';
|
|
2
|
+
export { HermesLogger } from './services/logger.service';
|
|
3
|
+
export { HttpTransport } from './transports/http.transport';
|
|
4
|
+
export type { HttpTransportOptions } from './transports/http.transport';
|
|
5
|
+
export { Logged } from './decorators/logged';
|
|
6
|
+
export type { LoggedOptions } from './decorators/logged';
|
|
7
|
+
export type { HermesConfig, LogLevel, LogFormat, TransportConfig } from './types';
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var __legacyDecorateClassTS=function(decorators,target,key,desc){var c=arguments.length,r=c<3?target:desc===null?desc=Object.getOwnPropertyDescriptor(target,key):desc,d;if(typeof Reflect==="object"&&typeof Reflect.decorate==="function")r=Reflect.decorate(decorators,target,key,desc);else for(var i=decorators.length-1;i>=0;i--)if(d=decorators[i])r=(c<3?d(r):c>3?d(target,key,r):d(target,key))||r;return c>3&&r&&Object.defineProperty(target,key,r),r};
|
|
2
|
+
var __legacyDecorateClassTS=function(decorators,target,key,desc){var c=arguments.length,r=c<3?target:desc===null?desc=Object.getOwnPropertyDescriptor(target,key):desc,d;if(typeof Reflect==="object"&&typeof Reflect.decorate==="function")r=Reflect.decorate(decorators,target,key,desc);else for(var i=decorators.length-1;i>=0;i--)if(d=decorators[i])r=(c<3?d(r):c>3?d(target,key,r):d(target,key))||r;return c>3&&r&&Object.defineProperty(target,key,r),r};import{LOGGER_SERVICE,Module}from"@elysiumjs/core";import{Service}from"@elysiumjs/core";import pino from"pino";var DEFAULT_CONFIG={level:"info",format:"json"};class HermesLogger{pino;config;static make(name){return Service.get(HermesLogger)?.child({name})??new HermesLogger({name})}constructor(config={}){this.config={...DEFAULT_CONFIG,...config},this.pino=this.createPinoInstance()}createPinoInstance(){let options={level:this.config.level,name:this.config.name,serializers:{error:pino.stdSerializers.err}};if(this.config.format==="pretty")return pino(options,pino.transport({target:"pino-pretty"}));return pino(options)}trace(msg,ctx){this.pino.trace(ctx??{},msg)}debug(msg,ctx){this.pino.debug(ctx??{},msg)}info(msg,ctx){this.pino.info(ctx??{},msg)}warn(msg,ctx){this.pino.warn(ctx??{},msg)}error(msg,ctx){this.pino.error(ctx??{},msg)}fatal(msg,ctx){this.pino.fatal(ctx??{},msg)}flush(){this.pino.flush?.()}onDestroy(){this.flush()}child(ctx){let child=Object.create(this);return child.pino=this.pino.child(ctx),child}}class HermesModule extends Module.configurable({defaults:{...DEFAULT_CONFIG}}){}HermesModule=__legacyDecorateClassTS([Module.register({services:(config)=>[{name:HermesLogger.name,global:!0,factory:()=>new HermesLogger(config)},{name:LOGGER_SERVICE,global:!0,factory:(_config,get)=>get(HermesLogger.name)}]})],HermesModule);class HttpTransport{options;buffer=[];flushTimer;maxBufferSize;constructor(options){this.options=options;this.options.batchSize??=100,this.options.flushInterval??=5000,this.maxBufferSize=this.options.maxBufferSize??1e4,this.flushTimer=setInterval(()=>this.flush(),this.options.flushInterval)}write(formatted){if(this.buffer.length>=this.maxBufferSize)this.buffer.shift();if(this.buffer.push(formatted),this.buffer.length>=this.options.batchSize)this.flush()}async flush(){if(this.buffer.length===0)return;let batch=this.buffer.splice(0);try{await fetch(this.options.url,{method:"POST",headers:{"Content-Type":"application/json",...this.options.headers},body:JSON.stringify({logs:batch})})}catch{let space=this.maxBufferSize-this.buffer.length;if(space>0)this.buffer.unshift(...batch.slice(0,space))}}async close(){if(this.flushTimer)clearInterval(this.flushTimer);await this.flush()}}import{getLogger}from"@elysiumjs/core";function Logged(options={}){return function(target,propertyKey,descriptor){let original=descriptor.value,methodName=`${target.constructor.name}.${String(propertyKey)}`;return descriptor.value=async function(...args){let logger=this.logger??getLogger(),startTime=Date.now();if(options.includeArgs)logger.debug(`[${methodName}] Entry`,{args});try{let result=await original.apply(this,args);if(options.includeResult||options.includeTiming){let duration=Date.now()-startTime;logger[options.level??"debug"](`[${methodName}] Exit`,{...options.includeResult&&{result},...options.includeTiming&&{duration:`${duration}ms`}})}return result}catch(error){let duration=Date.now()-startTime;throw logger.error(`[${methodName}] Error`,{error,duration:`${duration}ms`}),error}},descriptor}}export{HermesLogger,HermesModule,HttpTransport,Logged};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { LoggerInterface } from '@elysiumjs/core';
|
|
2
|
+
import type { HermesConfig } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Production-ready logger implementation wrapping Pino.
|
|
5
|
+
* Implements `LoggerInterface` from `@elysiumjs/core`.
|
|
6
|
+
*
|
|
7
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
8
|
+
*/
|
|
9
|
+
export declare class HermesLogger implements LoggerInterface {
|
|
10
|
+
private pino;
|
|
11
|
+
private config;
|
|
12
|
+
static make(name: string): HermesLogger;
|
|
13
|
+
constructor(config?: Partial<HermesConfig>);
|
|
14
|
+
private createPinoInstance;
|
|
15
|
+
trace(msg: string, ctx?: Record<string, unknown>): void;
|
|
16
|
+
debug(msg: string, ctx?: Record<string, unknown>): void;
|
|
17
|
+
info(msg: string, ctx?: Record<string, unknown>): void;
|
|
18
|
+
warn(msg: string, ctx?: Record<string, unknown>): void;
|
|
19
|
+
error(msg: string, ctx?: Record<string, unknown>): void;
|
|
20
|
+
fatal(msg: string, ctx?: Record<string, unknown>): void;
|
|
21
|
+
/**
|
|
22
|
+
* Flushes buffered log lines of the underlying transport, when it supports it.
|
|
23
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
24
|
+
*/
|
|
25
|
+
flush(): void;
|
|
26
|
+
/** Flushes on shutdown. Only called for a logger the container built. */
|
|
27
|
+
onDestroy(): void;
|
|
28
|
+
child(ctx: Record<string, unknown>): HermesLogger;
|
|
29
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface HttpTransportOptions {
|
|
2
|
+
url: string;
|
|
3
|
+
headers?: Record<string, string>;
|
|
4
|
+
batchSize?: number;
|
|
5
|
+
flushInterval?: number;
|
|
6
|
+
maxBufferSize?: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* HTTP transport for batching and sending logs to a remote endpoint.
|
|
10
|
+
*
|
|
11
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
12
|
+
*/
|
|
13
|
+
export declare class HttpTransport {
|
|
14
|
+
private options;
|
|
15
|
+
private buffer;
|
|
16
|
+
private flushTimer?;
|
|
17
|
+
private maxBufferSize;
|
|
18
|
+
constructor(options: HttpTransportOptions);
|
|
19
|
+
write(formatted: string): void;
|
|
20
|
+
flush(): Promise<void>;
|
|
21
|
+
close(): Promise<void>;
|
|
22
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
|
2
|
+
export type LogFormat = 'json' | 'pretty' | 'logfmt';
|
|
3
|
+
export interface TransportConfig {
|
|
4
|
+
type: 'console' | 'file' | 'http';
|
|
5
|
+
options?: Record<string, unknown>;
|
|
6
|
+
}
|
|
7
|
+
export interface HermesConfig {
|
|
8
|
+
name?: string;
|
|
9
|
+
level: LogLevel;
|
|
10
|
+
format: LogFormat;
|
|
11
|
+
transports?: TransportConfig[];
|
|
12
|
+
}
|
|
13
|
+
export declare const DEFAULT_CONFIG: HermesConfig;
|
package/package.json
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elysiumjs/hermes",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "git+https://github.com/na2axl/elysiumjs.git",
|
|
7
|
+
"directory": "@elysiumjs/hermes"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/na2axl/elysiumjs/tree/main/@elysiumjs/hermes#readme",
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/na2axl/elysiumjs/issues"
|
|
12
|
+
},
|
|
4
13
|
"type": "module",
|
|
5
14
|
"module": "dist/index.js",
|
|
6
15
|
"types": "dist/index.d.ts",
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"CHANGELOG.md"
|
|
19
|
+
],
|
|
7
20
|
"sideEffects": false,
|
|
8
21
|
"scripts": {
|
|
9
22
|
"format": "prettier --write \"./src/**/*.ts\"",
|
|
@@ -21,9 +34,9 @@
|
|
|
21
34
|
"pino-pretty": "^13.2.0"
|
|
22
35
|
},
|
|
23
36
|
"devDependencies": {
|
|
24
|
-
"@elysiumjs/core": "0.
|
|
37
|
+
"@elysiumjs/core": "0.10.0"
|
|
25
38
|
},
|
|
26
39
|
"peerDependencies": {
|
|
27
|
-
"@elysiumjs/core": "^0.
|
|
40
|
+
"@elysiumjs/core": "^0.10.0"
|
|
28
41
|
}
|
|
29
42
|
}
|
package/index.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025-present Workbud Technologies Inc. All rights reserved.
|
|
2
|
-
//
|
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
// you may not use this file except in compliance with the License.
|
|
5
|
-
// You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
// See the License for the specific language governing permissions and
|
|
13
|
-
// limitations under the License.
|
|
14
|
-
|
|
15
|
-
export * from './src/index';
|
package/src/decorators/logged.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2026-present Workbud Technologies Inc. All rights reserved.
|
|
2
|
-
//
|
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
// you may not use this file except in compliance with the License.
|
|
5
|
-
// You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
// See the License for the specific language governing permissions and
|
|
13
|
-
// limitations under the License.
|
|
14
|
-
|
|
15
|
-
import type { LoggerInterface } from '@elysiumjs/core';
|
|
16
|
-
import type { LogLevel } from '../types';
|
|
17
|
-
|
|
18
|
-
import { ConsoleLogger } from '@elysiumjs/core';
|
|
19
|
-
|
|
20
|
-
export interface LoggedOptions {
|
|
21
|
-
level?: LogLevel;
|
|
22
|
-
includeArgs?: boolean;
|
|
23
|
-
includeResult?: boolean;
|
|
24
|
-
includeTiming?: boolean;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Decorator that logs method entry/exit with optional timing.
|
|
29
|
-
*
|
|
30
|
-
* Logger is resolved at CALL TIME (not decorator definition time)
|
|
31
|
-
* to avoid capturing a stale logger reference.
|
|
32
|
-
*
|
|
33
|
-
* @author Axel Nana <axel.nana@workbud.com>
|
|
34
|
-
*/
|
|
35
|
-
export function Logged(options: LoggedOptions = {}): MethodDecorator {
|
|
36
|
-
return function (
|
|
37
|
-
target: object,
|
|
38
|
-
propertyKey: string | symbol,
|
|
39
|
-
descriptor: TypedPropertyDescriptor<any>
|
|
40
|
-
) {
|
|
41
|
-
const original = descriptor.value;
|
|
42
|
-
const methodName = `${target.constructor.name}.${String(propertyKey)}`;
|
|
43
|
-
|
|
44
|
-
descriptor.value = async function (...args: any[]) {
|
|
45
|
-
const logger: LoggerInterface = (this as any).logger ?? getServiceLogger();
|
|
46
|
-
|
|
47
|
-
const startTime = Date.now();
|
|
48
|
-
|
|
49
|
-
if (options.includeArgs) {
|
|
50
|
-
logger.debug(`[${methodName}] Entry`, { args });
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
const result = await original.apply(this, args);
|
|
55
|
-
|
|
56
|
-
if (options.includeResult || options.includeTiming) {
|
|
57
|
-
const duration = Date.now() - startTime;
|
|
58
|
-
logger[options.level ?? 'debug'](`[${methodName}] Exit`, {
|
|
59
|
-
...(options.includeResult && { result }),
|
|
60
|
-
...(options.includeTiming && { duration: `${duration}ms` })
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return result;
|
|
65
|
-
} catch (error) {
|
|
66
|
-
const duration = Date.now() - startTime;
|
|
67
|
-
logger.error(`[${methodName}] Error`, {
|
|
68
|
-
error: error instanceof Error ? error.message : error,
|
|
69
|
-
duration: `${duration}ms`
|
|
70
|
-
});
|
|
71
|
-
throw error;
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
return descriptor;
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function getServiceLogger(): LoggerInterface {
|
|
80
|
-
try {
|
|
81
|
-
const { Service } = require('@elysiumjs/core');
|
|
82
|
-
return Service.get('logger') ?? new ConsoleLogger();
|
|
83
|
-
} catch {
|
|
84
|
-
return new ConsoleLogger();
|
|
85
|
-
}
|
|
86
|
-
}
|
package/src/hermes.module.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2026-present Workbud Technologies Inc. All rights reserved.
|
|
2
|
-
//
|
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
// you may not use this file except in compliance with the License.
|
|
5
|
-
// You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
// See the License for the specific language governing permissions and
|
|
13
|
-
// limitations under the License.
|
|
14
|
-
|
|
15
|
-
import { Module } from '@elysiumjs/core';
|
|
16
|
-
|
|
17
|
-
import { HermesLogger } from './services/logger.service';
|
|
18
|
-
import { DEFAULT_CONFIG, type HermesConfig } from './types';
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Provides the {@link HermesLogger} as the global services `HermesLogger` and `logger`
|
|
22
|
-
* (the same instance), built from the module config.
|
|
23
|
-
* @author Axel Nana <axel.nana@workbud.com>
|
|
24
|
-
*/
|
|
25
|
-
@Module.register<HermesConfig>({
|
|
26
|
-
services: (config) => [
|
|
27
|
-
{ name: HermesLogger.name, global: true, factory: () => new HermesLogger(config) },
|
|
28
|
-
{
|
|
29
|
-
name: 'logger',
|
|
30
|
-
global: true,
|
|
31
|
-
factory: (_config: unknown, get: <T>(name: string) => T) => get<HermesLogger>(HermesLogger.name)
|
|
32
|
-
}
|
|
33
|
-
]
|
|
34
|
-
})
|
|
35
|
-
export class HermesModule extends Module.configurable<HermesConfig>({
|
|
36
|
-
defaults: { ...DEFAULT_CONFIG }
|
|
37
|
-
}) {}
|
package/src/index.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2026-present Workbud Technologies Inc. All rights reserved.
|
|
2
|
-
//
|
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
// you may not use this file except in compliance with the License.
|
|
5
|
-
// You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
// See the License for the specific language governing permissions and
|
|
13
|
-
// limitations under the License.
|
|
14
|
-
|
|
15
|
-
export { HermesModule } from './hermes.module';
|
|
16
|
-
export { HermesLogger } from './services/logger.service';
|
|
17
|
-
export { HttpTransport } from './transports/http.transport';
|
|
18
|
-
export type { HttpTransportOptions } from './transports/http.transport';
|
|
19
|
-
export { Logged } from './decorators/logged';
|
|
20
|
-
export type { LoggedOptions } from './decorators/logged';
|
|
21
|
-
export type { HermesConfig, LogLevel, LogFormat, TransportConfig } from './types';
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2026-present Workbud Technologies Inc. All rights reserved.
|
|
2
|
-
//
|
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
// you may not use this file except in compliance with the License.
|
|
5
|
-
// You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
// See the License for the specific language governing permissions and
|
|
13
|
-
// limitations under the License.
|
|
14
|
-
|
|
15
|
-
import type { LoggerInterface } from '@elysiumjs/core';
|
|
16
|
-
import type { Logger as PinoLogger } from 'pino';
|
|
17
|
-
import type { HermesConfig } from '../types';
|
|
18
|
-
|
|
19
|
-
import { Service } from '@elysiumjs/core';
|
|
20
|
-
import pino from 'pino';
|
|
21
|
-
|
|
22
|
-
import { DEFAULT_CONFIG } from '../types';
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Production-ready logger implementation wrapping Pino.
|
|
26
|
-
* Implements `LoggerInterface` from `@elysiumjs/core`.
|
|
27
|
-
*
|
|
28
|
-
* @author Axel Nana <axel.nana@workbud.com>
|
|
29
|
-
*/
|
|
30
|
-
export class HermesLogger implements LoggerInterface {
|
|
31
|
-
private pino: PinoLogger;
|
|
32
|
-
private config: HermesConfig;
|
|
33
|
-
|
|
34
|
-
public static make(name: string): HermesLogger {
|
|
35
|
-
return Service.get(HermesLogger)?.child({ name }) ?? new HermesLogger({ name });
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
constructor(config: Partial<HermesConfig> = {}) {
|
|
39
|
-
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
40
|
-
this.pino = this.createPinoInstance();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
private createPinoInstance(): PinoLogger {
|
|
44
|
-
const options: pino.LoggerOptions = {
|
|
45
|
-
level: this.config.level,
|
|
46
|
-
name: this.config.name
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
if (this.config.format === 'pretty') {
|
|
50
|
-
return pino(options, pino.transport({ target: 'pino-pretty' }));
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return pino(options);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
public trace(msg: string, ctx?: Record<string, unknown>): void {
|
|
57
|
-
this.pino.trace(ctx ?? {}, msg);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
public debug(msg: string, ctx?: Record<string, unknown>): void {
|
|
61
|
-
this.pino.debug(ctx ?? {}, msg);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
public info(msg: string, ctx?: Record<string, unknown>): void {
|
|
65
|
-
this.pino.info(ctx ?? {}, msg);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
public warn(msg: string, ctx?: Record<string, unknown>): void {
|
|
69
|
-
this.pino.warn(ctx ?? {}, msg);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
public error(msg: string, ctx?: Record<string, unknown>): void {
|
|
73
|
-
this.pino.error(ctx ?? {}, msg);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
public fatal(msg: string, ctx?: Record<string, unknown>): void {
|
|
77
|
-
this.pino.fatal(ctx ?? {}, msg);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Flushes buffered log lines of the underlying transport, when it supports it.
|
|
82
|
-
* @author Axel Nana <axel.nana@workbud.com>
|
|
83
|
-
*/
|
|
84
|
-
public flush(): void {
|
|
85
|
-
this.pino.flush?.();
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/** Flushes on shutdown. Only called for a logger the container built. */
|
|
89
|
-
public onDestroy(): void {
|
|
90
|
-
this.flush();
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
public child(ctx: Record<string, unknown>): HermesLogger {
|
|
94
|
-
const child = Object.create(this) as HermesLogger;
|
|
95
|
-
child.pino = this.pino.child(ctx);
|
|
96
|
-
return child;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2026-present Workbud Technologies Inc. All rights reserved.
|
|
2
|
-
//
|
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
// you may not use this file except in compliance with the License.
|
|
5
|
-
// You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
// See the License for the specific language governing permissions and
|
|
13
|
-
// limitations under the License.
|
|
14
|
-
|
|
15
|
-
export interface HttpTransportOptions {
|
|
16
|
-
url: string;
|
|
17
|
-
headers?: Record<string, string>;
|
|
18
|
-
batchSize?: number;
|
|
19
|
-
flushInterval?: number;
|
|
20
|
-
maxBufferSize?: number;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* HTTP transport for batching and sending logs to a remote endpoint.
|
|
25
|
-
*
|
|
26
|
-
* @author Axel Nana <axel.nana@workbud.com>
|
|
27
|
-
*/
|
|
28
|
-
export class HttpTransport {
|
|
29
|
-
private buffer: string[] = [];
|
|
30
|
-
private flushTimer?: ReturnType<typeof setInterval>;
|
|
31
|
-
private maxBufferSize: number;
|
|
32
|
-
|
|
33
|
-
constructor(private options: HttpTransportOptions) {
|
|
34
|
-
this.options.batchSize ??= 100;
|
|
35
|
-
this.options.flushInterval ??= 5000;
|
|
36
|
-
this.maxBufferSize = this.options.maxBufferSize ?? 10_000;
|
|
37
|
-
|
|
38
|
-
this.flushTimer = setInterval(() => this.flush(), this.options.flushInterval);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
public write(formatted: string): void {
|
|
42
|
-
if (this.buffer.length >= this.maxBufferSize) {
|
|
43
|
-
this.buffer.shift();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
this.buffer.push(formatted);
|
|
47
|
-
|
|
48
|
-
if (this.buffer.length >= this.options.batchSize!) {
|
|
49
|
-
this.flush();
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
public async flush(): Promise<void> {
|
|
54
|
-
if (this.buffer.length === 0) return;
|
|
55
|
-
|
|
56
|
-
const batch = this.buffer.splice(0);
|
|
57
|
-
|
|
58
|
-
try {
|
|
59
|
-
await fetch(this.options.url, {
|
|
60
|
-
method: 'POST',
|
|
61
|
-
headers: {
|
|
62
|
-
'Content-Type': 'application/json',
|
|
63
|
-
...this.options.headers
|
|
64
|
-
},
|
|
65
|
-
body: JSON.stringify({ logs: batch })
|
|
66
|
-
});
|
|
67
|
-
} catch {
|
|
68
|
-
const space = this.maxBufferSize - this.buffer.length;
|
|
69
|
-
if (space > 0) {
|
|
70
|
-
this.buffer.unshift(...batch.slice(0, space));
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
public async close(): Promise<void> {
|
|
76
|
-
if (this.flushTimer) clearInterval(this.flushTimer);
|
|
77
|
-
await this.flush();
|
|
78
|
-
}
|
|
79
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2026-present Workbud Technologies Inc. All rights reserved.
|
|
2
|
-
//
|
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
// you may not use this file except in compliance with the License.
|
|
5
|
-
// You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
// See the License for the specific language governing permissions and
|
|
13
|
-
// limitations under the License.
|
|
14
|
-
|
|
15
|
-
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
|
16
|
-
|
|
17
|
-
export type LogFormat = 'json' | 'pretty' | 'logfmt';
|
|
18
|
-
|
|
19
|
-
export interface TransportConfig {
|
|
20
|
-
type: 'console' | 'file' | 'http';
|
|
21
|
-
options?: Record<string, unknown>;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface HermesConfig {
|
|
25
|
-
name?: string;
|
|
26
|
-
level: LogLevel;
|
|
27
|
-
format: LogFormat;
|
|
28
|
-
transports?: TransportConfig[];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export const DEFAULT_CONFIG: HermesConfig = {
|
|
32
|
-
level: 'info',
|
|
33
|
-
format: 'json'
|
|
34
|
-
};
|