-
Notifications
You must be signed in to change notification settings - Fork 38
feat: Add redirected actor logs #769
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
base: master
Are you sure you want to change the base?
Conversation
Properly populate actor id and name
TODO: Try in actor
| "url": "https://github.com/apify/apify-client-js/issues" | ||
| }, | ||
| "homepage": "https://docs.apify.com/api/client/js/", | ||
| "files": [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just temp for testing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just curious, what are you testing this way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have an Actor that just points to this branch and I can test the tip of it on the Apify platform as it compiles on the fly. It takes time, but it does not require me to do any manual actions.
The actor has in pacakge.json
"dependencies": {
"apify-client": "github:apify/apify-client-js#log-redirection",
...
},
"scripts": {
...
"postinstall": "cd node_modules/apify-client && npm install && npm run build:node"
},
...
"overrides": {
"apify-client": "github:apify/apify-client-js#log-redirection"
},
and I removed --omit=dev from npm install in the docker file
If there is a more convenient way for TypeScript-based code, I would be very happy to learn about it.
|
@barjin @janbuchar can you guys please take a look at this and provide some early feedback? I'll be off tomorrow+monday, so it would have to wait a bit for me. |
src/resource_clients/actor.ts
Outdated
|
|
||
| export interface ActorCallOptions extends Omit<ActorStartOptions, 'waitForFinish'> { | ||
| waitSecs?: number; | ||
| log?: Log | null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The difference from Python is the name of the argument. While in Python it is called logger and can be of type Logger, here it is log and can be of type Log.
In JS tooling, the Log is a kind of Logger from Python, as it (implicitly) includes level, formatting, and handler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the reason for accepting null here? Wouldn't log?: Log work as well? If there are any semantics that I'm failing to see, please document them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
undefined -> use default
null -> explicitly requesting not to use any redirect logger
Log -> use whatever log is passed
In Python, it is similar, but since there is no undefined, there it is like
"default" -> use default
None -> explicitly requesting not to use any redirect logger
Logger -> use whatever Logger is passed
Added description
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't using a string literal like in Python work as well? I'd consider that more readable, too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't that going to cause more confusion? Aren't the default values in JS commonly applied when the key is undefined in options?
Using default would allow to define the default value through both undefined and default literal
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried in the last commit. It feels very redundant to me, but I can live with it
3e48c60
barjin
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I took a brief look, I don't see any obvious JS problems. Good job!
Here are some first ideas I had reading this:
4a3f418 to
bbeeee4
Compare
c1a034a to
a54e71f
Compare
a54e71f to
51dd246
Compare
barjin
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good! I have some points to discuss, but most of them are questions rather than apparent problems:
| console.log(line); // eslint-disable-line no-console | ||
| } | ||
|
|
||
| override _log(level: LogLevel, message: string, data?: any, exception?: unknown, opts: Record<string, any> = {}) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering, what's the biggest difference between this implementation and the _log from LoggerText (link)? Could we subclass LoggerText instead of abstract Logger and reduce the amount of code here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logger in our codebase is kind of a combination of a formater and a handler in Python terms. While LoggerActorRedirect has the same handler as LoggerText (they are both logging to console), they have completely different formater. So there is no point in inheriting from LoggerText as none of its methods would be reused in LoggerActorRedirect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we need to adjust the shared logger to support this use case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not really needed for this change. This change works with the current version of the codebase. Changing Log and Logger from the upstream repo to be more similar to the Python counterpart would be quite a refactoring.
|
|
||
| let maybeDate = ''; | ||
| if (!this.options.skipTime) { | ||
| maybeDate = `${new Date().toISOString().replace('Z', '').replace('T', ' ')} `; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need to repeat the timestamps?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those timestamps will not be the same. One is the timestamp of origin, and the other is the timestamp of redirection.
For example>
2025-10-31T09:04:03.380Z redirect-log-tester runId:V4OVPjPbmjflPDeMP -> 2025-10-31T09:03:26.899Z ACTOR: Pulling container image of build KNlNPqgcBBivdtNCr from registry.
It is important to
- keep logs formatted in the same way. (Otherwise, redirected logs would be offset by not having the timestamp) - which is used to detect the actual log lines separations.
- to keep it clear what the time of log redirection is and what the time of the actual log origin is (for redirected logs from actors that were started before we started redirection and to which we just attached)
src/resource_clients/log.ts
Outdated
| raw?: boolean; | ||
| } | ||
|
|
||
| // Temp create it here and ask Martin where to put it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is where the apify logger is defined, so maybe there?
https://github.com/apify/apify-shared-js/tree/master/packages/log
it doesnt feel right to have this in the client
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure about that. I can't imagine a scenario where this would be used outside of the client. It is very client-specific code. Maybe I can put it in to separate file? Something like redirection_utils or logging_utils?
| "url": "https://github.com/apify/apify-client-js/issues" | ||
| }, | ||
| "homepage": "https://docs.apify.com/api/client/js/", | ||
| "files": [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just curious, what are you testing this way?
src/resource_clients/actor.ts
Outdated
|
|
||
| export interface ActorCallOptions extends Omit<ActorStartOptions, 'waitForFinish'> { | ||
| waitSecs?: number; | ||
| log?: Log | null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the reason for accepting null here? Wouldn't log?: Log work as well? If there are any semantics that I'm failing to see, please document them.
6504a69 to
8eb6e71
Compare
janbuchar
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only a couple of nits. Also, some of my previous comments remain unanswered, it seems 🙂
I assume you tested this against Apify already?
src/resource_clients/actor.ts
Outdated
|
|
||
| export interface ActorCallOptions extends Omit<ActorStartOptions, 'waitForFinish'> { | ||
| waitSecs?: number; | ||
| log?: Log | null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't using a string literal like in Python work as well? I'd consider that more readable, too.
Tested for example, here: |
Description
rawparameter.Example usage:
Actor.call - default
This will redirect logs by default. Example default redirected line:
Actor.call - custom Log
This will redirect logs using custom log and logger. Example default redirected line:
Actor.call - no log redirection
This will disable all log redirection (same as current behavior)
Actor.run - attaching to already running actor and redirecting new logs
A typical use case is redirecting logs from an Actor that runs in standby. We do not want all the logs; we only want the new logs generated from the moment of connection.
Example actor with recursive redirection:
https://console.apify.com/actors/IcfIKTIvbmujMmovj/source
Issues
.actor().call()method to set the correct timeout, show the progress in status message, and stream logs #632