--- title: "App Logs" slug: "app-logs" updated: 2026-02-23T04:05:06Z published: 2026-02-23T04:05:06Z canonical: "developer.rocket.chat/app-logs" --- > ## Documentation Index > Fetch the complete documentation index at: https://developer.rocket.chat/llms.txt > Use this file to discover all available pages before exploring further. # App Logs App logs can be viewed from the app’s **Logs** tab. Here, the app’s activities are displayed, allowing admins to analyze what happened and debug issues. For an overview of the types of logs available, refer to the [App Development Lifecycle](/v1/docs/app-development-lifecycle) document. In this document, we will learn how to: - Access the logger correctly. - Structure slash commands, endpoints, and helper classes. - Handle the special case of scheduler processors. ## How to access the logger The **ILogger** interface is used to log events at different levels to the system and the database. It is automatically imported into your app’s main class. Refer to the [Rocket.Chat Apps TypeScript Definition](https://rocketchat.github.io/Rocket.Chat.Apps-engine/interfaces/accessors_ILogger.ILogger.html) for details on the interface’s methods and properties. Let’s learn how to access the logger in different cases. > [!WARNING] > Rocket.Chat version **8.2.0** fixes an issue where app logs were lost in nested requests. The [PR #38374](https://github.com/RocketChat/Rocket.Chat/pull/38374) ensures that the logger is persisted throughout the request handling with no mix-ups. This creates some requirements for app developers that should be kept in mind. This document guides you on how to access the logs correctly. ### Inside the `App` subclass The following code snippet shows you how to access the logger: ```typescript import {    IAppAccessors,    ILogger, } from '@rocket.chat/apps-engine/definition/accessors'; import { App } from '@rocket.chat/apps-engine/definition/App'; import { IAppInfo } from '@rocket.chat/apps-engine/definition/metadata'; export class HelloWorldApp extends App {    constructor(info: IAppInfo, logger: ILogger, accessors: IAppAccessors) {        super(info, logger, accessors);        this.getLogger().debug('some_message', extraData);        this.getLogger().error('something_went_wrong', { err });    } } ``` Use `this.getLogger()` from within your `App` subclass. Call the logger only when you need it. For example: ```typescript // ✅ Recommended export class MyApp extends App { public async initialize(/* ... */) { this.getLogger().debug('init'); } } ``` The engine will provide a correctly scoped logger for every request. **Do not** cache the logger across executions as shown in the following code snippet: ```typescript // ❌ Anti‑pattern: caching the logger instance export class MyApp extends App { private logger = this.getLogger(); public async initialize(/* ... */) { // This may use a logger from a different request context this.logger.debug('init'); } } ``` ### Inside external classes External classes refer to the classes that don’t extend `App`, such as slash commands, API endpoints, providers, and helpers. In these classes, the logger must be called in the following way: - Keep a reference to the app object internally. - Use that reference to obtain the logger. The following sample code snippet shows the recommended pattern: ```typescript // ✅ Recommended import type { App } from '@rocket.chat/apps-engine/definition/App'; import type { IHttp, IModify, IPersistence, IRead } from '@rocket.chat/apps-engine/definition/accessors'; import type { ISlashCommand, ISlashCommandPreview, ISlashCommandPreviewItem, SlashCommandContext } from '@rocket.chat/apps-engine/definition/slashcommands'; export class MySlashcommand implements ISlashCommand { /* Required properties omitted for brevity */ constructor(private readonly app: App) {} // This is the reference public async executor(context: SlashCommandContext, read: IRead, modify: IModify, http: IHttp, persis: IPersistence): Promise { this.app.getLogger().debug('Hello!'); // Here the reference is called for logging } } ``` Here, `MySlashcommand` is a separate class that implements `ISlashCommand`. It does not extend `App`. To reach the logger, it uses a reference to the app instance it stores internally. Keeping the app reference (`this.app`) allows the runtime to replace that app with a request-scoped proxy when executing your code, so that `this.app.getLogger()` always maps to the right logger. **Do not** capture the logger itself at the time of construction as shown in the following example: ```typescript // ❌ Anti‑pattern export class MySlashcommand implements ISlashCommand { /* Required properties omitted for brevity */ private logger: ILogger; constructor(app: App) { this.logger = app.getLogger(); } public async executor(context: SlashCommandContext, read: IRead, modify: IModify, http: IHttp, persis: IPersistence): Promise { this.app.getLogger().debug('Hello!'); } } ``` ### Endpoints, outbound providers, and other composable objects Any “provider” object that the engine executes for you (API endpoints, slash command classes, outbound communication providers, etc.) should follow the same pattern: - Keep a reference to the app. - Log via `this.app.getLogger()`. The following code snippet shows an example: ```typescript import type { ApiEndpoint, IApiRequest, IApiResponse, } from '@rocket.chat/apps-engine/definition/api'; import type { App } from '@rocket.chat/apps-engine/definition/App'; export class MyEndpoint implements ApiEndpoint { public path = 'my-endpoint'; constructor(private readonly app: App) {} // The reference to App public async get(request: IApiRequest): Promise { this.app.getLogger().debug('my_endpoint_get_called', { query: request.query }); // The reference is called for logging return { status: 200, content: { ok: true }, }; } } ``` ### Scheduler processors Scheduler processors are a special case for logging because of how they are registered. Take a look at the following example: ```typescript public async extendConfiguration(configuration: IConfigurationExtend) { configuration.scheduler.registerProcessors([ { id: 'first', // `this` is lexically bound at definition time, not set by the runtime processor: async (jobData) => this.getLogger().debug(`[${ new Date() }] this is a task`, jobData), }, ]); } ``` It is common to register the processor function as an arrow function. But this poses a challenge for us to inject the correct app reference to its execution. In this case, you have two options for logging: 1. Register a reference to a method inside your `App` class without binding `this`. This is the most ergonomic option. Refer to the following code sample: ```typescript export class SimpleMessageEndpointApp extends App { public async extendConfiguration(configuration: IConfigurationExtend) { configuration.scheduler.registerProcessors([ { id: 'MyProcessor', // NOTE: make sure NOT TO `.bind(this)` ! processor: this.myProcessorHandler, }, ]); } private async myProcessorHandler(jobData): Promise { this.getLogger().debug('Hello there!') } } ``` 1. The second option is to register a regular function, either named or anonymous, and use this as a reference to your `App` instance (which the runtime injects later on). While this is possible, it may be confusing for eventual maintenance. Refer to the following code sample: ```typescript public async extendConfiguration(configuration: IConfigurationExtend) { configuration.scheduler.registerProcessors([ { id: 'MyProcessor', processor: async function MyProcessorHandler(jobData) { this.getLogger().debug(`[${ new Date() }] this is a task`, jobData); }, }, ]); } ```