-
Notifications
You must be signed in to change notification settings - Fork 3
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
feat: Omit specific headers by default #92
Conversation
Function for omitting a list of property names from the input object.
A higher order function for creating a serializer that omits properties from the object assigned to the top-level property.
ed2af31
to
5394b1d
Compare
5394b1d
to
cea3d28
Compare
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.
Is the release summary format ok?
I'm not sure what it will look like in the release notes.
These headers amount to logging noise today, and so the new version will not require code changes or produce unexpected behaviour across our applications.
`toMatchObject` accepts an object subset, which makes it hard to reliably test property removal. For example, #92 adds a happy-path test case "should omit defaultOmitHeaderNames by default" which unexpectedly passes even if the omission logic is removed. These tests aren't great overall and I'd prefer them to use a more typical Jest structure so we can use `.only`s and the like, but this should be enough to get #92 through with confidence around regression testing.
// We can get away with a shallow clone as we only touch top-level properties. | ||
const output: Record<PropertyKey, unknown> = { | ||
...input, | ||
}; | ||
|
||
for (const property of properties) { | ||
// Remove the property from our shallow clone. | ||
delete output[property]; | ||
} |
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.
My assumption is that this is overall cheaper than re-spreading the object on every iteration
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.
Delete seems the fastest; thanks for showing me this.
Tested in TS Playground
const propertyNames = [...new Set(options.omitPropertyNames ?? [])].filter( | ||
(propertyName) => typeof propertyName === 'string', | ||
); | ||
|
||
if ( | ||
!topLevelPropertyName || | ||
typeof topLevelPropertyName !== 'string' || | ||
topLevelPropertyName.length === 0 || |
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.
My thinking is that we don't need to be quite this defensive, because the only variable input to createOmitPropertiesSerializer
is omitHeaderNames
, and that should be statically defined and type checked in the consumer repo.
By contrast it makes sense to be defensive when serializing an arbitrary logging object as we don't have any type guarantees around that.
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 don't mind this change since as you say they're meant to be statically defined and type-checked.
My thoughts were that it's done on initialisation and cleansing external inputs is just automatic (since you can cast as unknown as ExpectedType
) so wasn't expensive anyway.
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.
Thanks a bunch for your patience on this one, it looks great 🙌
I've made a few hopefully uncontroversial tweaks to the implementation. Let me know if you'd like me to run through any of these or if you feel any are regressions.
Otherwise I'll aim to merge this in and cut a release tomorrow.
* Matching is currently case sensitive. | ||
* You will typically express the header names in lowercase, | ||
* as server frameworks normalise incoming headers. |
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 noting this limitation here; we can also surface in the changeset or README if you think it necessary.
I assume we are comfortable with this for now; we can weigh up the cost of lowercasing the property names if it turns out to be an issue.
@72636c : I'm happy with the changes and learnt a few things too. Thanks for your review and updates! |
`toMatchObject` accepts an object subset, which makes it hard to reliably test property removal. For example, #92 adds a happy-path test case "should omit defaultOmitHeaderNames by default" which unexpectedly passes even if the omission logic is removed. These tests aren't great overall and I'd prefer them to use a more typical Jest structure so we can use `.only`s and the like, but this should be enough to get #92 through with confidence around regression testing. --------- Co-authored-by: Conrad Lang <[email protected]>
Purpose
Alternative to #84
Pino has a limitation where you can either redact or remove specified redact.paths by setting redact.remove to true or false.
In some cases, it's reasonable to want to redact some properties e.g. headers.authorization so you know that you have an authorization header but in other cases, the header, e.g. x-envoy-peer-metadata only contributes to increasing the log message size and therefore cost.
To reduce costs, we should be able to remove the properties that are not relevant for diagnostic purposes.
Approach
Properties containing headers that should be omitted were identified via this Splunk query over 1 business day:
req
,headers
, andrequestHeader
properties.Added
defaultOmitHeaderNames
which is a list of header names to omit.Add a
omitHeaderNames
logger option that defaults to thedefaultOmitHeaderNames
. If a consumer wants to opt out, they can supply an empty list or their own list instead.A
headers
custom serializer omits the specified headers from theheaders
property of the logged object.The existing
req
custom serializer also omits the specified headers from thereq.headers
property of the logged object.Notes
💡 It may be helpful to review commit-by-commit.
Answers from discussion with @72636c
Minimal Risk
based on this being "...business critical given it ladders up to a citizenship ask (cost savings), has limited risk (only removes headers from logs that you’ve de-risked, pending point 3), and it’s still up to individual team discretion to review & choose to merge the version bump".reqHeaders
custom serializer so some guidance around this is needed. We could add this as it would be a clone of theheaders
custom serializer.Since
reqHeaders
is produced by two services of one team, we will not be adding a specific custom serializer.Cloud platform team is ok with this change and has had a look at this PR.
serializers
since it isn't used internally and I don't think it's accessible via e.g.import serializers from '@seek/logger/lib-commonjs/serializers'
.Removed default export from serializers.
Issues
Relates to #83 but does not resolve it.