@contrast/agent 4.16.1 → 4.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. package/bin/VERSION +1 -1
  2. package/bin/linux/contrast-service +0 -0
  3. package/bin/mac/contrast-service +0 -0
  4. package/bin/windows/contrast-service.exe +0 -0
  5. package/esm.mjs +21 -11
  6. package/lib/assess/policy/propagators.json +4 -0
  7. package/lib/assess/policy/signatures.json +5 -0
  8. package/lib/assess/propagators/fastify-static/allowed-path.js +85 -0
  9. package/lib/cli-rewriter/index.js +1 -1
  10. package/lib/contrast.js +1 -1
  11. package/lib/core/rewrite/index.js +2 -2
  12. package/lib/protect/rules/cmd-injection/cmdinjection-rule.js +1 -1
  13. package/lib/protect/service.js +2 -0
  14. package/lib/util/trace-util.js +5 -4
  15. package/node_modules/@colors/colors/LICENSE +26 -0
  16. package/node_modules/@colors/colors/README.md +219 -0
  17. package/node_modules/@colors/colors/examples/normal-usage.js +83 -0
  18. package/node_modules/@colors/colors/examples/safe-string.js +80 -0
  19. package/node_modules/@colors/colors/index.d.ts +136 -0
  20. package/node_modules/@colors/colors/lib/colors.js +211 -0
  21. package/node_modules/@colors/colors/lib/custom/trap.js +46 -0
  22. package/node_modules/@colors/colors/lib/custom/zalgo.js +110 -0
  23. package/node_modules/@colors/colors/lib/extendStringPrototype.js +110 -0
  24. package/node_modules/@colors/colors/lib/index.js +13 -0
  25. package/node_modules/@colors/colors/lib/maps/america.js +10 -0
  26. package/node_modules/@colors/colors/lib/maps/rainbow.js +12 -0
  27. package/node_modules/@colors/colors/lib/maps/random.js +11 -0
  28. package/node_modules/@colors/colors/lib/maps/zebra.js +5 -0
  29. package/node_modules/@colors/colors/lib/styles.js +95 -0
  30. package/node_modules/@colors/colors/lib/system/has-flag.js +35 -0
  31. package/node_modules/@colors/colors/lib/system/supports-colors.js +151 -0
  32. package/node_modules/@colors/colors/package.json +49 -0
  33. package/node_modules/@colors/colors/safe.d.ts +48 -0
  34. package/node_modules/@colors/colors/safe.js +10 -0
  35. package/node_modules/@colors/colors/themes/generic-logging.js +12 -0
  36. package/node_modules/@dabh/diagnostics/README.md +16 -16
  37. package/node_modules/@dabh/diagnostics/package.json +9 -9
  38. package/node_modules/async/CHANGELOG.md +13 -0
  39. package/node_modules/async/all.js +74 -9
  40. package/node_modules/async/allLimit.js +3 -3
  41. package/node_modules/async/allSeries.js +3 -3
  42. package/node_modules/async/any.js +75 -9
  43. package/node_modules/async/anyLimit.js +3 -3
  44. package/node_modules/async/anySeries.js +3 -3
  45. package/node_modules/async/applyEach.js +2 -2
  46. package/node_modules/async/applyEachSeries.js +2 -2
  47. package/node_modules/async/asyncify.js +3 -3
  48. package/node_modules/async/auto.js +81 -15
  49. package/node_modules/async/autoInject.js +30 -4
  50. package/node_modules/async/cargo.js +1 -1
  51. package/node_modules/async/cargoQueue.js +1 -1
  52. package/node_modules/async/compose.js +1 -1
  53. package/node_modules/async/concat.js +72 -4
  54. package/node_modules/async/concatLimit.js +3 -3
  55. package/node_modules/async/concatSeries.js +2 -2
  56. package/node_modules/async/detect.js +43 -8
  57. package/node_modules/async/detectLimit.js +3 -3
  58. package/node_modules/async/detectSeries.js +3 -3
  59. package/node_modules/async/dir.js +1 -1
  60. package/node_modules/async/dist/async.js +1379 -168
  61. package/node_modules/async/dist/async.min.js +1 -1
  62. package/node_modules/async/dist/async.mjs +1372 -161
  63. package/node_modules/async/doDuring.js +3 -3
  64. package/node_modules/async/doUntil.js +2 -2
  65. package/node_modules/async/doWhilst.js +3 -3
  66. package/node_modules/async/during.js +3 -3
  67. package/node_modules/async/each.js +69 -28
  68. package/node_modules/async/eachLimit.js +4 -4
  69. package/node_modules/async/eachOf.js +85 -16
  70. package/node_modules/async/eachOfLimit.js +3 -3
  71. package/node_modules/async/eachOfSeries.js +2 -2
  72. package/node_modules/async/eachSeries.js +2 -2
  73. package/node_modules/async/ensureAsync.js +2 -2
  74. package/node_modules/async/every.js +74 -9
  75. package/node_modules/async/everyLimit.js +3 -3
  76. package/node_modules/async/everySeries.js +3 -3
  77. package/node_modules/async/filter.js +49 -9
  78. package/node_modules/async/filterLimit.js +3 -3
  79. package/node_modules/async/filterSeries.js +3 -3
  80. package/node_modules/async/find.js +43 -8
  81. package/node_modules/async/findLimit.js +3 -3
  82. package/node_modules/async/findSeries.js +3 -3
  83. package/node_modules/async/flatMap.js +72 -4
  84. package/node_modules/async/flatMapLimit.js +3 -3
  85. package/node_modules/async/flatMapSeries.js +2 -2
  86. package/node_modules/async/foldl.js +87 -11
  87. package/node_modules/async/foldr.js +2 -2
  88. package/node_modules/async/forEach.js +69 -28
  89. package/node_modules/async/forEachLimit.js +4 -4
  90. package/node_modules/async/forEachOf.js +85 -16
  91. package/node_modules/async/forEachOfLimit.js +3 -3
  92. package/node_modules/async/forEachOfSeries.js +2 -2
  93. package/node_modules/async/forEachSeries.js +2 -2
  94. package/node_modules/async/forever.js +4 -4
  95. package/node_modules/async/groupBy.js +62 -8
  96. package/node_modules/async/groupByLimit.js +3 -3
  97. package/node_modules/async/groupBySeries.js +2 -2
  98. package/node_modules/async/inject.js +87 -11
  99. package/node_modules/async/internal/applyEach.js +2 -2
  100. package/node_modules/async/internal/asyncEachOfLimit.js +1 -1
  101. package/node_modules/async/internal/consoleFunc.js +5 -1
  102. package/node_modules/async/internal/createTester.js +2 -2
  103. package/node_modules/async/internal/eachOfLimit.js +6 -6
  104. package/node_modules/async/internal/filter.js +2 -2
  105. package/node_modules/async/internal/iterator.js +5 -2
  106. package/node_modules/async/internal/map.js +1 -1
  107. package/node_modules/async/internal/parallel.js +3 -3
  108. package/node_modules/async/internal/queue.js +4 -4
  109. package/node_modules/async/internal/reject.js +2 -2
  110. package/node_modules/async/internal/setImmediate.js +6 -2
  111. package/node_modules/async/internal/wrapAsync.js +1 -1
  112. package/node_modules/async/log.js +1 -1
  113. package/node_modules/async/map.js +86 -6
  114. package/node_modules/async/mapLimit.js +3 -3
  115. package/node_modules/async/mapSeries.js +3 -3
  116. package/node_modules/async/mapValues.js +102 -12
  117. package/node_modules/async/mapValuesLimit.js +4 -4
  118. package/node_modules/async/mapValuesSeries.js +1 -1
  119. package/node_modules/async/memoize.js +3 -3
  120. package/node_modules/async/nextTick.js +3 -3
  121. package/node_modules/async/package.json +4 -6
  122. package/node_modules/async/parallel.js +96 -7
  123. package/node_modules/async/parallelLimit.js +2 -2
  124. package/node_modules/async/priorityQueue.js +11 -4
  125. package/node_modules/async/queue.js +4 -4
  126. package/node_modules/async/race.js +3 -3
  127. package/node_modules/async/reduce.js +87 -11
  128. package/node_modules/async/reduceRight.js +2 -2
  129. package/node_modules/async/reflect.js +2 -2
  130. package/node_modules/async/reflectAll.js +1 -1
  131. package/node_modules/async/reject.js +44 -10
  132. package/node_modules/async/rejectLimit.js +3 -3
  133. package/node_modules/async/rejectSeries.js +3 -3
  134. package/node_modules/async/retry.js +2 -2
  135. package/node_modules/async/retryable.js +4 -4
  136. package/node_modules/async/select.js +49 -9
  137. package/node_modules/async/selectLimit.js +3 -3
  138. package/node_modules/async/selectSeries.js +3 -3
  139. package/node_modules/async/seq.js +4 -4
  140. package/node_modules/async/series.js +112 -12
  141. package/node_modules/async/setImmediate.js +1 -1
  142. package/node_modules/async/some.js +75 -9
  143. package/node_modules/async/someLimit.js +3 -3
  144. package/node_modules/async/someSeries.js +3 -3
  145. package/node_modules/async/sortBy.js +121 -19
  146. package/node_modules/async/timeout.js +2 -2
  147. package/node_modules/async/times.js +1 -1
  148. package/node_modules/async/timesLimit.js +3 -3
  149. package/node_modules/async/timesSeries.js +1 -1
  150. package/node_modules/async/transform.js +111 -19
  151. package/node_modules/async/tryEach.js +3 -3
  152. package/node_modules/async/until.js +3 -3
  153. package/node_modules/async/waterfall.js +4 -4
  154. package/node_modules/async/whilst.js +3 -3
  155. package/node_modules/async/wrapSync.js +3 -3
  156. package/node_modules/color/README.md +9 -0
  157. package/node_modules/color/index.js +4 -1
  158. package/node_modules/color/package.json +8 -8
  159. package/node_modules/color-string/README.md +6 -2
  160. package/node_modules/color-string/index.js +21 -13
  161. package/node_modules/color-string/package.json +4 -4
  162. package/node_modules/colorspace/package.json +5 -5
  163. package/node_modules/fecha/dist/fecha.min.js +1 -1
  164. package/node_modules/fecha/dist/fecha.min.js.map +1 -0
  165. package/node_modules/fecha/lib/fecha.d.ts +2 -2
  166. package/node_modules/fecha/lib/fecha.js +35 -18
  167. package/node_modules/fecha/lib/fecha.js.map +1 -0
  168. package/node_modules/fecha/lib/fecha.umd.js +35 -18
  169. package/node_modules/fecha/lib/fecha.umd.js.map +1 -0
  170. package/node_modules/fecha/package.json +9 -10
  171. package/node_modules/fecha/src/fecha.ts +524 -0
  172. package/node_modules/logform/.eslintrc +1 -1
  173. package/node_modules/logform/CHANGELOG.md +24 -0
  174. package/node_modules/logform/README.md +15 -17
  175. package/node_modules/logform/browser.js +3 -1
  176. package/node_modules/logform/cli.js +1 -1
  177. package/node_modules/logform/colorize.js +2 -2
  178. package/node_modules/logform/dist/browser.js +8 -2
  179. package/node_modules/logform/dist/cli.js +2 -2
  180. package/node_modules/logform/dist/colorize.js +5 -5
  181. package/node_modules/logform/dist/errors.js +1 -1
  182. package/node_modules/logform/dist/format.js +9 -5
  183. package/node_modules/logform/dist/index.js +53 -20
  184. package/node_modules/logform/dist/json.js +7 -6
  185. package/node_modules/logform/dist/logstash.js +1 -1
  186. package/node_modules/logform/dist/pad-levels.js +4 -4
  187. package/node_modules/logform/dist/printf.js +1 -1
  188. package/node_modules/logform/dist/simple.js +1 -1
  189. package/node_modules/logform/dist/splat.js +2 -2
  190. package/node_modules/logform/dist/uncolorize.js +1 -1
  191. package/node_modules/logform/errors.js +1 -1
  192. package/node_modules/logform/index.d.ts +36 -0
  193. package/node_modules/logform/index.js +19 -20
  194. package/node_modules/logform/json.js +7 -7
  195. package/node_modules/logform/logstash.js +1 -1
  196. package/node_modules/logform/package.json +10 -9
  197. package/node_modules/logform/simple.js +1 -1
  198. package/node_modules/logform/uncolorize.js +1 -1
  199. package/node_modules/safe-stable-stringify/CHANGELOG.md +75 -0
  200. package/node_modules/safe-stable-stringify/LICENSE +21 -0
  201. package/node_modules/safe-stable-stringify/esm/package.json +4 -0
  202. package/node_modules/safe-stable-stringify/esm/wrapper.js +6 -0
  203. package/node_modules/safe-stable-stringify/index.d.ts +18 -0
  204. package/node_modules/safe-stable-stringify/index.js +618 -0
  205. package/node_modules/safe-stable-stringify/package.json +69 -0
  206. package/node_modules/safe-stable-stringify/readme.md +170 -0
  207. package/node_modules/safe-stable-stringify/tsconfig.json +22 -0
  208. package/node_modules/winston/LICENSE +18 -18
  209. package/node_modules/winston/README.md +1231 -1230
  210. package/node_modules/winston/dist/winston/common.js +10 -10
  211. package/node_modules/winston/dist/winston/config/index.js +17 -17
  212. package/node_modules/winston/dist/winston/container.js +46 -46
  213. package/node_modules/winston/dist/winston/create-logger.js +28 -24
  214. package/node_modules/winston/dist/winston/exception-handler.js +49 -49
  215. package/node_modules/winston/dist/winston/exception-stream.js +27 -27
  216. package/node_modules/winston/dist/winston/logger.js +152 -138
  217. package/node_modules/winston/dist/winston/profiler.js +22 -22
  218. package/node_modules/winston/dist/winston/rejection-handler.js +54 -54
  219. package/node_modules/winston/dist/winston/tail-file.js +14 -14
  220. package/node_modules/winston/dist/winston/transports/console.js +31 -31
  221. package/node_modules/winston/dist/winston/transports/file.js +101 -101
  222. package/node_modules/winston/dist/winston/transports/http.js +119 -41
  223. package/node_modules/winston/dist/winston/transports/index.js +17 -17
  224. package/node_modules/winston/dist/winston/transports/stream.js +24 -24
  225. package/node_modules/winston/dist/winston.js +91 -97
  226. package/node_modules/winston/index.d.ts +213 -193
  227. package/node_modules/winston/lib/winston/common.js +61 -61
  228. package/node_modules/winston/lib/winston/config/index.d.ts +99 -98
  229. package/node_modules/winston/lib/winston/config/index.js +35 -35
  230. package/node_modules/winston/lib/winston/container.js +114 -114
  231. package/node_modules/winston/lib/winston/create-logger.js +104 -104
  232. package/node_modules/winston/lib/winston/exception-handler.js +245 -245
  233. package/node_modules/winston/lib/winston/exception-stream.js +54 -54
  234. package/node_modules/winston/lib/winston/logger.js +676 -667
  235. package/node_modules/winston/lib/winston/profiler.js +51 -51
  236. package/node_modules/winston/lib/winston/rejection-handler.js +251 -251
  237. package/node_modules/winston/lib/winston/tail-file.js +124 -124
  238. package/node_modules/winston/lib/winston/transports/console.js +117 -117
  239. package/node_modules/winston/lib/winston/transports/file.js +695 -695
  240. package/node_modules/winston/lib/winston/transports/http.js +267 -202
  241. package/node_modules/winston/lib/winston/transports/index.d.ts +103 -100
  242. package/node_modules/winston/lib/winston/transports/index.js +56 -56
  243. package/node_modules/winston/lib/winston/transports/stream.js +63 -63
  244. package/node_modules/winston/lib/winston.js +176 -182
  245. package/node_modules/winston/node_modules/winston-transport/.babelrc +3 -0
  246. package/node_modules/winston/node_modules/winston-transport/.eslintrc +7 -0
  247. package/node_modules/winston/node_modules/winston-transport/.gitattributes +1 -0
  248. package/node_modules/winston/node_modules/winston-transport/.nyc_output/c3d7ddb9-cc26-466b-a4f6-993ad69e86f6.json +1 -0
  249. package/node_modules/winston/node_modules/winston-transport/.nyc_output/processinfo/c3d7ddb9-cc26-466b-a4f6-993ad69e86f6.json +1 -0
  250. package/node_modules/winston/node_modules/winston-transport/.nyc_output/processinfo/index.json +1 -0
  251. package/node_modules/winston/node_modules/winston-transport/CHANGELOG.md +126 -0
  252. package/node_modules/winston/node_modules/winston-transport/LICENSE +22 -0
  253. package/node_modules/winston/node_modules/winston-transport/README.md +50 -0
  254. package/node_modules/winston/node_modules/winston-transport/dist/index.js +215 -0
  255. package/node_modules/winston/node_modules/winston-transport/dist/legacy.js +116 -0
  256. package/node_modules/winston/node_modules/winston-transport/index.d.ts +39 -0
  257. package/node_modules/winston/node_modules/winston-transport/index.js +215 -0
  258. package/node_modules/winston/node_modules/winston-transport/legacy.js +119 -0
  259. package/node_modules/winston/node_modules/winston-transport/package.json +56 -0
  260. package/node_modules/winston/package.json +76 -75
  261. package/package.json +8 -6
  262. package/node_modules/@dabh/diagnostics/example.png +0 -0
  263. package/node_modules/color-string/CHANGELOG.md +0 -18
  264. package/node_modules/colorspace/test.js +0 -14
  265. package/node_modules/fecha/CHANGELOG.md +0 -67
  266. package/node_modules/logform/.travis.yml +0 -17
  267. package/node_modules/winston/CHANGELOG.md +0 -560
