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

Transport refactor #107

Merged
merged 22 commits into from
Mar 16, 2018
Merged

Transport refactor #107

merged 22 commits into from
Mar 16, 2018

Conversation

mattberther
Copy link
Member

No description provided.

@gilly3
Copy link
Contributor

gilly3 commented Jan 22, 2018

Is "Legacy" meant to imply that there is some better alternative for daily rotating log files?

@mattberther
Copy link
Member Author

@gilly3 "Legacy" will be the non winston 3.0 stuff. :)

@gilly3
Copy link
Contributor

gilly3 commented Jan 23, 2018

Will (does) winston 3.0 have daily rotate built in? Or is there a winston 3.0 version of this transport? (I don't see any 3.0 branch here)

@mattberther
Copy link
Member Author

No, winston 3.0 will not have this built in. I'm currently working on a 3.0 version (which is what this PR started).

@crussell52
Copy link

TLDR; I got it working. Now we just need to figure out how what the right way to fix it is.

@ericdrobinson Thanks for the tips

Your stack trace was limited to 10 stack frames and doesn't quite show the whole picture (though it might be most of it). Could you please remove the stack frame restriction with this:

So... after removing the stack trace limit and scattering some more traces around, it does look like the behaviors I described are taking place

Trace: DailyRotateFile open event proxy
    at EventEmitter.<anonymous> (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/winston-daily-rotate-file/daily-rotate-file.js:93:21)
    at emitOne (events.js:116:13)
    at EventEmitter.emit (events.js:211:7)
    at WriteStream.<anonymous> (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/file-stream-rotator/FileStreamRotator.js:506:15)
    at emitOne (events.js:116:13)
    at WriteStream.emit (events.js:211:7)
    at WriteStream.<anonymous> (fs.js:2156:10)
    at FSReqWrap.oncomplete (fs.js:135:15)


Trace: DailyRotateFile rotate event proxy
    at EventEmitter.<anonymous> (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/winston-daily-rotate-file/daily-rotate-file.js:98:21)
    at emitTwo (events.js:126:13)
    at EventEmitter.emit (events.js:214:7)
    at Object.<anonymous> (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/file-stream-rotator/FileStreamRotator.js:441:24)
    at DailyRotateFile.log (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/winston-daily-rotate-file/daily-rotate-file.js:141:24)
    at DailyRotateFile.TransportStream._write (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/winston-transport/index.js:90:17)
    at doWrite (_stream_writable.js:387:12)
    at writeOrBuffer (_stream_writable.js:373:5)
    at DailyRotateFile.Writable.write (_stream_writable.js:290:11)
    at DerivedLogger.ondata (_stream_readable.js:639:20)
    at emitOne (events.js:116:13)
    at DerivedLogger.emit (events.js:211:7)
    at addChunk (_stream_readable.js:263:12)
    at readableAddChunk (_stream_readable.js:250:11)
    at DerivedLogger.Readable.push (_stream_readable.js:208:10)
    at DerivedLogger.Transform.push (_stream_transform.js:147:32)
    at DerivedLogger._transform (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/winston/lib/winston/logger.js:218:8)
    at DerivedLogger.Transform._read (_stream_transform.js:186:10)
    at DerivedLogger.Transform._write (_stream_transform.js:174:12)
    at doWrite (_stream_writable.js:387:12)
    at writeOrBuffer (_stream_writable.js:373:5)
    at DerivedLogger.Writable.write (_stream_writable.js:290:11)
    at DerivedLogger.(anonymous function) [as info] (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/winston/lib/winston/create-logger.js:45:14)
    at Timeout._onTimeout (/Users/crussell/development/projects/plc-bridge/node-logger/test.js:16:16)
    at ontimeout (timers.js:475:11)
    at tryOnTimeout (timers.js:310:5)
    at Timer.listOnTimeout (timers.js:270:5)


Trace: DailyRotateFile close event proxy
    at EventEmitter.<anonymous> (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/winston-daily-rotate-file/daily-rotate-file.js:77:21)
    at emitNone (events.js:106:13)
    at EventEmitter.emit (events.js:208:7)
    at WriteStream.<anonymous> (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/file-stream-rotator/FileStreamRotator.js:497:15)
    at emitNone (events.js:111:20)
    at WriteStream.emit (events.js:208:7)
    at fs.close (fs.js:2081:12)
    at FSReqWrap.oncomplete (fs.js:135:15)


Trace: Transport unpipe event handler
    at DailyRotateFile.<anonymous> (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/winston-transport/index.js:50:13)
    at Object.onceWrapper (events.js:317:30)
    at emitTwo (events.js:131:20)
    at DailyRotateFile.emit (events.js:214:7)
    at DerivedLogger.Readable.unpipe (_stream_readable.js:735:12)
    at unpipe (_stream_readable.js:684:9)
    at DailyRotateFile.onclose (_stream_readable.js:672:5)
    at Object.onceWrapper (events.js:313:30)
    at emitNone (events.js:106:13)
    at DailyRotateFile.emit (events.js:208:7)
    at EventEmitter.<anonymous> (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/winston-daily-rotate-file/daily-rotate-file.js:78:18)
    at emitNone (events.js:106:13)
    at EventEmitter.emit (events.js:208:7)
    at WriteStream.<anonymous> (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/file-stream-rotator/FileStreamRotator.js:497:15)
    at emitNone (events.js:111:20)
    at WriteStream.emit (events.js:208:7)
    at fs.close (fs.js:2081:12)
    at FSReqWrap.oncomplete (fs.js:135:15)


Transport unpipe handler, closing self


Trace: DailyRotateFile ending this.logStream()
    at DailyRotateFile.close (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/winston-daily-rotate-file/daily-rotate-file.js:149:17)
    at DailyRotateFile.<anonymous> (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/winston-transport/index.js:61:14)
    at Object.onceWrapper (events.js:317:30)
    at emitTwo (events.js:131:20)
    at DailyRotateFile.emit (events.js:214:7)
    at DerivedLogger.Readable.unpipe (_stream_readable.js:735:12)
    at unpipe (_stream_readable.js:684:9)
    at DailyRotateFile.onclose (_stream_readable.js:672:5)
    at Object.onceWrapper (events.js:313:30)
    at emitNone (events.js:106:13)
    at DailyRotateFile.emit (events.js:208:7)
    at EventEmitter.<anonymous> (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/winston-daily-rotate-file/daily-rotate-file.js:78:18)
    at emitNone (events.js:106:13)
    at EventEmitter.emit (events.js:208:7)
    at WriteStream.<anonymous> (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/file-stream-rotator/FileStreamRotator.js:497:15)
    at emitNone (events.js:111:20)
    at WriteStream.emit (events.js:208:7)
    at fs.close (fs.js:2081:12)
    at FSReqWrap.oncomplete (fs.js:135:15)


Trace: DailyRotateFile open event proxy
    at EventEmitter.<anonymous> (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/winston-daily-rotate-file/daily-rotate-file.js:93:21)
    at emitOne (events.js:116:13)
    at EventEmitter.emit (events.js:211:7)
    at WriteStream.<anonymous> (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/file-stream-rotator/FileStreamRotator.js:506:15)
    at emitOne (events.js:121:20)
    at WriteStream.emit (events.js:211:7)
    at WriteStream.<anonymous> (fs.js:2156:10)
    at FSReqWrap.oncomplete (fs.js:135:15)


Trace: DailyRotateFile finish event proxy
    at EventEmitter.<anonymous> (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/winston-daily-rotate-file/daily-rotate-file.js:83:21)
    at emitNone (events.js:106:13)
    at EventEmitter.emit (events.js:208:7)
    at WriteStream.<anonymous> (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/file-stream-rotator/FileStreamRotator.js:500:15)
    at emitNone (events.js:111:20)
    at WriteStream.emit (events.js:208:7)
    at finishMaybe (_stream_writable.js:604:14)
    at afterWrite (_stream_writable.js:455:3)
    at onwrite (_stream_writable.js:445:7)
    at fs.js:2180:5
    at FSReqWrap.wrapper [as oncomplete] (fs.js:685:5)


Trace: DailyRotateFile close event proxy
    at EventEmitter.<anonymous> (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/winston-daily-rotate-file/daily-rotate-file.js:77:21)
    at emitNone (events.js:106:13)
    at EventEmitter.emit (events.js:208:7)
    at WriteStream.<anonymous> (/Users/crussell/development/projects/plc-bridge/node-logger/node_modules/file-stream-rotator/FileStreamRotator.js:497:15)
    at emitNone (events.js:106:13)
    at WriteStream.emit (events.js:208:7)
    at fs.close (fs.js:2081:12)
    at FSReqWrap.oncomplete (fs.js:135:15)

[winston] Attempt to write logs with no transports {"message":"hello","level":"info"}
(last message repeated 15 times, then silent)

Commenting out the behavior for the Transport to close itself eliminates the additional close and finish event which immediately follows the open event. The end result is no different. This only proves that that is the reason you previously witnessed the close-after-open behavior.

However

If I make it so that DailyRotateFile does not proxy the close event from file-stream-rotate it works as expected -- This was done by removing the event proxying at daily-rotate-file.js#L76

In terms of actually fixing it, I'm not sure we want to completely prevent the close event from bubbling. If the stream unexpectedly closes, I think we want that to be proxied. This may be a matter of disabling some of those event proxies when we know that a rotation is happening?

@mattberther @indexzero What are your thoughts?

@ericdrobinson
Copy link

ericdrobinson commented Mar 1, 2018

@crussell52 Smashup job tracking this down. It now makes a lot of sense and may explain why the main Winston File Transport breaks in a very similar way (internally it calls unpipe when doing its own file rotation).

If I make it so that DailyRotateFile does not proxy the close event from file-stream-rotate it works as expected -- This was done by removing the event proxying at daily-rotate-file.js#L76

In terms of actually fixing it, I'm not sure we want to completely prevent the close event from bubbling. If the stream unexpectedly closes, I think we want that to be proxied. This may be a matter of disabling some of those event proxies when we know that a rotation is happening?

I'm not sure that I agree. Let's take a look at the architecture of what we have:

Winston Logger
  ┗ Winston Daily Rotate File [a Winston Transport, which is a a stream.Writable]
      ┗ "Stream" Event Proxy [an EventEmitter NAMED 'stream' (from file-stream-rotator)]

[The symbol above means "has a reference to" or "encapsulates".]

The point is that from Winston's point of view, if a transport emits a standard "I'm closing" event, then it should close: it's transport has completed its work. It is the rotators that wrap the actual file streams and present themselves as streams that are doing things incorrectly. If Winston Daily Rotate File is a stream, then it should only emit a close event when it's actually done. It is not done when it rotates files. That it forwards the standard close event when it's simply "adjusting its internals", is incorrect.

You could go a step further and say that the culprit is actually file-stream-rotator. It does everything in its power to represent itself as a stream, even though it is actually a simple EventEmitter interface to a stream managed in module scope. The FileStreamRotator.getStream() function may make you think you're getting a stream, but you are not. While that function is responsible for setup of the actual streams, what it returns is actually the EventEmitter.

The point here is that a Winston Transport "stream" should only emit/bubble the close event when it is well and truly done (during/after its own teardown process). Any transport that handles streams internally should not bubble those events without first renaming them (e.g. internalStreamClosed or internalStreamOpened). In this manner, the Winston Transport "stream" can control/report its own lifecycle to those that wish to treat it as a Node Stream.

To that end, I see two possible directions for addressing this:

  1. In winston-daily-rotate-file, stop listening to events proxied from underlying streams. Rather, listen to (and proxy) the rotate and new events from file-stream-rotator.
    1.1 Another option here would be to rename the Stream events when bubbling.
  2. In file-stream-rotator, rename bubbled events to be more descriptive. Something like rotateStreamClose, rotateStreamOpen, rotateStreamFinish, rotateStreamError...

Looking over the source code of file-stream-rotator, I am beginning to think that solutions 1 or 1.1 are probably the way to go. Currently, the file-stream-rotator does not appear to have a concept of "end". You can call getStream (which begins working with streams) but nowhere is there a method or function that tells it to explicitly stop file rotation and cleanup - this is probably handled implicitly during garbage collection. As this package appears to focus exclusively on "opening a file and then rotating it at some point in the future", the proper place to handle this is likely in winston-daily-rotate-file.

@indexzero A few questions:

  1. Do you have any thoughts/advice/input on the above?
  2. With respect to issues 1144 and 1194 in the main winston repo, is it possible that something similar is happening with the File transport? It seems that file rotation triggers a call to unpipe. Is it possible that unpipe is bubbling its way into the main TransportStream class and closing the stream?
  3. You mentioned that you've been facing lots of "non-deterministic" issues with the 3.0.0 release candidates. How strictly have you been asking people to report the specific version of NodeJS that they're testing with? I ask because as I recall, one of the main changes that occurred in the 2.x→3.x transition was that transports are now Streams. If investigating this issue has taught me one thing it's that NodeJS is constantly tinkering with how streams work. The NodeJS issue called out by @crussell52 is one such example of how stream event flow may change between different versions of Node. Therefore, it may be extremely important to identify the specific version of Node that users are experiencing issues with as some bugs may have been "fixed" (or had problematic codepaths be otherwise moved/removed) by the core NodeJS team at some point.

Thoughts?

@crussell52
Copy link

@ericdrobinson I'd call it a very collaborative effort in tracking it down 👍

If Winston Daily Rotate File is a stream, then it should only emit a close event when it's actually done. It is not done when it rotates files. That it forwards the standard close event when it's simply "adjusting its internals", is incorrect.

I agree entirely (after this exercise) that the close event should never be bubbled during "normal operations". I also agree that both file-stream-rotator and winston-daily-rotate-file are behaving inappropriately in this regard. It makes the abstraction layers leaky at best and liars at worst.

Expanding on that, if such a transport (let's call it a stream-proxy) receives an unexpected close event from its inner stream(s), it should handle it -- that's its responsibility. However, if there isn't any reasonable path to recovery, I think the stream-proxy's only recourse is to close itself and let anybody upstream to deal with that however they can... Maybe also emit an error event but if it is in an unusable state, I think closing itself makes sense.

rename bubbled events to be more descriptive. Something like rotateStreamClose, rotateStreamOpen, rotateStreamFinish, rotateStreamError...

I think it is better to just swallow/handle the events. Even if they are renamed, it ends up leaking implementation details of the stream-proxy, again. The outside world should not know or care that there is an internal stream in play.

@ericdrobinson
Copy link

I think it is better to just swallow/handle the events. Even if they are renamed, it ends up leaking implementation details of the stream-proxy, again. The outside world should not know or care that there is an internal stream in play.

I'm convinced. Perhaps an Issue (or PR?) should be created in the file-stream-rotator project about this (I have no idea how much such a change would impact their users - it was added ~1 year ago). At the very least, winston-daily-rotate-file (this PR) should probably be updated to not listen to file-stream-rotator's events (with the exception of rotate).

@mattberther @indexzero does this approach make sense?

@crussell52
Copy link

crussell52 commented Mar 1, 2018

@ericdrobinson, @mattberther, @indexzero

Unfortunately, file-stream-rotator does not provide enough information to make a distinction between a "close" and a "close because of rotation" -- the file-stream-rotator.new event is emitted after the old stream is destroyed. The best that can be done (without upstream changes) is unconditionally ignoring file-stream-rotator.close and file-stream-rotator.finish events. That comes at the cost of never knowing if it file-stream-rotator actually closes.

EDIT: clarified ambiguous pronoun

@mattberther
Copy link
Member Author

mattberther commented Mar 1, 2018

@crussell52 What would your proposed upstream changes be? I'm happy to make a fork of the file-stream-rotator project to test this out (and ultimately submit the PR back to that project for incorporation).

Also, I presume similar changes would need to be made in the File transport. /cc @indexzero

@ericdrobinson
Copy link

@crussell52

Unfortunately, file-stream-rotator does not provide enough information to make a distinction between a "close" and a "close because of rotation" -- the file-stream-rotator.new event is emitted after the old stream is destroyed.

I don't think that's a problem. I can see users wanting the rotate event, regardless of what happens to the close event.

With respect to the close event, I don't see much value in it at the moment (other than perhaps for debug purposes? Perhaps make those callbacks purely debug-related?).

The best that can be done (without upstream changes) is unconditionally ignoring file-stream-rotator.close and file-stream-rotator.finish events.

I would argue that winston-daily-rotate-file should ignore all internal-stream-sourced events from file-stream-rotator (i.e. all events but rotate and maybe new [which for 99% of cases is made redundant by rotate, at least as used in winston-daily-rotate-file]).

The other option would be to rename-and-bubble, but I can't see what the value of that would be.

That comes at the cost of never knowing if it file-stream-rotator actually closes.

If you rename-and-bubble the event, then you could still see it. The other option would be to add a new, file-stream-rotator event that gets triggered on the call to file-stream-rotator's stream.end() function. Something like rotationEnded.

@mattberther

What would your proposed upstream changes be?

Regardless of any upstream changes (see above for my suggestions, though note that I consider them unnecessary), winston-daily-rotate-file should stop emitting stream-like events. You could capture the ones from file-stream-rotator but what benefit do they provide? Rather, bubbling the rotate event should be enough (for now).

You could incorporate the rotationEnded event I mentioned earlier in this comment (upstream). If you did that, then you could listen for it and then emit a close event. I'm not sure when this would actually be useful, though, because it seems that generally it's the user telling winston telling winston-daily-rotate-file telling file-stream-rotator to close, not something coming from a "completion" event bubbling up and telling some "owner" to close...

Basically, the bug we've been discussing here can be eliminated by changing winston-daily-rotate-file (in this PR) to not bubble Stream events (in contrast to file-stream-rotator events like rotate and, possibly, new).

That make sense?

@mattberther
Copy link
Member Author

@ericdrobinson @crussell52 I've pushed some new changes that should address this issue and pushed winston-daily-rotate-file@next to npm. Can you please review and let me know if it corrects the issue in your scenarios?

@crussell52
Copy link

@mattberther Looks good to me. Checked the code and I ran my test script and I see logs rotating correctly with no message loss.

Also took a look at the commit... LGTM

@mattberther
Copy link
Member Author

Cheers, @crussell52. Thanks for doing all the heavy lifting. :)

@crussell52
Copy link

I learned a lot in the process! Thanks @ericdrobinson

Copy link

@ericdrobinson ericdrobinson left a comment

Choose a reason for hiding this comment

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

Overall looks great! I don't pretend to understand the reason behind changing close() to end() and providing the function parameters, but it looks familiar enough (perhaps taken from the TransportStream super-class version?). Either way, these changes do address the transport-close-on-rotate issues (for my environment) discussed in the main PR comments.

The one request I have is that the README.md be updated to no longer claim:

You can listen to the open, close, error, and finish events generated by the underlying stream.

So... documentation update and then all set for me!

@ericdrobinson
Copy link

ericdrobinson commented Mar 2, 2018

My pleasure, @crussell52! Glad I was able to help out, but also thank you for actually identifying the core issue! I'd found a bunch of hints but not quite enough to put it all together. Great work!

Also, @mattberther, as I mentioned in the Review, I just tested 2.0.0rc4 and the daily rotation now works for me! Hurray!!!

@@ -137,9 +121,14 @@ if (semver.major(winston.version) === 2) {
};
}

