How to use `container.logger` ?

I see that there are 4 kinds of loggers: 1. this.container.logger 2. import {container} from '@sapphire/framework'; container.logger 3. import {container} from '@sapphire/pieces'; container.logger 4. import {Logger} from '@sapphire/framework'; const logger = new Logger(); Which type of logger should I use where? a. Inside sapphire command/sub-command classes b. inside sapphire listeners/interaction-handlers/etc c. inside some random utilities file which doesn't import/use anything of sapphire framework d. basically c but that random file is imported inside a or b
19 Replies
MRDGH2821
MRDGH2821OP•15mo ago
Because in some "pieces" this.container.logger didn't work. But that may be due to old version of framework 🤔
Favna
Favna•15mo ago
1-3 are all the same If this.container doesn’t work you’re on framework v1 where container was called context 4 is completely different and is used if you want to define your own custom logger.
MRDGH2821
MRDGH2821OP•15mo ago
So 2, 3 are usable in a, b & c ?
Favna
Favna•15mo ago
I mean obviously not C because you're not importing anything from sapphire lmfao
kyra
kyra•15mo ago
@sapphire/framework's container is a re-export of @sapphire/pieces. In fact, when you extend the container, you augment the latter. As for this.container in pieces, it's just a convenience getter to reduce the number of imports, like this:
public get container() {
return container;
}
public get container() {
return container;
}
MRDGH2821
MRDGH2821OP•15mo ago
Presenting you case d: Which is basically c but that random file is imported inside a or b (refer question for a & b)
Favna
Favna•15mo ago
this is like asking if you can make coffee without coffee beans. It's in the name. if by d you mean this c.ts
export function someFunc() {
container.logger.log('aaa');
}
export function someFunc() {
container.logger.log('aaa');
}
a.ts
import { someFunc } from '../c';
export class MyCommand extends Command {

public async override chatInputRun() {
someFunc();
}
}
import { someFunc } from '../c';
export class MyCommand extends Command {

public async override chatInputRun() {
someFunc();
}
}
then no obviously that wouldn't work. You should revise how JavaScript modules work.
kyra
kyra•15mo ago
1. Used in Sapphire pieces. 2. Used outside pieces in your bot. 3. In the rare occassion you're developing plugins for Sapphire without /framework (like plugin-api). 4. Should only be used when you don't instantiate the Client class in Sapphire, which is what sets it. It's lazy assigned/loaded after all. You can however: - Instantiate Client first, then do whatever you want, login later. - Instantiate Client later, you'll have to create the Logger instance and pass it to logger.instance in the client options. - If you don't instantiate Client (i.e. don't need it or something), you'll always have to create the instance yourself.
MRDGH2821
MRDGH2821OP•15mo ago
I have multiple files like case d which happen like this:
import { join } from 'node:path';
import { LogLevel, SapphireClient } from '@sapphire/framework';
import { getRootData } from '@sapphire/pieces';
import type { ClientOptions } from 'discord.js';
import { GatewayIntentBits, Partials } from 'discord.js';
import './lib/setup';
import './scheduledTasks'; // case d
import './healthInfo';
import { cred } from './lib/Firestore'; //case d

class CustomClient extends SapphireClient {
private rootData = getRootData();

public constructor(options: ClientOptions) {
super(options);
this.stores.registerPath(join(this.rootData.root, 'baseBot'));
this.stores.registerPath(join(this.rootData.root, 'hallOfFame'));
this.stores.registerPath(join(this.rootData.root, 'spiralAbyss'));
this.stores.registerPath(join(this.rootData.root, 'damageLeaderboard'));
}
}

const client = new CustomClient();

const main = async () => {
try {
if (!cred.cert && !cred.path) {
throw new Error('Cannot find Firebase credentials');
}
client.logger.info('Logging in');
await client.login();
client.logger.info('Logged in');
} catch (error) {
client.logger.fatal(error);
client.destroy();
process.exit(1);
}
};

main();
import { join } from 'node:path';
import { LogLevel, SapphireClient } from '@sapphire/framework';
import { getRootData } from '@sapphire/pieces';
import type { ClientOptions } from 'discord.js';
import { GatewayIntentBits, Partials } from 'discord.js';
import './lib/setup';
import './scheduledTasks'; // case d
import './healthInfo';
import { cred } from './lib/Firestore'; //case d

class CustomClient extends SapphireClient {
private rootData = getRootData();

public constructor(options: ClientOptions) {
super(options);
this.stores.registerPath(join(this.rootData.root, 'baseBot'));
this.stores.registerPath(join(this.rootData.root, 'hallOfFame'));
this.stores.registerPath(join(this.rootData.root, 'spiralAbyss'));
this.stores.registerPath(join(this.rootData.root, 'damageLeaderboard'));
}
}

const client = new CustomClient();

const main = async () => {
try {
if (!cred.cert && !cred.path) {
throw new Error('Cannot find Firebase credentials');
}
client.logger.info('Logging in');
await client.login();
client.logger.info('Logged in');
} catch (error) {
client.logger.fatal(error);
client.destroy();
process.exit(1);
}
};

main();
So I think I will have to create a custom logger for case d which are executing before client is starting up 🤔
Favna
Favna•15mo ago
or just use console.log not like there is much to log before init anyway or there shouldnt be
kyra
kyra•15mo ago
client.logger is available instantly after instantiating SapphireClient.
Favna
Favna•15mo ago
these are imports Kyra before the constructor is called
kyra
kyra•15mo ago
In that case you can do the second option here, instantiate it then pass it to logger.instance. In which case, Sapphire will use your instance instead of creating a new one: https://github.com/sapphiredev/framework/blob/main/src/lib/SapphireClient.ts#L282-L283
MRDGH2821
MRDGH2821OP•15mo ago
Thanks for the exact words! If I conclude: The following 3 are same:
this.container.logger // 1
import {container} from '@sapphire/framework'; container.logger // 2
import {container} from '@sapphire/pieces'; container.logger // 3
this.container.logger // 1
import {container} from '@sapphire/framework'; container.logger // 2
import {container} from '@sapphire/pieces'; container.logger // 3
This is different:
import {Logger} from '@sapphire/framework';
const logger = new Logger(); // 4
import {Logger} from '@sapphire/framework';
const logger = new Logger(); // 4
And their usage is like this: 1. Used in Sapphire pieces. 2. Used outside pieces in the bot. For example, utility files of bot. But it will work after Sapphire client is instantiated not before. For "before" cases, use type 4 3. Used In the rare occasion where you're developing plugins for Sapphire without /framework (like plugin-api). I have some "before" cases & some "after" cases. Time to laafo (look around and find out)
Favna
Favna•15mo ago
2. for before just use console.log, the are identical at that point anyway.
MRDGH2821
MRDGH2821OP•15mo ago
I want the formatting of Sapphire's logs in those before cases too, so that logging is consistent
Favna
Favna•15mo ago
suit yourself
kyra
kyra•15mo ago
You can do the 2nd solution I listed, it's compatible with "before" and "after" cases, just instantiate it in one file and import it everywhere in "before" cases:
Create the Logger instance and pass it to logger.instance in the client options. In which case, Sapphire will use your instance instead of creating a new one: https://github.com/sapphiredev/framework/blob/main/src/lib/SapphireClient.ts#L282-L283
MRDGH2821
MRDGH2821OP•15mo ago
Oh yea 🤯 lib/Firestore & scheduledTasks both were using container.logger But since those were imported before the client instance was created, my bot crashed on errors saying some property was undefined. So I ultimately settled with a custom logger for those two files. Others didn't find a problem so it just worked
Want results from more Discord servers?
Add your server