在Node.js中解析巨大的日志文件
我需要在Javascript / Node.js(我使用Cube)中对大型(5-10 Gb)日志文件进行解析。
logline看起来像这样:
10:00:43.343423 I'm a friendly log message. There are 5 cats, and 7 dogs. We are in state "SUCCESS".
我们需要阅读每一行,做一些解析(如剔除5
, 7
,并SUCCESS
利用其客户端JS),则该泵的数据为立方体(https://github.com/square/cube)。
首先,Node中的规范方法是逐行读入文件?
这似乎是相当常见的在线问题:
很多答案似乎都指向了一堆第三方模块:
然而,这似乎是一个相当基本的任务 - 当然,stdlib中有一个简单的方法可以逐行读入文本文件?
其次,我需要处理每一行(例如,将时间戳转换为Date对象,并提取有用的字段)。
什么是最好的方式来做到这一点,最大限度地提高吞吐量? 是否有某种方法不会在每行读取或将其发送到Cube时阻塞?
第三 - 我猜测是使用字符串分割,并且包含(IndexOf!= -1?)的JS等价物比正则表达式要快很多? 有没有人在解析Node.js中的大量文本数据方面有很多经验?
干杯,维克多
我搜索了一个解决方案,使用流来逐行解析非常大的文件(gbs)。 所有的第三方库和示例都不适合我的需求,因为他们不是逐行处理文件(如1,2,3,4 ...)或将整个文件读取到内存中
以下解决方案可以使用stream&pipe逐行解析非常大的文件。 为了测试,我使用了一个2.1 GB的文件,记录了17万条记录。 公羊使用量不超过60 mb。
var fs = require('fs')
, es = require('event-stream');
var lineNr = 0;
var s = fs.createReadStream('very-large-file.csv')
.pipe(es.split())
.pipe(es.mapSync(function(line){
// pause the readstream
s.pause();
lineNr += 1;
// process line here and call s.resume() when rdy
// function below was for logging memory usage
logMemoryUsage(lineNr);
// resume the readstream, possibly from a callback
s.resume();
})
.on('error', function(err){
console.log('Error while reading file.', err);
})
.on('end', function(){
console.log('Read entire file.')
})
);
请让我知道它是怎么回事!
您可以使用内置的readline
包,请参阅文档。 我使用流创建一个新的输出流。
var fs = require('fs'),
readline = require('readline'),
stream = require('stream');
var instream = fs.createReadStream('/path/to/file');
var outstream = new stream;
outstream.readable = true;
outstream.writable = true;
var rl = readline.createInterface({
input: instream,
output: outstream,
terminal: false
});
rl.on('line', function(line) {
console.log(line);
//Do your stuff ...
//Then write to outstream
rl.write(cubestuff);
});
大文件需要一些时间来处理。 请告诉它是否有效。
我真的很喜欢@gerard答案,这实际上应该是这里的正确答案。 我做了一些改进:
代码如下:
'use strict'
const fs = require('fs'),
util = require('util'),
stream = require('stream'),
es = require('event-stream'),
parse = require("csv-parse"),
iconv = require('iconv-lite');
class CSVReader {
constructor(filename, batchSize, columns) {
this.reader = fs.createReadStream(filename).pipe(iconv.decodeStream('utf8'))
this.batchSize = batchSize || 1000
this.lineNumber = 0
this.data = []
this.parseOptions = {delimiter: 't', columns: true, escape: '/', relax: true}
}
read(callback) {
this.reader
.pipe(es.split())
.pipe(es.mapSync(line => {
++this.lineNumber
parse(line, this.parseOptions, (err, d) => {
this.data.push(d[0])
})
if (this.lineNumber % this.batchSize === 0) {
callback(this.data)
}
})
.on('error', function(){
console.log('Error while reading file.')
})
.on('end', function(){
console.log('Read entirefile.')
}))
}
continue () {
this.data = []
this.reader.resume()
}
}
module.exports = CSVReader
所以基本上,这里是你将如何使用它:
let reader = CSVReader('path_to_file.csv')
reader.read(() => reader.continue())
我用一个35GB的CSV文件测试了它,它为我工作,这就是为什么我选择在@ gerard的答案上构建它,欢迎反馈。
链接地址: http://www.djcxy.com/p/52277.html上一篇: Parsing huge logfiles in Node.js
下一篇: node.js: read a text file into an array. (Each line an item in the array.)