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 PR - Create transport #1

Merged
merged 7 commits into from
Jun 23, 2021
Merged

Initial PR - Create transport #1

merged 7 commits into from
Jun 23, 2021

Conversation

RLRabinowitz
Copy link
Contributor

@RLRabinowitz RLRabinowitz commented Jun 21, 2021

This PR creates the transport. It's very similar to the implementation of https://github.com/lazywithclass/winston-cloudwatch, with some adjustments for DynamoDB:

Index.js:

  • Throw exception when region doesn't support DynamoDB (partially taken from https://github.com/jwchang0206/winston-dynamodb)
  • Remove logGroupName and retentionInDays, which is CW specific, and add tableName instead
  • Change AWS config format to match that of DynamoDB client
  • Rename every CW to DynamoDB

util.js:

  • Rename WINSTON_CLOUDWATCH_DEBUG to WINSTON_DYNAMODB_DEBUG

dynamodb-integration.js:

  • Remove all unnecessary logic from cloudwatch-integration.js - Managing tokens and related errors, not being able to send multiple batch requests simultaniously...
  • Keep size limit logic + message truncation
  • Do not allow more than 25 items in a batch
  • Creates a batch request. The id is the streamName, timestamp is the timestamp of log addition, message is the log message

@RLRabinowitz RLRabinowitz self-assigned this Jun 21, 2021
@RLRabinowitz RLRabinowitz force-pushed the chore-initial-setup branch from 35bb93f to 9736f0d Compare June 22, 2021 13:45
@RLRabinowitz RLRabinowitz changed the title Temp PR Initial PR - Create transport Jun 22, 2021
@RLRabinowitz RLRabinowitz requested a review from a team June 22, 2021 16:35
Copy link

@avnerenv0 avnerenv0 left a comment

Choose a reason for hiding this comment

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

@RLRabinowitz This looks good enough to use, but I reviewed it like 'new code', feel free to apply comments however you like

index.js Outdated
Comment on lines 3 to 12
var util = require('util'),
winston = require('winston'),
AWS = require('aws-sdk'),
dynamodbIntegration = require('./lib/dynamodb-integration'),
isEmpty = require('lodash.isempty'),
assign = require('lodash.assign'),
isError = require('lodash.iserror'),
stringify = require('./lib/utils').stringify,
debug = require('./lib/utils').debug,
defaultFlushTimeoutMs = 10000;

Choose a reason for hiding this comment

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

put a const in each 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.

Comment on lines +81 to +84
if (!/^uncaughtException: /.test(info.message)) {
// do not wait, just return right away
return callback(null, true);
}

Choose a reason for hiding this comment

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

this one is confusing - maybe reverse the condition

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since I'm not too sure on this code here (Specifically what the callback(null, true) means. It seems that the callback is called immediately with these params on logger.info, but is called again with other params when the batch is sent), I'll probably skip this...

Choose a reason for hiding this comment

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

callbacks usually follow the pattern of func(error, result) and in this case cb(null, true) means everything worked. This is the 'happy flow' and will happen for EVERY log call except when the message includes uncaughtException.

It's confusing because the 'normal' case is inside the if and short circuits this function.

If the error message does include uncaughtException then we clear everything and submit the logs and get ready for a 'crash' exit of the process.

index.js Outdated
WinstonDynamoDB.prototype.add = function(log) {
debug('add log to queue', log);

var self = this;

Choose a reason for hiding this comment

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

maybe use arrow functions and get rid of this shameful piece of javascript history

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Interesting, read online and didn't know about the whole this scope thing that is different between functions and arrow functions.
TIL

Copy link
Contributor Author

Choose a reason for hiding this comment

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

);
};

WinstonDynamoDB.prototype.kthxbye = function(callback) {

Choose a reason for hiding this comment

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

kthxbye is unique to that transport right?
Doesn't winston have something more 'built-in' ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep it's unique to the transport

index.js Show resolved Hide resolved
@@ -0,0 +1,83 @@
var LIMITS = {

Choose a reason for hiding this comment

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

prefer const

Copy link
Contributor Author

Choose a reason for hiding this comment

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

jest.config.js Outdated
@@ -0,0 +1,9 @@
/*

Choose a reason for hiding this comment

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

why is this needed if there aren't any tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right. This isn't need yet. I can remove it and add it later when necessary

Copy link
Contributor Author

Choose a reason for hiding this comment

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

const payload = {
RequestItems: {
[tableName]: eventsToSend.map(event =>
({

Choose a reason for hiding this comment

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

The streamName will be the partition key and the timestamp sort key?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In this PR, yes. I'll change the timestamp to be a sequenceNumber in a next PR, because of this issue

Comment on lines +18 to +34
var entryIndex = 0;
var bytes = 0;
while (entryIndex < Math.min(logEvents.length, LIMITS.MAX_BATCH_ITEM_NUM)) {
var ev = logEvents[entryIndex];
// unit tests pass null elements
var evSize = ev ? Buffer.byteLength(ev.message, 'utf8') + BASE_EVENT_SIZE_BYTES : 0;
if(evSize > LIMITS.MAX_EVENT_MSG_SIZE_BYTES) {
evSize = LIMITS.MAX_EVENT_MSG_SIZE_BYTES;
ev.message = ev.message.substring(0, evSize);
const msgTooBigErr = new Error('Message Truncated because it exceeds the DynamoDB size limit');
msgTooBigErr.logEvent = ev;
cb(msgTooBigErr);
}
if (bytes + evSize > LIMITS.MAX_BATCH_SIZE_BYTES) break;
bytes += evSize;
entryIndex++;
}

Choose a reason for hiding this comment

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

I would put all this in a different function

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ev.message = ev.message.substring(0, evSize);
const msgTooBigErr = new Error('Message Truncated because it exceeds the DynamoDB size limit');
msgTooBigErr.logEvent = ev;
cb(msgTooBigErr);

Choose a reason for hiding this comment

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

this calls the callback but continues the run ? doesn't seem right, maybe missing a return ?
Also maybe instead we can split the large message to several smaller messages ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thought about it, but since this is the existent logic in CW transport, I've decided not to touch this.
Also, the size limit for the truncation is bigger on DynamoDB (400KB) than in CW (256KB), so I wouldn't worry about it for our use case

@avnerenv0
Copy link

I would also add a Dynamo DB Table definition file

@RLRabinowitz
Copy link
Contributor Author

RLRabinowitz commented Jun 23, 2021

I would also add a Dynamo DB Table definition file

Add this to the missing README issue

@RLRabinowitz RLRabinowitz merged commit 7690388 into main Jun 23, 2021
@RLRabinowitz
Copy link
Contributor Author

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.

2 participants