DailyRotateFile.prototype.close = function () {
DailyRotateFile.prototype.end = function (chunk, encoding, callback) {
Copy link

@crussell52 crussell52 Mar 2, 2018

Choose a reason for hiding this comment

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

I'm continuing to read about Streams -- I'm actually not sure end() should be overridden. The base implementation does a lot more work which is not being handled here, including managing the corked state of the stream

This trivial example for decoding within a stream also wraps another stream and they do their cleanup as part of _destroy() _final(). I'm still not clear on the full lifecycle of Streams, so this is all still a little mysterious to me, but my gut says that overriding end() might have odd side effects later.

Copy link
Member Author

@mattberther mattberther Mar 2, 2018

Choose a reason for hiding this comment

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

Good point. Pushing a version that does the cleanup in _final(). Please try npm install winston-daily-rotate-file@next and let me know of any issues.

Actually, _final() was introduced with 8.0, so I need to look for another place to implement the cleanup code. Please hold off on trying the new version until we have a passing build.

@mattberther
Copy link
Member Author

Fundamentally, that close() method was there to support the unit tests. I believe the desired outcome can be accomplished by instead keeping it and performing the wrapped stream cleanup in there.

You've made very valid points as to why end() shouldnt be overridden. If one of you have a better place to move this cleanup code that supports 4.x, 6.x, and 8.x, please let me know.

Copy link

@ericdrobinson ericdrobinson left a comment

Choose a reason for hiding this comment

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

Suggest changing the name of close to something a little less stream-like... (maybe with Transport in the name...?).

callback = callback || noop;
callback();
});
DailyRotateFile.prototype.close = function () {

Choose a reason for hiding this comment

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

I'm a little concerned about this function name. Streams already expose a close event and this feels ripe for confusion. For my part (not a stream expert by any stretch), I had to go and look at the core NodeJS stream and winston-transport source code to verify that this wasn't overwriting a pre-existing close() function.

If this is specifically for winston-daily-rotate-rotate (WDRF) would it be possible to sidestep any such confusion and call it something WDRF-specific? Something like endRotation() or endLogging() or closeTransport()?

@indexzero This raises an interesting question. Is there a set of functions (hooks?) defined in winston-transport that cover setup and teardown? Or at least a convention by which Transports can name basic functionality like this?

Copy link

@crussell52 crussell52 Mar 3, 2018

Choose a reason for hiding this comment

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

@ericdrobinson

For my part (not a stream expert by any stretch), I had to go and look at the core NodeJS stream and winston-transport source code to verify that this wasn't overwriting a pre-existing close() function.

FWIW, I did the same thing.

However, I think it does have meaning to winston. In the area I explored with the rotation bug -- In winston-transport -- the unpipe handler calls close on the Transport if it is defined: (https://github.com/winstonjs/winston-transport/blob/master/index.js#L49-L62)

Perhaps this just needs some clarity in the documentation -- maybe this is also the "most correct" place for transports to call this.end()" and do additional cleanup if necessary since _final()` is not an option in node versions < 8.0. I'll add more thoughts about that to the thread about _final() main PR conversation

@crussell52
Copy link

crussell52 commented Mar 3, 2018

so that's unfortunate about _final being 8.0+ ...

Maybe close() on the winston-transport API is the "most right place" for cleanup then. There appear to be some specifics to certain stream events which might need to be highlighted to Transport authors since they are creating/extending Streams whether they realize it or not.

From what I've seen, I'd say the important points are about finish and close -- the behavior appears to be consistent back to 4.x. Specifically:

  • finish occurs after Writable.end() and when all data has been written to the underlying resource. The docs do not list this as an optional event so every Transport author should be providing this event -- (probably via their close() method?).
  • Not every Writable emits close event -- the winston team should probably decide on a "preferred behavior" for Transports and encourage authors to document deviation from that behavior.
  • close is the last event, so authors should be made aware that no additional stream events should occur after close -- most notably, the finish event must precede the close event.

Technically, the Stream documentation covers all of this, but winston has made it so simple to implement a Transport (which is a good thing) that the fact that it is even a Stream is well hidden so some guidance via documentaiton is probably warranted.... that is, unless there is a way the winston-transport can manage the timing of these events. I'm just not sure how that would work since only the implementation knows when everything has been "written to the underlying resource".

@crussell52
Copy link

that is, unless there is a way the winston-transport can manage the timing of these events. I'm just not sure how that would work since only the implementation knows when everything has been "written to the underlying resource".

Hmmmmm... Okay, I realize this is now out of scope for this plugin but I want to add a followup thought since it builds on the current conversation and the audience is right since this is a winston-maintained transport. 😬

What if.... winston-transport took a page out of the Streams playbook? It could implement the close() method and strongly encourage Transport authors to implement a _close(callback) method.

I haven't worked out all the details, of course... but basically winston-transport.close() would look something like this:

if (this._close) {
  this._close(function() {
    this.end();
    this.emit('finish'); // Does the built-in `end()` emit this for us?
  });
} else {
  this.end();
  this.emit('finish');
}

The "rule" for _close(cb) would be that authors call cb() once all messages "have been written to the underlying resource". Authors don't need to worry about emitting finish manually, and winston gains a clean way to shutdown transports (winstonjs/winston#228). In cases where finish gets emitted prematurely, it is up to the Transport authors to fix individually by implementing a proper _close(cb) method.

@ericdrobinson
Copy link

@crussell52 You might want to open a new Issue on the main Winston repo for this. Linking to that comment (or this PR in general) for added context would probably be helpful as well. :)

@mattberther mattberther merged commit 0f96e0a into master Mar 16, 2018
@mattberther mattberther deleted the transport-refactor branch March 16, 2018 17:07
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.

9 participants