-
Notifications
You must be signed in to change notification settings - Fork 133
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
Conversation
|
||
|
||
updateLastInteraction() { | ||
this.lastFunctionDevInteraction = new Date(); |
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 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?
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.
actually never mind. I think it should be an event. I need to use it somewhere else too. I'll update the PR
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.
+1 for event
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.
@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> { |
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.
range [](start = 59, length = 5)
Why is range optional now?
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 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.
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.
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.
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 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.
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 rename getLogs
to getLatestFunctionLogs
?
@@ -742,4 +748,9 @@ export class FunctionDevComponent extends FunctionAppContextComponent implements | |||
} | |||
}); | |||
} | |||
|
|||
|
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.
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> { |
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.
nit. force: boolean = false
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.
* @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 { |
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.
nit. prefix with '_'
}); | ||
|
||
return promise; | ||
return newLogs.length + (oldLogs || '').length; |
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.
newLogs.length + (oldLogs || '') [](start = 15, length = 32)
should this be this.log.length?
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.
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; |
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.
should these logs get truncated based on the maxCharsInLog?
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.
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
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.
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; |
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.
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)
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.
you're right
@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 |
sounds good. In reply to: 382588602 [](ancestors = 382588602) |
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.
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))) |
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.
.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); | |||
} |
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's probably helpful to explain why you might be doing this even in the streaming 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.
I added a comment explaining
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.
No description provided.