Repository: pinojs/pino
Pull request #327 (2017-11-02 15:14:56)

Add crlf support
This is an attempt to add support for CRLF line endings.

Fixes: https://github.com/pinojs/pino/issues/326


Patch:
diff --git a/docs/API.md b/docs/API.md
index 22eb5b2..97cf011 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -77,6 +77,7 @@
     See [Extreme mode explained](extreme.md) for more detail.
   * `enabled` (boolean): enables logging. Default: `true`
   * `browser` (Object): browser only, may have `asObject` and `write` keys, see [Pino in the Browser](../readme.md#browser)
+  * `crlf` (boolean): logs newline delimited JSON with `\r\n` instead of `\n`. Default: `false`.
 + `stream` (Writable): a writable stream where the logs will be written.
   It can also receive some log-line [metadata](#metadata), if the
   relative protocol is enabled. Default: `process.stdout`
@@ -113,6 +114,7 @@ Returns a new [logger](#logger) instance.
   message. Default: `msg`.
   * `forceColor` (boolean): if set to `true`, will add color information to the formatted output
   message. Default: `false`.
+  * `crlf` (boolean): emit `\r\n` instead of `\n`. Default: `false`.
 
 ### Example:
 ```js
diff --git a/pino.js b/pino.js
index 5b09b5d..0243ad7 100644
--- a/pino.js
+++ b/pino.js
@@ -274,7 +274,7 @@ function pino (opts, stream) {
   iopts.stringify = iopts.safe ? stringifySafe : JSON.stringify
   iopts.formatOpts = {lowres: true}
   iopts.messageKeyString = `,"${iopts.messageKey}":`
-  iopts.end = ',"v":' + LOG_VERSION + '}\n'
+  iopts.end = ',"v":' + LOG_VERSION + '}' + (iopts.crlf ? '\r\n' : '\n')
   iopts.cache = !iopts.extreme ? null : {
     size: 4096,
     buf: ''
diff --git a/pretty.js b/pretty.js
index 832ddd5..049f519 100755
--- a/pretty.js
+++ b/pretty.js
@@ -23,22 +23,22 @@ var standardKeys = [
   'v'
 ]
 
-function withSpaces (value) {
-  var lines = value.split('\n')
+function withSpaces (value, eol) {
+  var lines = value.split('\r?\n')
   for (var i = 1; i < lines.length; i++) {
     lines[i] = '    ' + lines[i]
   }
-  return lines.join('\n')
+  return lines.join(eol)
 }
 
-function filter (value, messageKey) {
+function filter (value, messageKey, eol) {
   var keys = Object.keys(value)
   var filteredKeys = standardKeys.concat([messageKey])
   var result = ''
 
   for (var i = 0; i < keys.length; i++) {
     if (filteredKeys.indexOf(keys[i]) < 0) {
-      result += '    ' + keys[i] + ': ' + withSpaces(JSON.stringify(value[keys[i]], null, 2)) + '\n'
+      result += '    ' + keys[i] + ': ' + withSpaces(JSON.stringify(value[keys[i]], null, 2), eol) + eol
     }
   }
 
@@ -58,6 +58,7 @@ function pretty (opts) {
   var levelFirst = opts && opts.levelFirst
   var messageKey = opts && opts.messageKey
   var forceColor = opts && opts.forceColor
+  var eol = opts && opts.crlf ? '\r\n' : '\n'
   messageKey = messageKey || 'msg'
 
   var stream = split(mapLine)
@@ -100,12 +101,12 @@ function pretty (opts) {
     }
 
     if (formatter) {
-      return opts.formatter(parsed.value) + '\n'
+      return opts.formatter(parsed.value) + eol
     }
 
     if (timeTransOnly) {
       value.time = asISODate(value.time)
-      return JSON.stringify(value) + '\n'
+      return JSON.stringify(value) + eol
     }
 
     line = (levelFirst)
@@ -123,9 +124,9 @@ function pretty (opts) {
     }
     line += '\n'
     if (value.type === 'Error') {
-      line += '    ' + withSpaces(value.stack) + '\n'
+      line += '    ' + withSpaces(value.stack, eol) + eol
     } else {
-      line += filter(value, messageKey)
+      line += filter(value, messageKey, eol)
     }
     return line
   }


Test patch:
diff --git a/test/crlf.test.js b/test/crlf.test.js
new file mode 100644
index 0000000..e86a6cd
--- /dev/null
+++ b/test/crlf.test.js
@@ -0,0 +1,57 @@
+'use strict'
+
+var test = require('tap').test
+var pino = require('../')
+var pretty = require('../pretty')
+var writer = require('flush-write-stream')
+
+function capture () {
+  var ws = writer(function (chunk, enc, cb) {
+    this.data += chunk.toString()
+    cb()
+  })
+  ws.data = ''
+  return ws
+}
+
+test('pino uses LF by default', function (t) {
+  t.plan(1)
+  var stream = capture()
+  var logger = pino(stream)
+  logger.info('foo')
+  logger.error('bar')
+  t.ok(/foo[^\r\n]+\n[^\r\n]+bar[^\r\n]+\n/.test(stream.data))
+  t.end()
+})
+
+test('pino can log CRLF', function (t) {
+  t.plan(1)
+  var stream = capture()
+  var logger = pino({
+    crlf: true
+  }, stream)
+  logger.info('foo')
+  logger.error('bar')
+  t.ok(/foo[^\n]+\r\n[^\n]+bar[^\n]+\r\n/.test(stream.data))
+  t.end()
+})
+
+test('pretty can log CRLF', function (t) {
+  t.plan(1)
+  var prettier = pretty({
+    crlf: true,
+    formatter: function (data) {
+      return data.msg
+    }
+  })
+
+  var stream = capture()
+  prettier.pipe(stream)
+
+  var logger = pino(prettier)
+  logger.info('foo')
+  logger.info('bar')
+
+  t.is(stream.data, 'foo\r\nbar\r\n')
+  t.end()
+})