Skip to content

Conversation

@Pijukatel
Copy link
Contributor

@Pijukatel Pijukatel commented Nov 21, 2025

Description

Add the option to log status and status messages of another Actor run. If such Actor run is also redirecting logs from another Actor run, then those logs can propagate all the way to the top through the StreamedLog - deep redirection. If the Actor run from which we are redirecting status messages is not redirecting logs from its children, then the log and status redirection remains shallow - only from that Actor run.

Example usage:

Actor.call - default

This will redirect logs by default. Example default redirected line:

2025-10-22T13:06:39.476Z redirect-log-tester runId:SoK1VRJxG61tVrgNz -> 2025-10-22T13:06:06.686Z ACTOR: Status: FAILED, Message: Different status message

...
await client.actor('redirect-actor-id').call();

Actor.call - custom Log

This will redirect logs using custom log and logger. Example default redirected line:

2025-10-22T13:06:39.476Z customPrefix 2025-10-22T13:06:06.686Z ACTOR: Status: FAILED, Message: Different status message

...
await client.actor('redirect-actor-id').call(someInputs, {
    log: new Log({ level: LEVELS.DEBUG, prefix: 'customPrefix', logger: new LoggerActorRedirect()
    }),

Actor.call - no log redirection

This will disable all log redirection (same as current behavior)

...
await client.actor('redirect-actor-id').call(someInputs, {log: null}),

Actor.run - attaching to already running actor and redirecting statuses and status messages

A typical use case is redirecting statuses and status messages from an Actor that runs in standby.

...
const statusMessageWatcher = await client.run('redirect-run-id').getStatusMessageWatcher();

statusMessageWatcher.start();
// Do some stuff while also redirecting statuses from another Actor run
await statusMessageWatcher.stop();

Example actor with recursive redirection:

https://console.apify.com/actors/IcfIKTIvbmujMmovj/source

Issues

@github-actions github-actions bot added this to the 128th sprint - Tooling team milestone Nov 21, 2025
@github-actions github-actions bot added t-tooling Issues with this label are in the ownership of the tooling team. tested Temporary label used only programatically for some analytics. labels Nov 21, 2025
@Pijukatel Pijukatel changed the title feat: Add StatusSessageWatcher feat: Add StatusMessageWatcher Nov 21, 2025
"url": "https://github.com/apify/apify-client-js/issues"
},
"homepage": "https://docs.apify.com/api/client/js/",
"files": [
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for testing, revert before merging.

@Pijukatel
Copy link
Contributor Author

Pijukatel commented Nov 21, 2025

Example log from nested actor runs and their status redirection:

image

* Helper class for redirecting Actor run status and status message to log.
*/
export class StatusMessageWatcher {
private static finalSleepTimeMs = 6000;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two constants are for sure up for discussion.

@Pijukatel Pijukatel requested review from B4nan and barjin November 21, 2025 14:38
@janbuchar janbuchar marked this pull request as ready for review November 26, 2025 11:45
Copy link
Member

@barjin barjin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @Pijukatel! I know it's already in Python Client, and there's an issue asking for this, but still - I'm not completely convinced we need this feature in the Client.

I have a few opinions, in no particular order:

  1. The status message is a "discrete" piece of data (unlike e.g. log), so polling the API should be the user's job.
  2. (If we still do this), we should allow for consuming the changed status without logging it (e.g. run().onStatus((status) => { user code }))
  3. (If we don't do this), could this be an option for the logger, e.g. .getStreamedLog({ statusMessage: true })? Having a separate method for this doesn't make much sense to me.

cc @janbuchar @B4nan wdyt?

Anyway, regarding the code, a few comments from a quick look:

Comment on lines +307 to +316
const runData = await this.get();
const runId = runData ? `${runData.id ?? ''}` : '';

const actorId = runData?.actId ?? '';
const actorData = (await this.apifyClient.actor(actorId).get()) || { name: '' };

const actorName = runData ? (actorData.name ?? '') : '';
const name = [actorName, `runId:${runId}`].filter(Boolean).join(' ');

return new Log({ level: LEVELS.DEBUG, prefix: `${name} -> `, logger: new LoggerActorRedirect() });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const runData = await this.get();
const runId = runData ? `${runData.id ?? ''}` : '';
const actorId = runData?.actId ?? '';
const actorData = (await this.apifyClient.actor(actorId).get()) || { name: '' };
const actorName = runData ? (actorData.name ?? '') : '';
const name = [actorName, `runId:${runId}`].filter(Boolean).join(' ');
return new Log({ level: LEVELS.DEBUG, prefix: `${name} -> `, logger: new LoggerActorRedirect() });
const runData = await this.get();
const runId = runData?.id ?? '';
const actorId = runData?.actId ?? '';
const actorData = (await this.apifyClient.actor(actorId).get()) || { name: '' };
const actorName = actorData?.name ?? '';
const name = [actorName, `runId:${runId}`].filter(Boolean).join(' ');
return new Log({ level: LEVELS.DEBUG, prefix: `${name} -> `, logger: new LoggerActorRedirect() });

This is the removed code from L282, maybe this PR was made before merging the final version of log streaming?

if (newStatusMessage !== this.lastStatusMessage) {
// Log only when status or status message changed
this.lastStatusMessage = newStatusMessage;
this.toLog.info(newStatusMessage);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idea: we might set the logging level based on the run status?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

t-tooling Issues with this label are in the ownership of the tooling team. tested Temporary label used only programatically for some analytics.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants