Skip to content

Adds structured logging API. #665

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

Merged
merged 14 commits into from
Jun 2, 2020
Merged

Adds structured logging API. #665

merged 14 commits into from
Jun 2, 2020

Conversation

mbleigh
Copy link
Contributor

@mbleigh mbleigh commented Apr 21, 2020

Node 10 runtime will soon support structured logging by emitting single-line JSON-encoded data to STDOUT / STDERR. This adds a new firebase-functions/logger export that provides structured logging in easy-to-use methods as well as firebase-functions/logger/compat that patches system console methods to output structured logs, solving the "multi-line log" problem for Node 10.

TODO

  • Write reference docs
  • Write tests
  • Get API finalized / approved internally

@mbleigh mbleigh requested a review from laurenzlong April 22, 2020 21:27
@mbleigh mbleigh marked this pull request as ready for review April 22, 2020 21:27
@mbleigh
Copy link
Contributor Author

mbleigh commented Apr 22, 2020

@laurenzlong for code review
@egilmorez for reference doc review

@@ -0,0 +1,135 @@
// Determine if structured logs are supported (node >= 10). If something goes wrong,
import { format } from 'util';
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this import got in the middle of two lines of comments?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops, fixed.


// Map LogSeverity types to their equivalent `console.*` method.
const CONSOLE_SEVERITY: {
[severity: string]: 'debug' | 'info' | 'warn' | 'error';
Copy link
Contributor

Choose a reason for hiding this comment

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

(I'm not sure if TS would let you do this, but if it does). This would be clearer as:
[severity: LogSeverity]: 'debug' | 'info' | 'warn' | 'error';

Copy link
Contributor Author

Choose a reason for hiding this comment

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

TS doesn't let you do this 😢

Copy link
Contributor

Choose a reason for hiding this comment

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

😢

CHANGELOG.md Outdated
@@ -0,0 +1,16 @@
- Adds `functions.logger` SDK to enabled structured logging in the Node.js 10 runtime. Example:
Copy link
Contributor

Choose a reason for hiding this comment

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

Should add that in Node.js versions < 10, the method still is supported but the data is displayed in a single line.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

import { format } from 'util';

// safely preserve unpatched console.* methods in case of compat require
const unpatchedConsole = {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this still won't work if somebody did this:

require('firebase-functions/logger/compat');
// Already overwrote console.* methods before requiring "firebase-functions"
const functions = require('firebase-functions');
// unpatchedConsole is actually the patched console
functions.logger.info('something') // will probably do weird things

I think there's many opportunities for there to be weird bugs and edge cases with 'compat', and I'm not sure it's worth the incremental convenience of providing this functionality.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It does work, because the first thing compat does is require logger which preserves the unpatched console methods. Added a comment to clarify.

This wasn't in my original proposal but Kato asked if it was possible and it turned out to be easier than expected. I do worry about the chance for bugs here but this is just one way to use the SDK and provides a convenience. Anyone who encounters bugs we can just tell to use the other ways of using the SDK.

Copy link
Contributor

@laurenzlong laurenzlong Apr 27, 2020

Choose a reason for hiding this comment

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

Ah yes, compat does import logger first, this is really subtle behavior, it's good you added a comment to clarify.

I'm still not a fan of compat and I think it's just making this SDK harder to maintain (it's already way too complicated and has too many subtle behaviors) But if you feel like the risk is worth the user experience benefit, we can keep it in.

@mbleigh mbleigh requested a review from laurenzlong April 24, 2020 20:53
import { format } from 'util';

// safely preserve unpatched console.* methods in case of compat require
const unpatchedConsole = {
Copy link
Contributor

@laurenzlong laurenzlong Apr 27, 2020

Choose a reason for hiding this comment

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

Ah yes, compat does import logger first, this is really subtle behavior, it's good you added a comment to clarify.

I'm still not a fan of compat and I think it's just making this SDK harder to maintain (it's already way too complicated and has too many subtle behaviors) But if you feel like the risk is worth the user experience benefit, we can keep it in.


// Map LogSeverity types to their equivalent `console.*` method.
const CONSOLE_SEVERITY: {
[severity: string]: 'debug' | 'info' | 'warn' | 'error';
Copy link
Contributor

Choose a reason for hiding this comment

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

😢

Copy link
Contributor

@egilmorez egilmorez left a comment

Choose a reason for hiding this comment

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

Thanks Michael! Some style things for you to look at.

CHANGELOG.md Outdated
@@ -0,0 +1,18 @@
- Adds `functions.logger` SDK to enabled structured logging in the Node.js 10 runtime. Example:
Copy link
Contributor

Choose a reason for hiding this comment

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

Typo, should be "to enable"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -0,0 +1,18 @@
- Adds `functions.logger` SDK to enabled structured logging in the Node.js 10 runtime. Example:

Copy link
Contributor

Choose a reason for hiding this comment

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

A nit, but in a text flow we prefer "For example:"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -0,0 +1,18 @@
- Adds `functions.logger` SDK to enabled structured logging in the Node.js 10 runtime. Example:

```js
Copy link
Contributor

Choose a reason for hiding this comment

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

Wow, does this work? In the d.ts files we use ```javascript, but a shorter form would be nice.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup 😄

CHANGELOG.md Outdated
require('firebase-functions/logger/compat');
```

In older runtimes, the logger will print to console, no structured data is saved.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggest "logger prints to the console, and no..."

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -0,0 +1,135 @@
import { format } from 'util';

// safely preserve unpatched console.* methods in case of compat require
Copy link
Contributor

Choose a reason for hiding this comment

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

Will these be parsed into the reference by our TypeDoc script? If so, suggest "//Safely . . . require." with init cap and period.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nope.

const SUPPORTS_STRUCTURED_LOGS =
parseInt(process.versions?.node?.split('.')?.[0] || '8', 10) >= 10;

// Map LogSeverity types to their equivalent `console.*` method.
Copy link
Contributor

Choose a reason for hiding this comment

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

If this goes to public docs, let's backtick LogSeverity or just say "log severity" in the text flow.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Won't be in public docs.

src/logger.ts Outdated
};

/**
* LogSeverity indicates the detailed severity of the log entry. See [Cloud Logging docs](https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#logseverity) for more.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggest backticks for LogSeverity

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

src/logger.ts Outdated
};

/**
* LogSeverity indicates the detailed severity of the log entry. See [Cloud Logging docs](https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#logseverity) for more.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggest the actual doc section title, LogSeverity

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

src/logger.ts Outdated
| 'EMERGENCY';

/**
* LogEntry represents a structured Cloud Logging entry. All keys aside from `severity` and `message` are
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggest backticks for LogEntry

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

src/logger.ts Outdated
}

/**
* Writes a LogEntry to `stdout`/`stderr` (depending on severity).
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggest backticks for LogEntry (and every other literal that appears in the HTML output for the reference docs).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@rhodgkins
Copy link
Contributor

Any ideas when this is due to be merged/released?

@sk-
Copy link
Contributor

sk- commented May 26, 2020

@mbleigh Will this PR correctly display the log severity when using firebase-functions/logger/compat? See https://issuetracker.google.com/issues/124403972 for context.

@mbleigh
Copy link
Contributor Author

mbleigh commented May 26, 2020

@sk- yes, although I'm waiting for some corresponding changes to land in the Firebase console before merging and releasing this as we need to adjust for some changes to the way logs are structured in Node 10.

@mbleigh mbleigh merged commit bb92d38 into master Jun 2, 2020
@mbleigh mbleigh deleted the mb-logger branch June 2, 2020 16:31
@sk-
Copy link
Contributor

sk- commented Jun 3, 2020

@mbleigh I can confirm that the severity is respected, at least on Cloud Logging (I don't see it on Firebase yet).

However there are some issues:

  1. The log entry in Cloud Logging is double quoted.

Screen Shot 2020-06-03 at 11 32 05 AM

2) The `message` field does not populate the `textPayload` log field, so one needs to change the queries to use `jsonPayload.message` instead. Is there any way to keep using `textPayload` for the queries? As it is now, using the compat module would be a breaking change, as it would break any metrics one may have.

@mbleigh
Copy link
Contributor Author

mbleigh commented Jun 3, 2020

Hmm...interesting. Will try to dig into this a bit more before release. The way this works is using the container logs contract from Cloud Run (newer Cloud Functions runtimes are built on the same infra).

It seems like it should be populating textPayload so long as there is no structured data attached to the log.

@sk-
Copy link
Contributor

sk- commented Jun 3, 2020

@mbleigh You are right, if there is no structured data (meaning no extra fields other than message and severity) the logs do populate textPayload and are displayed without the double quotes.

By reading the logs contract docs, it only says about the message field that it is used as the display text, but mentions nothing of the special case when only these two fields are populated.

It'd be great if textPayload was always populated, and no double quotes were added to the displayed message.

@mbleigh
Copy link
Contributor Author

mbleigh commented Jun 3, 2020

Let's continue this conversation on #697 since this PR is closed and merged.

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

Successfully merging this pull request may close these issues.

6 participants