-
-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add context to the logger #7
Comments
Okay, now I roughly understand what you mean by context, thanks for the explanation! Here are some questions:
|
E.g. a function that wraps the various category loggers export const initLogger = getLogger(["package", "init"]);
export const buildLogger = getLogger(["package", "build"]);
export const generalLogger = getLogger(["package", "general"]);
// Interface to extend Logger with custom loggers
export type LoggerWithCustomLoggers<L extends string> = Logger<L> & {
[P in L]: Logger<L>;
}
// Function to create a logger wrapper with typed custom loggers
export function createWrapper<L extends string>(options: LoggerOptions<L> = {}): LoggerWithCustomLoggers<L> {
const { logger = "general" as L, template, context = {}, loggers = {} } = options;
const loggers = {
general: generalLogger,
} as Record<L | "general", LogTapeLogger>;
let currentLogger = logger;
const result: LoggerWithCustomLoggers<L> = {
with(options: LoggerOptions<L> = {}) {
if (options.logger) {
currentLogger = options.logger;
if (!loggers[options.logger]) {
throw new Error(`Logger "${options.logger}" is not registered.`);
}
}
if (options.template) {
template = options.template;
}
if (options.context) {
Object.assign(context, options.context);
}
return loggers[options.logger].with({ template, context });
}
}
// Dynamically create getters for custom loggers
for (const [name, _logger] of Object.entries<LogTapeLogger>(loggers)) {
loggers[name as L] = _logger;
Object.defineProperty(result, name as L, {
get() {
currentLogger = name;
return result;
},
configurable: true,
enumerable: true,
});
}
return result;
}
const logger = createWrapper({
loggers: {
init: initLogger,
build: buildLogger,
customLogger: getLogger(["package", "custom"]),
},
});
Just for clarity when you say shortcuts are you referring to the |
Yeah, they were what I referred to. By the way, I'd like to keep our focus on contexts first. Could we deal with shortcuts in a separate issue? 😅 In the meantime, we could simply use variables and Coming back to contexts, I think the two methods, const logger = getLogger("package");
logger.getChild("init").error("error {message}", { message: "Error message" });
logger.getChild("init").context({ message: "Error message" }).error("error {message}"); |
Ok, that should work, we can discuss the shortcuts in a separate issue. All in all, you get most of what I'm imaging. There is something to note though, we'd probably also want a E.g. function fn() {
return 10;
}
const logger = getLogger("package");
logger.getChild("init").error("error {message}", { message: "Error message" });
logger.getChild("init").context({ message: "Error message" }).message("error {message}").error();
logger.getChild("init").context({ message: "Error message" }).message(fn).error();
// This should stringify `fn` and not apply any magic, ideally we'd let the sink's handle how to stringify none string values
// We'd still keep the context and emit said context as `properties` in the log record |
Hmm, okay. That's basically a syntactic sugar for the below invocation, right? lgoger.getChild("init").context({ message: "Error message" }).error("{fn}", { fn }); |
No, not quite since we'd want the sinks themselves to handle the serialization. I used the example of a function, but a more apt example would be the DOM. On a browser you can log a DOM element to the console, if you were using the console sink you'd want the actual element logged to the console, so you can view it, get live updates, etc... But for the file sink you'd want the DOM element to be a string, so it can be viewed. Basically we'd want the sink to handle the serialization. In this example, by doing this we'd be manually coercing the type of the function into a string before the message is emitted as a log record. logger.getChild("init").context({ message: "Error message" }).error("{fn}", { fn }); To enable what I'm describing we'd ideally want logger.getChild("init").context({ message: "Error message" }).message(fn).error();
// Where `fn` is the message that's passed directly to the log record (with no modifications) Hopefully that makes sense. |
There seems to be a misunderstanding of one fact! Properties are not serialized when it gets into a const element = document.getElementById("element");
console.info(element);
getLogger("test").info("{element}", { element }); |
Oh, I did not know about that...that is pretty cool |
About a method name: Is |
I personally prefer |
Okay, let's go with |
Thanks for taking the time 👍🏽 |
Right now LogTape has some support for what I describe as "context". When I say context, I'm referring to a way to externalized LogTape log function parameters.
Right now you can do
This is awesome but it has a 2 key weaknesses
console.log
where you can log almost anything.The goal is to add context to classes such that these workflows can be supported.
What makes this workflow somewhat interesting is that you can define the properties the log functions should use via
.with({...})
but on each call line the properties are reset to their defaults. Allowing for some pretty cool use cases.In terms of dealing with the first weakness, with the addition of a
.with(...)
method you would be able to just log anything not just strings, the.with(...)
will also help with tagged template functions because you can just use.with(...)
to store the properties.The inspiration for this idea comes from https://jsr.io/@libs/logger/doc/~/Logger which you might find interesting.
The text was updated successfully, but these errors were encountered: