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

Initial application logging setup #1102

Merged
merged 1 commit into from
Oct 28, 2021
Merged

Initial application logging setup #1102

merged 1 commit into from
Oct 28, 2021

Conversation

carolinemodic
Copy link
Contributor

@carolinemodic carolinemodic commented Oct 22, 2021

Initial framework for logging from client side applications in addition to a sample implementation in election-manager. This will likely change over time as I solidify things.

Its possible it might make sense for /logging to be its own repo so it can also be used in places like kiosk-browser.... but that would add friction whenever you need to add a new log so not sure the tradeoff there.

#1095
#1096
#1097

disposition = LogDispositionStandardTypes.NotApplicable,
...additionalData
} = logData;
const logLine = {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This doesn't have a type annotation but probably should.

...additionalData,
};
if (this.isClientSide) {
await fetch('/log', {
Copy link
Collaborator

Choose a reason for hiding this comment

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

If we're posting an HTTP service then we should include a time stamp (and probably an incrementing counter) to ensure the server can properly reconstruct the order (and maybe identify any missing logs?).

NotApplicable = 'na',
}

export type LoggingUserRole = CardDataTypes | 'vx-admin' | 'system';
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should these be from the LogSource enum with the same values? Should this be an enum when all the other similar types are too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

LogSource is the application making the log, this is the user causing the log to happen, vx-admin here is meant to represent a votingworks employee making a change, vs it referring to the name of election manager in LogSource but I see that that is confusing so I'll come up with a better name here.

@@ -0,0 +1,16 @@
{
"extends": "./tsconfig.json",
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the better split here is actually tsconfig.build.json (for building, which excludes tests) and tsconfig.json (for IDE/type checking).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oh I see, I was just copying how utils is set up.

app.use(express.json({ limit: '5mb', type: 'application/json' }));
app.post('/log', (req, res) => {
console.log(req.body)
res.json({success: true})
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: formatting

@@ -39,6 +39,9 @@
{
"path": "libs/ui"
},
{
"path": "libs/logging"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Don't forget to add this library to the CircleCI config too.

export enum LogEventId {
ElectionConfigured = 'election-configured',
ElectionUnconfigured = 'election-unconfigured',
MachineBoot = 'machine-booted',
Copy link
Collaborator

Choose a reason for hiding this comment

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

This feels more like "application started" than actual machine boot.

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 log is defined here for completeness its not going to be logged from the application it will be a reprocessed version of the actual machine boot log from the syslog.

const calls = fetchMock.calls('/log');
expect(calls).toHaveLength(1);
assert(calls[0] && calls[0][1]);
expect(calls[0][1]).toMatchObject(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Avoid the previous assert:

Suggested change
expect(calls[0][1]).toMatchObject(
expect(calls[0]?.[1]).toMatchObject(

Copy link
Contributor Author

Choose a reason for hiding this comment

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

lol for some reason I keep thinking when I'm coding that avoiding using "!" also means we can't use "?" which is obviously not true. Thanks for catching.

@carolinemodic carolinemodic force-pushed the caro/logging/poc branch 2 times, most recently from fc9b0b8 to f3bd548 Compare October 22, 2021 23:26
Copy link
Contributor

@benadida benadida left a comment

Choose a reason for hiding this comment

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

wondering about architecture, still thinking that a kiosk-browser API would introduce fewer moving parts than a service.

Comment on lines 54 to 57
app.post('/log', (req, res) => {
console.log(JSON.stringify(req.body))
res.json({ success: true })
})
Copy link
Contributor

Choose a reason for hiding this comment

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

I thought design doc had refocused on using a kiosk-browser call instead of an HTTP call?

I think I would prefer a kiosk-browser call instead of an additional service running, but maybe I'm missing a key reason for this?

Copy link
Collaborator

Choose a reason for hiding this comment

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

It's not really an additional service (yet), but yeah I had the same question.

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 isn't an additional service? I guess I'm not really seeing what the downside to this approach is, when its just 4 lines to implement in each of our front-end apps and my understanding of how https calls work when we are just going over localhost and not the internet was that we don't really have to have the same reliability/speed concerns as we would over the internet but correct me if I'm wrong there.

I don't think its reasonable to move all of the types and log event id definitions into kiosk-browser as that would be a huge pain for development when we will likely be needed to add new log types pretty frequently alongside implementing other features. So we would still need to have the type definitions and the Logger class wrapper in vxsuite-complete-system, also so that we can reuse the same code for logging from the server-side, and we would then need to pass window.kiosk to the Logger to make that idea work which feels kind of convoluted to me, but I suppose it would be doable.

The other thing I'm unsure about in that approach if how confusing it might be when debugging things that the logs go to kiosk-browser and that you would need to be running the app through kiosk-browser (which I thought I'm the only person who regularly does?) in order to see the logs, which might be kind of annoying.

I can try to play around with this and make a PR for how that version would look but I guess I'm still just missing what the downside is with this approach.

Copy link
Collaborator

Choose a reason for hiding this comment

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

The types part is a good point, though maybe it's just as simple as your setupProxy thing where it just forwards to console.log and doesn't care about the types. If not running in kiosk-browser it could just console.log. I'm not sure which approach I prefer. I'm curious to hear more from @benadida.

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 thats also what I was thinking we would do in the kiosk-browser approach.

Somewhat separately though from this decision kiosk-browser will probably need to make its own logs, so I'm not totally sure how to best handle that with the types, if I should just double-implement the kiosk-browser ones in kiosk-browser and vxsuite (so the UI reading this to display logs and the code to transform the logs to the CDF format knows the types), or if I should try to import the logging package into kiosk-browser somehow.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I've been thinking we should have CI publish from main with a version like 1.0.0-abcdefg using the git hash, that way we can reference monorepo things from the outside.

Copy link
Contributor

Choose a reason for hiding this comment

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

@carolinemodic good point on the types, yeah, I wouldn't want you to have to import them all into kiosk-browser just for this. I was thinking more along the lines of what @eventualbuddha describes, where kiosk-browser implements just a pipe of text to stdout.

Thinking about this some more... I think I care less than I originally thought about whether it's a kiosk-browser call vs. a /log call. I lean a little bit in the direction of kiosk-browser, because adding a /log call turns the server from a pure server of static content to one that does a little bit more than that... but that's just one data point, and if you prefer the /log approach @carolinemodic I'm comfortable with it.

Quick thought about kiosk-browser logging. We've build kiosk-browser as a generic tool, and I think it would be great if we keep it that way. I would suggest that the web content can register for certain events from kiosk-browser and, upon getting notified, can log it. A good example of this already happening is how we register for printers being connected/disconnected --> content gets notified and can /log it. I'm thinking we may be able to do that for any other events from kiosk-browser. What do you think @carolinemodic ? (Also, this could be an entirely different discussion that doesn't belong in this ticket.)

Bottom line:

  • I stll lean very slightly in the direction of a kiosk-browser .log API, but after hearing @carolinemodic out I'm fully comfortable with the /log API call instead. Please feel free to do whatever feels right to you @carolinemodic.
  • I think it might be best if we can keep kiosk-browser logging within the same framework: content notification and then the content logs. The good part of this is that we don't need to include the logging library in kiosk-browser, which I think is best left as a standalone system. But to be clear, I'm still happy to discuss this.

@@ -1,7 +1,7 @@
declare namespace KioskBrowser {
export interface BatteryInfo {
discharging: boolean
level: number // Number between 0–1
discharging: boolean;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

why was the file not already semi-colon-ified?

debug(logLine); // for internal debugging use log to the console
await this.kiosk.log(
JSON.stringify({
timeLogInitiated: Date.now().toString(),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@eventualbuddha With using kiosk-browser instead of a POST request is there still a risk that things get sent out of order? Do you think it's still important to have this extra time (the logs will also have the time the console.log call gets made)?

eventType: LogEventType.UserAction,
defaultMessage: 'Application has been configured for a new election.',
documentationMessage:
'Message that the user has configured current machine to a new election definition.',
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe no need for Message that at the beginning of these documentation messages?

eventId: LogEventId.MachineBoot,
eventType: LogEventType.HardwareAction,
documentationMessage:
'Log that appears when the machine has successfully booted.',
Copy link
Contributor

Choose a reason for hiding this comment

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

same, maybe just "the machine has successfully booted."

eventId: LogEventId.MachineBoot,
eventType: LogEventType.HardwareAction,
user: 'system',
message: 'I come back stronger than a 90s trend',
Copy link
Contributor

Choose a reason for hiding this comment

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

💯

@carolinemodic carolinemodic merged commit 3277387 into main Oct 28, 2021
@carolinemodic carolinemodic deleted the caro/logging/poc branch October 28, 2021 21:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants