2015-07-21 46 views
0

節點JS - 12.7 溫斯頓 - 1.0.1溫斯頓的NodeJS記錄大量寫入停滯不前

我,試圖產生「假」日誌代表許多「設備」文件掙扎。基本上我將在Kibana中使用這些數據進行測試和可視化。

設置我的溫斯頓高達日誌記錄文件爲這樣:

var winston = require('winston'); 

var logger = new (winston.Logger)({ 
transports: [ 
    new (winston.transports.File)({ 
     filename: 'logs/dsm.log' 
    }) 
    ] 
}); 

所以我有設備陣列這僅僅是一個列表12字節的哈希值,如:AB4576D4C6AC

我我通過設備陣列循環和組裝我的「假」的日誌條目,像這樣:

for (var i = 0; i < devices.length; i++) { 
    var timeStmp = startDateTime.clone().add((i * config.dsmIntervalMinutes), "m"); 
    dsm = { 
     deviceId: devices[i].deviceId, 
     msg: "some log entry text", 
     timeStmp: timeStmp 
    }; 
    logger.info(dsm); 
} 

正如你所看到的,我最終通過記錄消息info到Winst在循環中創建每個DSM後開啓。

所以這似乎工作得很好,除非我有大量的設備。我想要達到1米左右的設備。我目前正在努力做10萬。

我所看到的是節點進程不會將日誌寫入日誌文件。它似乎將它們全部存儲在內存中(如使用top所示),一旦它達到10k左右,它就會慢慢爬行並最終停止前進。日誌文件從不寫入。

所以我想知道是否有辦法做到這一點?我可以以某種方式迫使node/winston停止並偶爾寫入磁盤嗎?像刷新緩存一樣?我在這裏沒有看到溫斯頓的選擇。

任何幫助將不勝感激!

+0

你嘗試 爲(VAR I = 0; I bluesman

回答

1

這裏的問題並不是說循環是同步的,而是當你刷新到磁盤時,你需要停止寫入底層的fs.createWriteStream實例。通過不等待你繼續填充內部緩衝區,這就是爲什麼你的內存使用量無限增加。

所以考慮到這一點,在代碼示例變爲:

var fs = require('fs'); 
var winston = require('winston'); 

var stream = fs.createWriteStream('logs/dsm.log'); 
var file = new (winston.transports.File)({ stream: stream }); 
var logger = new (winston.Logger)({ transports: [file] }); 

// 
// We'll store this for our position in all devices across 
// `drain` events from the stream. 
// 
var curr = 0; 

// 
// function isFlushing() 
// Returns a value if the file is flushing 
// 
function isFlushing() { 
    var state = stream._writeableState; 
    return state.length < state.highWaterMark; 
} 

// 
// function logDevice() 
// Logs a single device and returns a value 
// indicating if the underlying filestream is full. 
// 
function logDevice(d) { 
    var timeStmp = startDateTime.clone().add((i * config.dsmIntervalMinutes), 'm'); 
    logger.info({ 
    deviceId: d.deviceId, 
    msg: 'some log entry text', 
    timeStmp: timeStmp 
    }); 

    return isFlushing(); 
} 

(function logAllDevices() { 
    for (; curr < devices.length; curr++) { 
    if (!logDevice(devices[curr])) { 
     stream.once('drain', logAllDevices); 
     break; 
    } 
    } 
})(); 
+0

感謝您的幫助。真心讚賞!我遇到了'isFlushing()'函數的問題。它返回一個異常'無法讀取未定義的屬性'長度'。我正在通過谷歌調查'stream._writeableState',但如果你有一個想法,真棒;) – warspite

+0

我不能得到isFlushing()工作。如果像getBuffer()和_writeableState.buffer那樣,我嘗試了一個數字 – warspite

1

這是因爲您正在使用for循環。 Node.js(JavaScript層)是單線程的。您的for循環阻止了實際進入寫入階段的過程。

將其更改爲這樣的事情,所以你遍歷所有的設備,而無需燻蒸過程:

function doLog (d, cb) { 
    var timeStmp = startDateTime.clone().add((i * config.dsmIntervalMinutes), "m"); 
    dsm = { 
     deviceId: d.deviceId, 
     msg: "some log entry text", 
     timeStmp: timeStmp 
    }; 
    // Callback is fired once the write is complete 
    logger.info(dsm, cb); 
} 

async.eachSeries(devices, doLog, function() { 
    log.info('Finished!') 
}); 

異步模塊將按順序一個接一個(eachSeries)後基本上運行的每個操作和內部使用setImmediate來在開始下一個設備doLog調用之前,延遲每個調用以允許I/O。您也可以考慮使用async.parallelLimit來同時完成一些操作,以獲得更好的性能。

+0

thx so much @eshortie我知道它與異步與同步有關,我只是不知道如何解決它。我之前沒有使用'async'。感謝把我放到那裏;)最後一個問題,'async.each()'和'async.eachSeries()'有什麼區別?我GOOGLE了它,但沒有發現太多,異步文檔沒有進入它。 – warspite

+0

沒問題。 async.eachSeries依次運行給定的函數和數組項。 async.each將同時運行每個項目的功能。一個警告; async.each假定函數run(例如doLog)是異步的,如果它不是那麼自然,操作不會真正並行運行,而是一旦它們可以完成。回調異步注入到你的doLog函數是一種方式來說每個系列的「嘿這一個完成,做下一個或調用最後的回調」每一個它是「嘿這是完成,檢查我們做了一切,如果是這樣的火最終回調「 – eshortie

+0

其實@eshortie我可能說得太快了。上述工作只適用於少數設備,但如果我將其調高到10000或更高,我觀察到每個日誌條目都沒有順序寫出,但它們保存在內存中(堆棧上下爬),然後寫出到最後的磁盤。有趣的是,eachSeries()回調函數被調用,但實際寫入操作還沒有完成,我的應用程序直到完成時才真正結束。任何想法爲什麼async.eachSeries()不會在每次迭代中強制寫入? – warspite