@@ -1,695 +1,695 @@
1
- /* eslint-disable complexity,max-statements */
2
- /**
3
- * file.js: Transport for outputting to a local log file.
4
- *
5
- * (C) 2010 Charlie Robbins
6
- * MIT LICENCE
7
- */
8
-
9
- 'use strict';
10
-
11
- const fs = require('fs');
12
- const path = require('path');
13
- const asyncSeries = require('async/series');
14
- const zlib = require('zlib');
15
- const { MESSAGE } = require('triple-beam');
16
- const { Stream, PassThrough } = require('readable-stream');
17
- const TransportStream = require('winston-transport');
18
- const debug = require('@dabh/diagnostics')('winston:file');
19
- const os = require('os');
20
- const tailFile = require('../tail-file');
21
-
22
- /**
23
- * Transport for outputting to a local log file.
24
- * @type {File}
25
- * @extends {TransportStream}
26
- */
27
- module.exports = class File extends TransportStream {
28
- /**
29
- * Constructor function for the File transport object responsible for
30
- * persisting log messages and metadata to one or more files.
31
- * @param {Object} options - Options for this instance.
32
- */
33
- constructor(options = {}) {
34
- super(options);
35
-
36
- // Expose the name of this Transport on the prototype.
37
- this.name = options.name || 'file';
38
-
39
- // Helper function which throws an `Error` in the event that any of the
40
- // rest of the arguments is present in `options`.
41
- function throwIf(target, ...args) {
42
- args.slice(1).forEach(name => {
43
- if (options[name]) {
44
- throw new Error(`Cannot set ${name} and ${target} together`);
45
- }
46
- });
47
- }
48
-
49
- // Setup the base stream that always gets piped to to handle buffering.
50
- this._stream = new PassThrough();
51
- this._stream.setMaxListeners(30);
52
-
53
- // Bind this context for listener methods.
54
- this._onError = this._onError.bind(this);
55
-
56
- if (options.filename || options.dirname) {
57
- throwIf('filename or dirname', 'stream');
58
- this._basename = this.filename = options.filename
59
- ? path.basename(options.filename)
60
- : 'winston.log';
61
-
62
- this.dirname = options.dirname || path.dirname(options.filename);
63
- this.options = options.options || { flags: 'a' };
64
- } else if (options.stream) {
65
- // eslint-disable-next-line no-console
66
- console.warn('options.stream will be removed in winston@4. Use winston.transports.Stream');
67
- throwIf('stream', 'filename', 'maxsize');
68
- this._dest = this._stream.pipe(this._setupStream(options.stream));
69
- this.dirname = path.dirname(this._dest.path);
70
- // We need to listen for drain events when write() returns false. This
71
- // can make node mad at times.
72
- } else {
73
- throw new Error('Cannot log to file without filename or stream.');
74
- }
75
-
76
- this.maxsize = options.maxsize || null;
77
- this.rotationFormat = options.rotationFormat || false;
78
- this.zippedArchive = options.zippedArchive || false;
79
- this.maxFiles = options.maxFiles || null;
80
- this.eol = options.eol || os.EOL;
81
- this.tailable = options.tailable || false;
82
-
83
- // Internal state variables representing the number of files this instance
84
- // has created and the current size (in bytes) of the current logfile.
85
- this._size = 0;
86
- this._pendingSize = 0;
87
- this._created = 0;
88
- this._drain = false;
89
- this._opening = false;
90
- this._ending = false;
91
-
92
- if (this.dirname) this._createLogDirIfNotExist(this.dirname);
93
- this.open();
94
- }
95
-
96
- finishIfEnding() {
97
- if (this._ending) {
98
- if (this._opening) {
99
- this.once('open', () => {
100
- this._stream.once('finish', () => this.emit('finish'));
101
- setImmediate(() => this._stream.end());
102
- });
103
- } else {
104
- this._stream.once('finish', () => this.emit('finish'));
105
- setImmediate(() => this._stream.end());
106
- }
107
- }
108
- }
109
-
110
-
111
- /**
112
- * Core logging method exposed to Winston. Metadata is optional.
113
- * @param {Object} info - TODO: add param description.
114
- * @param {Function} callback - TODO: add param description.
115
- * @returns {undefined}
116
- */
117
- log(info, callback = () => {}) {
118
- // Remark: (jcrugzz) What is necessary about this callback(null, true) now
119
- // when thinking about 3.x? Should silent be handled in the base
120
- // TransportStream _write method?
121
- if (this.silent) {
122
- callback();
123
- return true;
124
- }
125
-
126
- // Output stream buffer is full and has asked us to wait for the drain event
127
- if (this._drain) {
128
- this._stream.once('drain', () => {
129
- this._drain = false;
130
- this.log(info, callback);
131
- });
132
- return;
133
- }
134
- if (this._rotate) {
135
- this._stream.once('rotate', () => {
136
- this._rotate = false;
137
- this.log(info, callback);
138
- });
139
- return;
140
- }
141
-
142
- // Grab the raw string and append the expected EOL.
143
- const output = `${info[MESSAGE]}${this.eol}`;
144
- const bytes = Buffer.byteLength(output);
145
-
146
- // After we have written to the PassThrough check to see if we need
147
- // to rotate to the next file.
148
- //
149
- // Remark: This gets called too early and does not depict when data
150
- // has been actually flushed to disk.
151
- function logged() {
152
- this._size += bytes;
153
- this._pendingSize -= bytes;
154
-
155
- debug('logged %s %s', this._size, output);
156
- this.emit('logged', info);
157
-
158
- // Do not attempt to rotate files while opening
159
- if (this._opening) {
160
- return;
161
- }
162
-
163
- // Check to see if we need to end the stream and create a new one.
164
- if (!this._needsNewFile()) {
165
- return;
166
- }
167
-
168
- // End the current stream, ensure it flushes and create a new one.
169
- // This could potentially be optimized to not run a stat call but its
170
- // the safest way since we are supporting `maxFiles`.
171
- this._rotate = true;
172
- this._endStream(() => this._rotateFile());
173
- }
174
-
175
- // Keep track of the pending bytes being written while files are opening
176
- // in order to properly rotate the PassThrough this._stream when the file
177
- // eventually does open.
178
- this._pendingSize += bytes;
179
- if (this._opening
180
- && !this.rotatedWhileOpening
181
- && this._needsNewFile(this._size + this._pendingSize)) {
182
- this.rotatedWhileOpening = true;
183
- }
184
-
185
- const written = this._stream.write(output, logged.bind(this));
186
- if (!written) {
187
- this._drain = true;
188
- this._stream.once('drain', () => {
189
- this._drain = false;
190
- callback();
191
- });
192
- } else {
193
- callback(); // eslint-disable-line callback-return
194
- }
195
-
196
- debug('written', written, this._drain);
197
-
198
- this.finishIfEnding();
199
-
200
- return written;
201
- }
202
-
203
- /**
204
- * Query the transport. Options object is optional.
205
- * @param {Object} options - Loggly-like query options for this instance.
206
- * @param {function} callback - Continuation to respond to when complete.
207
- * TODO: Refactor me.
208
- */
209
- query(options, callback) {
210
- if (typeof options === 'function') {
211
- callback = options;
212
- options = {};
213
- }
214
-
215
- options = normalizeQuery(options);
216
- const file = path.join(this.dirname, this.filename);
217
- let buff = '';
218
- let results = [];
219
- let row = 0;
220
-
221
- const stream = fs.createReadStream(file, {
222
- encoding: 'utf8'
223
- });
224
-
225
- stream.on('error', err => {
226
- if (stream.readable) {
227
- stream.destroy();
228
- }
229
- if (!callback) {
230
- return;
231
- }
232
-
233
- return err.code !== 'ENOENT' ? callback(err) : callback(null, results);
234
- });
235
-
236
- stream.on('data', data => {
237
- data = (buff + data).split(/\n+/);
238
- const l = data.length - 1;
239
- let i = 0;
240
-
241
- for (; i < l; i++) {
242
- if (!options.start || row >= options.start) {
243
- add(data[i]);
244
- }
245
- row++;
246
- }
247
-
248
- buff = data[l];
249
- });
250
-
251
- stream.on('close', () => {
252
- if (buff) {
253
- add(buff, true);
254
- }
255
- if (options.order === 'desc') {
256
- results = results.reverse();
257
- }
258
-
259
- // eslint-disable-next-line callback-return
260
- if (callback) callback(null, results);
261
- });
262
-
263
- function add(buff, attempt) {
264
- try {
265
- const log = JSON.parse(buff);
266
- if (check(log)) {
267
- push(log);
268
- }
269
- } catch (e) {
270
- if (!attempt) {
271
- stream.emit('error', e);
272
- }
273
- }
274
- }
275
-
276
- function push(log) {
277
- if (
278
- options.rows &&
279
- results.length >= options.rows &&
280
- options.order !== 'desc'
281
- ) {
282
- if (stream.readable) {
283
- stream.destroy();
284
- }
285
- return;
286
- }
287
-
288
- if (options.fields) {
289
- log = options.fields.reduce((obj, key) => {
290
- obj[key] = log[key];
291
- return obj;
292
- }, {});
293
- }
294
-
295
- if (options.order === 'desc') {
296
- if (results.length >= options.rows) {
297
- results.shift();
298
- }
299
- }
300
- results.push(log);
301
- }
302
-
303
- function check(log) {
304
- if (!log) {
305
- return;
306
- }
307
-
308
- if (typeof log !== 'object') {
309
- return;
310
- }
311
-
312
- const time = new Date(log.timestamp);
313
- if (
314
- (options.from && time < options.from) ||
315
- (options.until && time > options.until) ||
316
- (options.level && options.level !== log.level)
317
- ) {
318
- return;
319
- }
320
-
321
- return true;
322
- }
323
-
324
- function normalizeQuery(options) {
325
- options = options || {};
326
-
327
- // limit
328
- options.rows = options.rows || options.limit || 10;
329
-
330
- // starting row offset
331
- options.start = options.start || 0;
332
-
333
- // now
334
- options.until = options.until || new Date();
335
- if (typeof options.until !== 'object') {
336
- options.until = new Date(options.until);
337
- }
338
-
339
- // now - 24
340
- options.from = options.from || (options.until - (24 * 60 * 60 * 1000));
341
- if (typeof options.from !== 'object') {
342
- options.from = new Date(options.from);
343
- }
344
-
345
- // 'asc' or 'desc'
346
- options.order = options.order || 'desc';
347
-
348
- return options;
349
- }
350
- }
351
-
352
- /**
353
- * Returns a log stream for this transport. Options object is optional.
354
- * @param {Object} options - Stream options for this instance.
355
- * @returns {Stream} - TODO: add return description.
356
- * TODO: Refactor me.
357
- */
358
- stream(options = {}) {
359
- const file = path.join(this.dirname, this.filename);
360
- const stream = new Stream();
361
- const tail = {
362
- file,
363
- start: options.start
364
- };
365
-
366
- stream.destroy = tailFile(tail, (err, line) => {
367
- if (err) {
368
- return stream.emit('error', err);
369
- }
370
-
371
- try {
372
- stream.emit('data', line);
373
- line = JSON.parse(line);
374
- stream.emit('log', line);
375
- } catch (e) {
376
- stream.emit('error', e);
377
- }
378
- });
379
-
380
- return stream;
381
- }
382
-
383
- /**
384
- * Checks to see the filesize of.
385
- * @returns {undefined}
386
- */
387
- open() {
388
- // If we do not have a filename then we were passed a stream and
389
- // don't need to keep track of size.
390
- if (!this.filename) return;
391
- if (this._opening) return;
392
-
393
- this._opening = true;
394
-
395
- // Stat the target file to get the size and create the stream.
396
- this.stat((err, size) => {
397
- if (err) {
398
- return this.emit('error', err);
399
- }
400
- debug('stat done: %s { size: %s }', this.filename, size);
401
- this._size = size;
402
- this._dest = this._createStream(this._stream);
403
- this._opening = false;
404
- this.once('open', () => {
405
- if (this._stream.eventNames().includes('rotate')) {
406
- this._stream.emit('rotate');
407
- } else {
408
- this._rotate = false;
409
- }
410
- });
411
- });
412
- }
413
-
414
- /**
415
- * Stat the file and assess information in order to create the proper stream.
416
- * @param {function} callback - TODO: add param description.
417
- * @returns {undefined}
418
- */
419
- stat(callback) {
420
- const target = this._getFile();
421
- const fullpath = path.join(this.dirname, target);
422
-
423
- fs.stat(fullpath, (err, stat) => {
424
- if (err && err.code === 'ENOENT') {
425
- debug('ENOENT ok', fullpath);
426
- // Update internally tracked filename with the new target name.
427
- this.filename = target;
428
- return callback(null, 0);
429
- }
430
-
431
- if (err) {
432
- debug(`err ${err.code} ${fullpath}`);
433
- return callback(err);
434
- }
435
-
436
- if (!stat || this._needsNewFile(stat.size)) {
437
- // If `stats.size` is greater than the `maxsize` for this
438
- // instance then try again.
439
- return this._incFile(() => this.stat(callback));
440
- }
441
-
442
- // Once we have figured out what the filename is, set it
443
- // and return the size.
444
- this.filename = target;
445
- callback(null, stat.size);
446
- });
447
- }
448
-
449
- /**
450
- * Closes the stream associated with this instance.
451
- * @param {function} cb - TODO: add param description.
452
- * @returns {undefined}
453
- */
454
- close(cb) {
455
- if (!this._stream) {
456
- return;
457
- }
458
-
459
- this._stream.end(() => {
460
- if (cb) {
461
- cb(); // eslint-disable-line callback-return
462
- }
463
- this.emit('flush');
464
- this.emit('closed');
465
- });
466
- }
467
-
468
- /**
469
- * TODO: add method description.
470
- * @param {number} size - TODO: add param description.
471
- * @returns {undefined}
472
- */
473
- _needsNewFile(size) {
474
- size = size || this._size;
475
- return this.maxsize && size >= this.maxsize;
476
- }
477
-
478
- /**
479
- * TODO: add method description.
480
- * @param {Error} err - TODO: add param description.
481
- * @returns {undefined}
482
- */
483
- _onError(err) {
484
- this.emit('error', err);
485
- }
486
-
487
- /**
488
- * TODO: add method description.
489
- * @param {Stream} stream - TODO: add param description.
490
- * @returns {mixed} - TODO: add return description.
491
- */
492
- _setupStream(stream) {
493
- stream.on('error', this._onError);
494
-
495
- return stream;
496
- }
497
-
498
- /**
499
- * TODO: add method description.
500
- * @param {Stream} stream - TODO: add param description.
501
- * @returns {mixed} - TODO: add return description.
502
- */
503
- _cleanupStream(stream) {
504
- stream.removeListener('error', this._onError);
505
-
506
- return stream;
507
- }
508
-
509
- /**
510
- * TODO: add method description.
511
- */
512
- _rotateFile() {
513
- this._incFile(() => this.open());
514
- }
515
-
516
- /**
517
- * Unpipe from the stream that has been marked as full and end it so it
518
- * flushes to disk.
519
- *
520
- * @param {function} callback - Callback for when the current file has closed.
521
- * @private
522
- */
523
- _endStream(callback = () => {}) {
524
- if (this._dest) {
525
- this._stream.unpipe(this._dest);
526
- this._dest.end(() => {
527
- this._cleanupStream(this._dest);
528
- callback();
529
- });
530
- } else {
531
- callback(); // eslint-disable-line callback-return
532
- }
533
- }
534
-
535
- /**
536
- * Returns the WritableStream for the active file on this instance. If we
537
- * should gzip the file then a zlib stream is returned.
538
- *
539
- * @param {ReadableStream} source – PassThrough to pipe to the file when open.
540
- * @returns {WritableStream} Stream that writes to disk for the active file.
541
- */
542
- _createStream(source) {
543
- const fullpath = path.join(this.dirname, this.filename);
544
-
545
- debug('create stream start', fullpath, this.options);
546
- const dest = fs.createWriteStream(fullpath, this.options)
547
- // TODO: What should we do with errors here?
548
- .on('error', err => debug(err))
549
- .on('close', () => debug('close', dest.path, dest.bytesWritten))
550
- .on('open', () => {
551
- debug('file open ok', fullpath);
552
- this.emit('open', fullpath);
553
- source.pipe(dest);
554
-
555
- // If rotation occured during the open operation then we immediately
556
- // start writing to a new PassThrough, begin opening the next file
557
- // and cleanup the previous source and dest once the source has drained.
558
- if (this.rotatedWhileOpening) {
559
- this._stream = new PassThrough();
560
- this._stream.setMaxListeners(30);
561
- this._rotateFile();
562
- this.rotatedWhileOpening = false;
563
- this._cleanupStream(dest);
564
- source.end();
565
- }
566
- });
567
-
568
- debug('create stream ok', fullpath);
569
- if (this.zippedArchive) {
570
- const gzip = zlib.createGzip();
571
- gzip.pipe(dest);
572
- return gzip;
573
- }
574
-
575
- return dest;
576
- }
577
-
578
- /**
579
- * TODO: add method description.
580
- * @param {function} callback - TODO: add param description.
581
- * @returns {undefined}
582
- */
583
- _incFile(callback) {
584
- debug('_incFile', this.filename);
585
- const ext = path.extname(this._basename);
586
- const basename = path.basename(this._basename, ext);
587
-
588
- if (!this.tailable) {
589
- this._created += 1;
590
- this._checkMaxFilesIncrementing(ext, basename, callback);
591
- } else {
592
- this._checkMaxFilesTailable(ext, basename, callback);
593
- }
594
- }
595
-
596
- /**
597
- * Gets the next filename to use for this instance in the case that log
598
- * filesizes are being capped.
599
- * @returns {string} - TODO: add return description.
600
- * @private
601
- */
602
- _getFile() {
603
- const ext = path.extname(this._basename);
604
- const basename = path.basename(this._basename, ext);
605
- const isRotation = this.rotationFormat
606
- ? this.rotationFormat()
607
- : this._created;
608
-
609
- // Caveat emptor (indexzero): rotationFormat() was broken by design When
610
- // combined with max files because the set of files to unlink is never
611
- // stored.
612
- const target = !this.tailable && this._created
613
- ? `${basename}${isRotation}${ext}`
614
- : `${basename}${ext}`;
615
-
616
- return this.zippedArchive && !this.tailable
617
- ? `${target}.gz`
618
- : target;
619
- }
620
-
621
- /**
622
- * Increment the number of files created or checked by this instance.
623
- * @param {mixed} ext - TODO: add param description.
624
- * @param {mixed} basename - TODO: add param description.
625
- * @param {mixed} callback - TODO: add param description.
626
- * @returns {undefined}
627
- * @private
628
- */
629
- _checkMaxFilesIncrementing(ext, basename, callback) {
630
- // Check for maxFiles option and delete file.
631
- if (!this.maxFiles || this._created < this.maxFiles) {
632
- return setImmediate(callback);
633
- }
634
-
635
- const oldest = this._created - this.maxFiles;
636
- const isOldest = oldest !== 0 ? oldest : '';
637
- const isZipped = this.zippedArchive ? '.gz' : '';
638
- const filePath = `${basename}${isOldest}${ext}${isZipped}`;
639
- const target = path.join(this.dirname, filePath);
640
-
641
- fs.unlink(target, callback);
642
- }
643
-
644
- /**
645
- * Roll files forward based on integer, up to maxFiles. e.g. if base if
646
- * file.log and it becomes oversized, roll to file1.log, and allow file.log
647
- * to be re-used. If file is oversized again, roll file1.log to file2.log,
648
- * roll file.log to file1.log, and so on.
649
- * @param {mixed} ext - TODO: add param description.
650
- * @param {mixed} basename - TODO: add param description.
651
- * @param {mixed} callback - TODO: add param description.
652
- * @returns {undefined}
653
- * @private
654
- */
655
- _checkMaxFilesTailable(ext, basename, callback) {
656
- const tasks = [];
657
- if (!this.maxFiles) {
658
- return;
659
- }
660
-
661
- // const isZipped = this.zippedArchive ? '.gz' : '';
662
- const isZipped = this.zippedArchive ? '.gz' : '';
663
- for (let x = this.maxFiles - 1; x > 1; x--) {
664
- tasks.push(function (i, cb) {
665
- let fileName = `${basename}${(i - 1)}${ext}${isZipped}`;
666
- const tmppath = path.join(this.dirname, fileName);
667
-
668
- fs.exists(tmppath, exists => {
669
- if (!exists) {
670
- return cb(null);
671
- }
672
-
673
- fileName = `${basename}${i}${ext}${isZipped}`;
674
- fs.rename(tmppath, path.join(this.dirname, fileName), cb);
675
- });
676
- }.bind(this, x));
677
- }
678
-
679
- asyncSeries(tasks, () => {
680
- fs.rename(
681
- path.join(this.dirname, `${basename}${ext}`),
682
- path.join(this.dirname, `${basename}1${ext}${isZipped}`),
683
- callback
684
- );
685
- });
686
- }
687
-
688
- _createLogDirIfNotExist(dirPath) {
689
- /* eslint-disable no-sync */
690
- if (!fs.existsSync(dirPath)) {
691
- fs.mkdirSync(dirPath, { recursive: true });
692
- }
693
- /* eslint-enable no-sync */
694
- }
695
- };
1
+ /* eslint-disable complexity,max-statements */
2
+ /**
3
+ * file.js: Transport for outputting to a local log file.
4
+ *
5
+ * (C) 2010 Charlie Robbins
6
+ * MIT LICENCE
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+ const asyncSeries = require('async/series');
14
+ const zlib = require('zlib');
15
+ const { MESSAGE } = require('triple-beam');
16
+ const { Stream, PassThrough } = require('readable-stream');
17
+ const TransportStream = require('winston-transport');
18
+ const debug = require('@dabh/diagnostics')('winston:file');
19
+ const os = require('os');
20
+ const tailFile = require('../tail-file');
21
+
22
+ /**
23
+ * Transport for outputting to a local log file.
24
+ * @type {File}
25
+ * @extends {TransportStream}
26
+ */
27
+ module.exports = class File extends TransportStream {
28
+ /**
29
+ * Constructor function for the File transport object responsible for
30
+ * persisting log messages and metadata to one or more files.
31
+ * @param {Object} options - Options for this instance.
32
+ */
33
+ constructor(options = {}) {
34
+ super(options);
35
+
36
+ // Expose the name of this Transport on the prototype.
37
+ this.name = options.name || 'file';
38
+
39
+ // Helper function which throws an `Error` in the event that any of the
40
+ // rest of the arguments is present in `options`.
41
+ function throwIf(target, ...args) {
42
+ args.slice(1).forEach(name => {
43
+ if (options[name]) {
44
+ throw new Error(`Cannot set ${name} and ${target} together`);
45
+ }
46
+ });
47
+ }
48
+
49
+ // Setup the base stream that always gets piped to to handle buffering.
50
+ this._stream = new PassThrough();
51
+ this._stream.setMaxListeners(30);
52
+
53
+ // Bind this context for listener methods.
54
+ this._onError = this._onError.bind(this);
55
+
56
+ if (options.filename || options.dirname) {
57
+ throwIf('filename or dirname', 'stream');
58
+ this._basename = this.filename = options.filename
59
+ ? path.basename(options.filename)
60
+ : 'winston.log';
61
+
62
+ this.dirname = options.dirname || path.dirname(options.filename);
63
+ this.options = options.options || { flags: 'a' };
64
+ } else if (options.stream) {
65
+ // eslint-disable-next-line no-console
66
+ console.warn('options.stream will be removed in winston@4. Use winston.transports.Stream');
67
+ throwIf('stream', 'filename', 'maxsize');
68
+ this._dest = this._stream.pipe(this._setupStream(options.stream));
69
+ this.dirname = path.dirname(this._dest.path);
70
+ // We need to listen for drain events when write() returns false. This
71
+ // can make node mad at times.
72
+ } else {
73
+ throw new Error('Cannot log to file without filename or stream.');
74
+ }
75
+
76
+ this.maxsize = options.maxsize || null;
77
+ this.rotationFormat = options.rotationFormat || false;
78
+ this.zippedArchive = options.zippedArchive || false;
79
+ this.maxFiles = options.maxFiles || null;
80
+ this.eol = (typeof options.eol === 'string') ? options.eol : os.EOL;
81
+ this.tailable = options.tailable || false;
82
+
83
+ // Internal state variables representing the number of files this instance
84
+ // has created and the current size (in bytes) of the current logfile.
85
+ this._size = 0;
86
+ this._pendingSize = 0;
87
+ this._created = 0;
88
+ this._drain = false;
89
+ this._opening = false;
90
+ this._ending = false;
91
+
92
+ if (this.dirname) this._createLogDirIfNotExist(this.dirname);
93
+ this.open();
94
+ }
95
+
96
+ finishIfEnding() {
97
+ if (this._ending) {
98
+ if (this._opening) {
99
+ this.once('open', () => {
100
+ this._stream.once('finish', () => this.emit('finish'));
101
+ setImmediate(() => this._stream.end());
102
+ });
103
+ } else {
104
+ this._stream.once('finish', () => this.emit('finish'));
105
+ setImmediate(() => this._stream.end());
106
+ }
107
+ }
108
+ }
109
+
110
+
111
+ /**
112
+ * Core logging method exposed to Winston. Metadata is optional.
113
+ * @param {Object} info - TODO: add param description.
114
+ * @param {Function} callback - TODO: add param description.
115
+ * @returns {undefined}
116
+ */
117
+ log(info, callback = () => {}) {
118
+ // Remark: (jcrugzz) What is necessary about this callback(null, true) now
119
+ // when thinking about 3.x? Should silent be handled in the base
120
+ // TransportStream _write method?
121
+ if (this.silent) {
122
+ callback();
123
+ return true;
124
+ }
125
+
126
+ // Output stream buffer is full and has asked us to wait for the drain event
127
+ if (this._drain) {
128
+ this._stream.once('drain', () => {
129
+ this._drain = false;
130
+ this.log(info, callback);
131
+ });
132
+ return;
133
+ }
134
+ if (this._rotate) {
135
+ this._stream.once('rotate', () => {
136
+ this._rotate = false;
137
+ this.log(info, callback);
138
+ });
139
+ return;
140
+ }
141
+
142
+ // Grab the raw string and append the expected EOL.
143
+ const output = `${info[MESSAGE]}${this.eol}`;
144
+ const bytes = Buffer.byteLength(output);
145
+
146
+ // After we have written to the PassThrough check to see if we need
147
+ // to rotate to the next file.
148
+ //
149
+ // Remark: This gets called too early and does not depict when data
150
+ // has been actually flushed to disk.
151
+ function logged() {
152
+ this._size += bytes;
153
+ this._pendingSize -= bytes;
154
+
155
+ debug('logged %s %s', this._size, output);
156
+ this.emit('logged', info);
157
+
158
+ // Do not attempt to rotate files while opening
159
+ if (this._opening) {
160
+ return;
161
+ }
162
+
163
+ // Check to see if we need to end the stream and create a new one.
164
+ if (!this._needsNewFile()) {
165
+ return;
166
+ }
167
+
168
+ // End the current stream, ensure it flushes and create a new one.
169
+ // This could potentially be optimized to not run a stat call but its
170
+ // the safest way since we are supporting `maxFiles`.
171
+ this._rotate = true;
172
+ this._endStream(() => this._rotateFile());
173
+ }
174
+
175
+ // Keep track of the pending bytes being written while files are opening
176
+ // in order to properly rotate the PassThrough this._stream when the file
177
+ // eventually does open.
178
+ this._pendingSize += bytes;
179
+ if (this._opening
180
+ && !this.rotatedWhileOpening
181
+ && this._needsNewFile(this._size + this._pendingSize)) {
182
+ this.rotatedWhileOpening = true;
183
+ }
184
+
185
+ const written = this._stream.write(output, logged.bind(this));
186
+ if (!written) {
187
+ this._drain = true;
188
+ this._stream.once('drain', () => {
189
+ this._drain = false;
190
+ callback();
191
+ });
192
+ } else {
193
+ callback(); // eslint-disable-line callback-return
194
+ }
195
+
196
+ debug('written', written, this._drain);
197
+
198
+ this.finishIfEnding();
199
+
200
+ return written;
201
+ }
202
+
203
+ /**
204
+ * Query the transport. Options object is optional.
205
+ * @param {Object} options - Loggly-like query options for this instance.
206
+ * @param {function} callback - Continuation to respond to when complete.
207
+ * TODO: Refactor me.
208
+ */
209
+ query(options, callback) {
210
+ if (typeof options === 'function') {
211
+ callback = options;
212
+ options = {};
213
+ }
214
+
215
+ options = normalizeQuery(options);
216
+ const file = path.join(this.dirname, this.filename);
217
+ let buff = '';
218
+ let results = [];
219
+ let row = 0;
220
+
221
+ const stream = fs.createReadStream(file, {
222
+ encoding: 'utf8'
223
+ });
224
+
225
+ stream.on('error', err => {
226
+ if (stream.readable) {
227
+ stream.destroy();
228
+ }
229
+ if (!callback) {
230
+ return;
231
+ }
232
+
233
+ return err.code !== 'ENOENT' ? callback(err) : callback(null, results);
234
+ });
235
+
236
+ stream.on('data', data => {
237
+ data = (buff + data).split(/\n+/);
238
+ const l = data.length - 1;
239
+ let i = 0;
240
+
241
+ for (; i < l; i++) {
242
+ if (!options.start || row >= options.start) {
243
+ add(data[i]);
244
+ }
245
+ row++;
246
+ }
247
+
248
+ buff = data[l];
249
+ });
250
+
251
+ stream.on('close', () => {
252
+ if (buff) {
253
+ add(buff, true);
254
+ }
255
+ if (options.order === 'desc') {
256
+ results = results.reverse();
257
+ }
258
+
259
+ // eslint-disable-next-line callback-return
260
+ if (callback) callback(null, results);
261
+ });
262
+
263
+ function add(buff, attempt) {
264
+ try {
265
+ const log = JSON.parse(buff);
266
+ if (check(log)) {
267
+ push(log);
268
+ }
269
+ } catch (e) {
270
+ if (!attempt) {
271
+ stream.emit('error', e);
272
+ }
273
+ }
274
+ }
275
+
276
+ function push(log) {
277
+ if (
278
+ options.rows &&
279
+ results.length >= options.rows &&
280
+ options.order !== 'desc'
281
+ ) {
282
+ if (stream.readable) {
283
+ stream.destroy();
284
+ }
285
+ return;
286
+ }
287
+
288
+ if (options.fields) {
289
+ log = options.fields.reduce((obj, key) => {
290
+ obj[key] = log[key];
291
+ return obj;
292
+ }, {});
293
+ }
294
+
295
+ if (options.order === 'desc') {
296
+ if (results.length >= options.rows) {
297
+ results.shift();
298
+ }
299
+ }
300
+ results.push(log);
301
+ }
302
+
303
+ function check(log) {
304
+ if (!log) {
305
+ return;
306
+ }
307
+
308
+ if (typeof log !== 'object') {
309
+ return;
310
+ }
311
+
312
+ const time = new Date(log.timestamp);
313
+ if (
314
+ (options.from && time < options.from) ||
315
+ (options.until && time > options.until) ||
316
+ (options.level && options.level !== log.level)
317
+ ) {
318
+ return;
319
+ }
320
+
321
+ return true;
322
+ }
323
+
324
+ function normalizeQuery(options) {
325
+ options = options || {};
326
+
327
+ // limit
328
+ options.rows = options.rows || options.limit || 10;
329
+
330
+ // starting row offset
331
+ options.start = options.start || 0;
332
+
333
+ // now
334
+ options.until = options.until || new Date();
335
+ if (typeof options.until !== 'object') {
336
+ options.until = new Date(options.until);
337
+ }
338
+
339
+ // now - 24
340
+ options.from = options.from || (options.until - (24 * 60 * 60 * 1000));
341
+ if (typeof options.from !== 'object') {
342
+ options.from = new Date(options.from);
343
+ }
344
+
345
+ // 'asc' or 'desc'
346
+ options.order = options.order || 'desc';
347
+
348
+ return options;
349
+ }
350
+ }
351
+
352
+ /**
353
+ * Returns a log stream for this transport. Options object is optional.
354
+ * @param {Object} options - Stream options for this instance.
355
+ * @returns {Stream} - TODO: add return description.
356
+ * TODO: Refactor me.
357
+ */
358
+ stream(options = {}) {
359
+ const file = path.join(this.dirname, this.filename);
360
+ const stream = new Stream();
361
+ const tail = {
362
+ file,
363
+ start: options.start
364
+ };
365
+
366
+ stream.destroy = tailFile(tail, (err, line) => {
367
+ if (err) {
368
+ return stream.emit('error', err);
369
+ }
370
+
371
+ try {
372
+ stream.emit('data', line);
373
+ line = JSON.parse(line);
374
+ stream.emit('log', line);
375
+ } catch (e) {
376
+ stream.emit('error', e);
377
+ }
378
+ });
379
+
380
+ return stream;
381
+ }
382
+
383
+ /**
384
+ * Checks to see the filesize of.
385
+ * @returns {undefined}
386
+ */
387
+ open() {
388
+ // If we do not have a filename then we were passed a stream and
389
+ // don't need to keep track of size.
390
+ if (!this.filename) return;
391
+ if (this._opening) return;
392
+
393
+ this._opening = true;
394
+
395
+ // Stat the target file to get the size and create the stream.
396
+ this.stat((err, size) => {
397
+ if (err) {
398
+ return this.emit('error', err);
399
+ }
400
+ debug('stat done: %s { size: %s }', this.filename, size);
401
+ this._size = size;
402
+ this._dest = this._createStream(this._stream);
403
+ this._opening = false;
404
+ this.once('open', () => {
405
+ if (this._stream.eventNames().includes('rotate')) {
406
+ this._stream.emit('rotate');
407
+ } else {
408
+ this._rotate = false;
409
+ }
410
+ });
411
+ });
412
+ }
413
+
414
+ /**
415
+ * Stat the file and assess information in order to create the proper stream.
416
+ * @param {function} callback - TODO: add param description.
417
+ * @returns {undefined}
418
+ */
419
+ stat(callback) {
420
+ const target = this._getFile();
421
+ const fullpath = path.join(this.dirname, target);
422
+
423
+ fs.stat(fullpath, (err, stat) => {
424
+ if (err && err.code === 'ENOENT') {
425
+ debug('ENOENT ok', fullpath);
426
+ // Update internally tracked filename with the new target name.
427
+ this.filename = target;
428
+ return callback(null, 0);
429
+ }
430
+
431
+ if (err) {
432
+ debug(`err ${err.code} ${fullpath}`);
433
+ return callback(err);
434
+ }
435
+
436
+ if (!stat || this._needsNewFile(stat.size)) {
437
+ // If `stats.size` is greater than the `maxsize` for this
438
+ // instance then try again.
439
+ return this._incFile(() => this.stat(callback));
440
+ }
441
+
442
+ // Once we have figured out what the filename is, set it
443
+ // and return the size.
444
+ this.filename = target;
445
+ callback(null, stat.size);
446
+ });
447
+ }
448
+
449
+ /**
450
+ * Closes the stream associated with this instance.
451
+ * @param {function} cb - TODO: add param description.
452
+ * @returns {undefined}
453
+ */
454
+ close(cb) {
455
+ if (!this._stream) {
456
+ return;
457
+ }
458
+
459
+ this._stream.end(() => {
460
+ if (cb) {
461
+ cb(); // eslint-disable-line callback-return
462
+ }
463
+ this.emit('flush');
464
+ this.emit('closed');
465
+ });
466
+ }
467
+
468
+ /**
469
+ * TODO: add method description.
470
+ * @param {number} size - TODO: add param description.
471
+ * @returns {undefined}
472
+ */
473
+ _needsNewFile(size) {
474
+ size = size || this._size;
475
+ return this.maxsize && size >= this.maxsize;
476
+ }
477
+
478
+ /**
479
+ * TODO: add method description.
480
+ * @param {Error} err - TODO: add param description.
481
+ * @returns {undefined}
482
+ */
483
+ _onError(err) {
484
+ this.emit('error', err);
485
+ }
486
+
487
+ /**
488
+ * TODO: add method description.
489
+ * @param {Stream} stream - TODO: add param description.
490
+ * @returns {mixed} - TODO: add return description.
491
+ */
492
+ _setupStream(stream) {
493
+ stream.on('error', this._onError);
494
+
495
+ return stream;
496
+ }
497
+
498
+ /**
499
+ * TODO: add method description.
500
+ * @param {Stream} stream - TODO: add param description.
501
+ * @returns {mixed} - TODO: add return description.
502
+ */
503
+ _cleanupStream(stream) {
504
+ stream.removeListener('error', this._onError);
505
+
506
+ return stream;
507
+ }
508
+
509
+ /**
510
+ * TODO: add method description.
511
+ */
512
+ _rotateFile() {
513
+ this._incFile(() => this.open());
514
+ }
515
+
516
+ /**
517
+ * Unpipe from the stream that has been marked as full and end it so it
518
+ * flushes to disk.
519
+ *
520
+ * @param {function} callback - Callback for when the current file has closed.
521
+ * @private
522
+ */
523
+ _endStream(callback = () => {}) {
524
+ if (this._dest) {
525
+ this._stream.unpipe(this._dest);
526
+ this._dest.end(() => {
527
+ this._cleanupStream(this._dest);
528
+ callback();
529
+ });
530
+ } else {
531
+ callback(); // eslint-disable-line callback-return
532
+ }
533
+ }
534
+
535
+ /**
536
+ * Returns the WritableStream for the active file on this instance. If we
537
+ * should gzip the file then a zlib stream is returned.
538
+ *
539
+ * @param {ReadableStream} source – PassThrough to pipe to the file when open.
540
+ * @returns {WritableStream} Stream that writes to disk for the active file.
541
+ */
542
+ _createStream(source) {
543
+ const fullpath = path.join(this.dirname, this.filename);
544
+
545
+ debug('create stream start', fullpath, this.options);
546
+ const dest = fs.createWriteStream(fullpath, this.options)
547
+ // TODO: What should we do with errors here?
548
+ .on('error', err => debug(err))
549
+ .on('close', () => debug('close', dest.path, dest.bytesWritten))
550
+ .on('open', () => {
551
+ debug('file open ok', fullpath);
552
+ this.emit('open', fullpath);
553
+ source.pipe(dest);
554
+
555
+ // If rotation occured during the open operation then we immediately
556
+ // start writing to a new PassThrough, begin opening the next file
557
+ // and cleanup the previous source and dest once the source has drained.
558
+ if (this.rotatedWhileOpening) {
559
+ this._stream = new PassThrough();
560
+ this._stream.setMaxListeners(30);
561
+ this._rotateFile();
562
+ this.rotatedWhileOpening = false;
563
+ this._cleanupStream(dest);
564
+ source.end();
565
+ }
566
+ });
567
+
568
+ debug('create stream ok', fullpath);
569
+ if (this.zippedArchive) {
570
+ const gzip = zlib.createGzip();
571
+ gzip.pipe(dest);
572
+ return gzip;
573
+ }
574
+
575
+ return dest;
576
+ }
577
+
578
+ /**
579
+ * TODO: add method description.
580
+ * @param {function} callback - TODO: add param description.
581
+ * @returns {undefined}
582
+ */
583
+ _incFile(callback) {
584
+ debug('_incFile', this.filename);
585
+ const ext = path.extname(this._basename);
586
+ const basename = path.basename(this._basename, ext);
587
+
588
+ if (!this.tailable) {
589
+ this._created += 1;
590
+ this._checkMaxFilesIncrementing(ext, basename, callback);
591
+ } else {
592
+ this._checkMaxFilesTailable(ext, basename, callback);
593
+ }
594
+ }
595
+
596
+ /**
597
+ * Gets the next filename to use for this instance in the case that log
598
+ * filesizes are being capped.
599
+ * @returns {string} - TODO: add return description.
600
+ * @private
601
+ */
602
+ _getFile() {
603
+ const ext = path.extname(this._basename);
604
+ const basename = path.basename(this._basename, ext);
605
+ const isRotation = this.rotationFormat
606
+ ? this.rotationFormat()
607
+ : this._created;
608
+
609
+ // Caveat emptor (indexzero): rotationFormat() was broken by design When
610
+ // combined with max files because the set of files to unlink is never
611
+ // stored.
612
+ const target = !this.tailable && this._created
613
+ ? `${basename}${isRotation}${ext}`
614
+ : `${basename}${ext}`;
615
+
616
+ return this.zippedArchive && !this.tailable
617
+ ? `${target}.gz`
618
+ : target;
619
+ }
620
+
621
+ /**
622
+ * Increment the number of files created or checked by this instance.
623
+ * @param {mixed} ext - TODO: add param description.
624
+ * @param {mixed} basename - TODO: add param description.
625
+ * @param {mixed} callback - TODO: add param description.
626
+ * @returns {undefined}
627
+ * @private
628
+ */
629
+ _checkMaxFilesIncrementing(ext, basename, callback) {
630
+ // Check for maxFiles option and delete file.
631
+ if (!this.maxFiles || this._created < this.maxFiles) {
632
+ return setImmediate(callback);
633
+ }
634
+
635
+ const oldest = this._created - this.maxFiles;
636
+ const isOldest = oldest !== 0 ? oldest : '';
637
+ const isZipped = this.zippedArchive ? '.gz' : '';
638
+ const filePath = `${basename}${isOldest}${ext}${isZipped}`;
639
+ const target = path.join(this.dirname, filePath);
640
+
641
+ fs.unlink(target, callback);
642
+ }
643
+
644
+ /**
645
+ * Roll files forward based on integer, up to maxFiles. e.g. if base if
646
+ * file.log and it becomes oversized, roll to file1.log, and allow file.log
647
+ * to be re-used. If file is oversized again, roll file1.log to file2.log,
648
+ * roll file.log to file1.log, and so on.
649
+ * @param {mixed} ext - TODO: add param description.
650
+ * @param {mixed} basename - TODO: add param description.
651
+ * @param {mixed} callback - TODO: add param description.
652
+ * @returns {undefined}
653
+ * @private
654
+ */
655
+ _checkMaxFilesTailable(ext, basename, callback) {
656
+ const tasks = [];
657
+ if (!this.maxFiles) {
658
+ return;
659
+ }
660
+
661
+ // const isZipped = this.zippedArchive ? '.gz' : '';
662
+ const isZipped = this.zippedArchive ? '.gz' : '';
663
+ for (let x = this.maxFiles - 1; x > 1; x--) {
664
+ tasks.push(function (i, cb) {
665
+ let fileName = `${basename}${(i - 1)}${ext}${isZipped}`;
666
+ const tmppath = path.join(this.dirname, fileName);
667
+
668
+ fs.exists(tmppath, exists => {
669
+ if (!exists) {
670
+ return cb(null);
671
+ }
672
+
673
+ fileName = `${basename}${i}${ext}${isZipped}`;
674
+ fs.rename(tmppath, path.join(this.dirname, fileName), cb);
675
+ });
676
+ }.bind(this, x));
677
+ }
678
+
679
+ asyncSeries(tasks, () => {
680
+ fs.rename(
681
+ path.join(this.dirname, `${basename}${ext}`),
682
+ path.join(this.dirname, `${basename}1${ext}${isZipped}`),
683
+ callback
684
+ );
685
+ });
686
+ }
687
+
688
+ _createLogDirIfNotExist(dirPath) {
689
+ /* eslint-disable no-sync */
690
+ if (!fs.existsSync(dirPath)) {
691
+ fs.mkdirSync(dirPath, { recursive: true });
692
+ }
693
+ /* eslint-enable no-sync */
694
+ }
695
+ };