Skip to content
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

poll function logs if the app is dynamic linux. Closes #2571 #2584

Merged
merged 1 commit into from
Apr 19, 2018

Conversation

ahmelsayed
Copy link
Contributor

No description provided.



updateLastInteraction() {
this.lastFunctionDevInteraction = new Date();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this is sort of a hack I think. The other way I think it can be done is to broadcast an event that streaming logs subscribes to. @ehamai what do you think is the right approach here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

actually never mind. I think it should be an event. I need to use it somewhere else too. I'll update the PR

Copy link
Contributor

Choose a reason for hiding this comment

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

+1 for event

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@nertim I updated the PR

@@ -581,25 +581,33 @@ export class FunctionAppService {
.map(r => r.json() as HostStatus));
}

getOldLogs(context: FunctionAppContext, fi: FunctionInfo, range: number): Result<string> {
getLogs(context: FunctionAppContext, fi: FunctionInfo, range?: number, force = false): Result<string> {
Copy link
Contributor

Choose a reason for hiding this comment

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

range [](start = 59, length = 5)

Why is range optional now?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This method used to be only used before we start streaming request for logs.
The range was used to retrieve the last range bytes from the latest log file. That way when you switch to a new function, you get to see the last range bytes of the log file then the log streaming starts after it.

When polling, we don't need to pass a range, since we can just get the whole last file and update the UI with the content of that file. It makes the behavior slightly different between polling and streaming logs, but I think that's okay. The goal is to eventually remove both approaches and have an API that will return the set of logs for a given invocation, and use the Errors and Warnings for compilation errors.

Copy link
Contributor

Choose a reason for hiding this comment

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

would it be better to split the function in that case? getPollingLogs and getStreamingLogs ? factor out the common pieces in helper methods. Like that we can avoid branching within this method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure the labels (getPollingLogs, getStreamingLogs) would make sense here since it's more of the caller who is either polling or streaming. This method is more generic in that it just retrieves the latest log either with a range or not.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

maybe rename getLogs to getLatestFunctionLogs?

@@ -742,4 +748,9 @@ export class FunctionDevComponent extends FunctionAppContextComponent implements
}
});
}


Copy link
Contributor

Choose a reason for hiding this comment

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

nit extra line

@@ -581,25 +581,33 @@ export class FunctionAppService {
.map(r => r.json() as HostStatus));
}

getOldLogs(context: FunctionAppContext, fi: FunctionInfo, range: number): Result<string> {
getLogs(context: FunctionAppContext, fi: FunctionInfo, range?: number, force = false): Result<string> {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit. force: boolean = false

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sure, though TypeScript can infer that
image

* @param newLogs string to use to update the update the displayed logs with
* @param oldLogs logs to prepend to newLogs. This is only used in streaming scenario
*/
private updateLogs(newLogs: string, oldLogs?: string): number {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit. prefix with '_'

});

return promise;
return newLogs.length + (oldLogs || '').length;
Copy link
Contributor

Choose a reason for hiding this comment

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

newLogs.length + (oldLogs || '') [](start = 15, length = 32)

should this be this.log.length?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah, I suspect that you're probably right. Though this is the existing logic. I just tried to reduce copy-paste so I moved it to it's own function. I'd rather not change it because there is a lot of odd math going on to handle some random cases we had before and it's not well documented why it's this way (I added all that logic a time ago and don't remember all the intricacies about why it's the way it's now)

Even at the time I added all that, the plan was for it to be temporary until we get an API that gives us the logs for an invocation. We are probably closer now, so I'm tempted to leave this funny math as is until that happend.

} else {
this.log = oldLogs
? oldLogs + newLogs.substring(newLogs.indexOf('\n') + 1)
: newLogs;
Copy link
Contributor

Choose a reason for hiding this comment

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

should these logs get truncated based on the maxCharsInLog?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes.

The only "overage" that can happen is if oldLogs + newLogs are > maxCharactersInLog but we know that oldLogs are at the most 10,000 bytes (or 10,000 characters, probably unless there is unicode in there) so I figured 500,000 +- 10,000 isn't that much off.

All the math in here is too complicated and we should simplify it once there is a specific logs API. I'll update this to truncate based on maxChars though

Copy link
Contributor

@nertim nertim left a comment

Choose a reason for hiding this comment

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

Sorry for all the questions :(...

I think there is a lot of math happening in a lot of places.

const nextInterval = diff - oldLogs.length > intervalIncreaseThreshold ? this.timerInterval + defaultInterval : this.timerInterval - defaultInterval;
if (nextInterval < defaultInterval) {
this.timerInterval = defaultInterval;
} else if (nextInterval > maxInterval) {
this.timerInterval = defaultInterval;
Copy link
Contributor

Choose a reason for hiding this comment

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

defaultInterval [](start = 41, length = 15)

This is existing code, but is this correct? if so, can we combine the two if statements?

if (next < default || next > max)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

you're right

@ahmelsayed
Copy link
Contributor Author

ahmelsayed commented Apr 19, 2018

@nertim so since I'm mainly trying to reduce any regressions to what's there today (especially since the math is pretty complicated), I changed the PR to not touch existing logic in the streaming case, and just add new logic for the polling case. #Resolved

@nertim
Copy link
Contributor

nertim commented Apr 19, 2018

sounds good.


In reply to: 382588602 [](ancestors = 382588602)

Copy link
Contributor

@nertim nertim 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 for your patience with my comments Ahmed! :)

// if logging isn't stopped
.filter(() => !this.stopped)
// and until this.pollingActive OR 5 minutes have passed, which ever comes first
.takeUntil(this.pollingActive$.merge(Observable.timer(5 * 60 * 1000)))
Copy link
Contributor

Choose a reason for hiding this comment

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

.takeUntil(this.pollingActive$.merge(Observable.timer(5 * 60 * 1000))) [](start = 12, length = 70)

nice!

@@ -174,7 +230,7 @@ export class LogStreamingComponent extends FunctionAppContextComponent implement
this.xhReq.setRequestHeader('FunctionsPortal', '1');
this.xhReq.send(null);
if (!createEmpty) {
this._functionAppService.getOldLogs(this.context, this.functionInfo, 10000).subscribe(r => oldLogs = r.result);
this._functionAppService.getLogs(this.context, this.functionInfo, 10000).subscribe(r => oldLogs = r.result);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

It's probably helpful to explain why you might be doing this even in the streaming case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added a comment explaining

Copy link
Contributor

@ehamai ehamai left a comment

Choose a reason for hiding this comment

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

:shipit:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants