Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/http/UnsecureWebSocketsProtocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ export class UnsecureWebSocketsProtocol extends WebSocketHandler {

public constructor(source: EventEmitter) {
super();

this.logger.warn('The chosen configuration includes Solid WebSockets API 0.1, which is unauthenticated.');
this.logger.warn('This component will be removed from default configurations in future versions.');

source.on('changed', (changed: ResourceIdentifier): void => this.onResourceChanged(changed));
}

Expand Down
5 changes: 3 additions & 2 deletions src/http/input/body/SparqlUpdateBodyParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ export class SparqlUpdateBodyParser extends BodyParser {
try {
algebra = translate(sparql, { quads: true, baseIRI: metadata.identifier.value }) as Algebra.Update;
} catch (error: unknown) {
this.logger.warn('Could not translate SPARQL query to SPARQL algebra', { error });
throw new BadRequestHttpError(createErrorMessage(error), { cause: error });
const message = createErrorMessage(error);
this.logger.warn(`Could not translate SPARQL query to SPARQL algebra: ${message}`);
throw new BadRequestHttpError(message, { cause: error });
}

// Prevent body from being requested again
Expand Down
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,6 @@ export * from './init/ServerInitializer';
export * from './init/ModuleVersionVerifier';

// Logging
export * from './logging/LazyLogger';
export * from './logging/LazyLoggerFactory';
export * from './logging/Logger';
export * from './logging/LoggerFactory';
Expand Down
29 changes: 0 additions & 29 deletions src/logging/LazyLogger.ts

This file was deleted.

93 changes: 70 additions & 23 deletions src/logging/LazyLoggerFactory.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,92 @@
import { LazyLogger } from './LazyLogger';
import { WrappingLogger } from './Logger';
import type { Logger } from './Logger';
import type { LoggerFactory } from './LoggerFactory';
import type { LogLevel } from './LogLevel';

/**
* Wraps over another {@link LoggerFactory} that can be set lazily.
* This is a singleton class, for which the instance can be retrieved using {@link LazyLoggerFactory.getInstance}.
*
* Loggers can safely be created before a {@link LoggerFactory} is set.
* But an error will be thrown if {@link Logger.log} is invoked before a {@link LoggerFactory} is set.
*
* This creates instances of {@link LazyLogger}.
* Temporary {@link LoggerFactory} that buffers log messages in memory
* until the {@link TemporaryLoggerFactory#switch} method is called.
*/
export class LazyLoggerFactory implements LoggerFactory {
private static readonly instance = new LazyLoggerFactory();
class TemporaryLoggerFactory implements LoggerFactory {
private bufferSpaces: number;
private readonly wrappers: { wrapper: WrappingLogger; label: string }[] = [];
private readonly buffer: { logger: Logger; level: LogLevel; message: string }[] = [];

private ploggerFactory: LoggerFactory | undefined;
public constructor(bufferSize = 1024) {
this.bufferSpaces = bufferSize;
}

private constructor() {
// Singleton instance
public createLogger(label: string): WrappingLogger {
const wrapper = new WrappingLogger({
log: (level: LogLevel, message: string): Logger =>
this.bufferLogEntry(wrapper, level, message),
});
this.wrappers.push({ wrapper, label });
return wrapper;
}

public static getInstance(): LazyLoggerFactory {
return LazyLoggerFactory.instance;
private bufferLogEntry(logger: WrappingLogger, level: LogLevel, message: string): Logger {
// Buffer the message if spaces are still available
if (this.bufferSpaces > 0) {
this.bufferSpaces -= 1;
// If this is the last space, instead generate a warning through a new logger
if (this.bufferSpaces === 0) {
logger = this.createLogger('LazyLoggerFactory');
level = 'warn';
message = `Memory-buffered logging limit of ${this.buffer.length + 1} reached`;
}
this.buffer.push({ logger, level, message });
}
return logger;
}

public createLogger(label: string): Logger {
return new LazyLogger(this, label);
/**
* Swaps all lazy loggers to new loggers from the given factory,
* and emits any buffered messages through those actual loggers.
*/
public switch(loggerFactory: LoggerFactory): void {
// Instantiate an actual logger within every lazy logger
for (const { wrapper, label } of this.wrappers.splice(0, this.wrappers.length)) {
wrapper.logger = loggerFactory.createLogger(label);
}
// Emit all buffered log messages
for (const { logger, level, message } of this.buffer.splice(0, this.buffer.length)) {
logger.log(level, message);
}
}
}

public resetLoggerFactory(): void {
this.ploggerFactory = undefined;
/**
* Wraps around another {@link LoggerFactory} that can be set lazily.
* This is useful when objects are instantiated (and when they create loggers)
* before the logging system has been fully instantiated,
* as is the case when using a dependency injection framework such as Components.js.
*
* Loggers can be created even before a {@link LoggerFactory} is set;
* any log messages will be buffered and re-emitted.
*/
export class LazyLoggerFactory implements LoggerFactory {
private factory: LoggerFactory;

public constructor(options: { bufferSize?: number } = {}) {
this.factory = new TemporaryLoggerFactory(options.bufferSize);
}

public get loggerFactory(): LoggerFactory {
if (!this.ploggerFactory) {
throw new Error('No logger factory has been set. Can be caused by logger invocation during initialization.');
if (this.factory instanceof TemporaryLoggerFactory) {
throw new Error('Logger factory not yet set.');
}
return this.ploggerFactory;
return this.factory;
}

public set loggerFactory(loggerFactory: LoggerFactory) {
this.ploggerFactory = loggerFactory;
if (this.factory instanceof TemporaryLoggerFactory) {
this.factory.switch(loggerFactory);
}
this.factory = loggerFactory;
}

public createLogger(label: string): Logger {
return this.factory.createLogger(label);
}
}
48 changes: 34 additions & 14 deletions src/logging/LogUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import { LazyLoggerFactory } from './LazyLoggerFactory';
import type { Logger } from './Logger';
import type { LoggerFactory } from './LoggerFactory';

let loggerFactoryWrapper = new LazyLoggerFactory();
let classLoggers = new WeakMap<Constructor, Logger>();

/**
* Gets a logger instance for the given class instance.
*
* The returned type of logger depends on the configured {@link LoggerFactory} in {@link Setup}.
*
* The following shows a typical pattern on how to create loggers:
* ```
* class MyClass {
Expand All @@ -21,32 +22,51 @@ import type { LoggerFactory } from './LoggerFactory';
* @param loggable - A class instance or a class string name.
*/
export function getLoggerFor(loggable: string | Instance): Logger {
return LazyLoggerFactory.getInstance()
.createLogger(typeof loggable === 'string' ? loggable : loggable.constructor.name);
let logger: Logger;
// Create a logger with a text label
if (typeof loggable === 'string') {
logger = loggerFactoryWrapper.createLogger(loggable);
// Create or reuse a logger for a specific class
} else {
const { constructor } = loggable;
if (classLoggers.has(constructor)) {
logger = classLoggers.get(constructor)!;
} else {
logger = loggerFactoryWrapper.createLogger(constructor.name);
classLoggers.set(constructor, logger);
}
}
return logger;
}

/**
* Sets the global logger factory.
* This will cause all loggers created by {@link getLoggerFor} to be delegated to a logger from the given factory.
* This causes loggers created by {@link getLoggerFor} to delegate to a logger from the given factory.
* @param loggerFactory - A logger factory.
*/
export function setGlobalLoggerFactory(loggerFactory: LoggerFactory): void {
LazyLoggerFactory.getInstance().loggerFactory = loggerFactory;
loggerFactoryWrapper.loggerFactory = loggerFactory;
}

/**
* Resets the global logger factory to undefined.
*
* This typically only needs to be called during testing.
* Call this at your own risk.
* Resets the internal logger factory, which holds the global logger factory.
* For testing purposes only.
*/
export function resetInternalLoggerFactory(factory = new LazyLoggerFactory()): void {
loggerFactoryWrapper = factory;
classLoggers = new WeakMap();
}

/**
* Any class constructor.
*/
export function resetGlobalLoggerFactory(): void {
LazyLoggerFactory.getInstance().resetLoggerFactory();
interface Constructor {
name: string;
}

/**
* Helper interface to identify class instances.
* Any class instance.
*/
interface Instance {
constructor: { name: string };
constructor: Constructor;
}
Loading