@signalk/streams
Version:
Utilities for handling streams of Signal K data
150 lines (133 loc) • 4.19 kB
JavaScript
/*
* Copyright 2017 Scott Bender (scott@scottbender.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const { FileTimestampStream } = require('file-timestamp-stream')
const path = require('path')
let debug = require('debug')('signalk:streams:logging')
const fs = require('fs')
const { isUndefined } = require('lodash')
const filenamePattern = /skserver-raw_\d\d\d\d-\d\d-\d\dT\d\d\.log/
const loggers = {}
module.exports = {
getLogger,
getFullLogDir,
listLogFiles
}
class FileTimestampStreamWithDelete extends FileTimestampStream {
constructor(app, fullLogDir, filesToKeep, options) {
super(options)
this.app = app
this.filesToKeep = filesToKeep
this.fullLogDir = fullLogDir
this.prevFilename = undefined
debug = (options.createDebug || require('debug'))('signalk:streams:logging')
}
// This method of base class is called when new file name is contemplated
// So let's override it to check how many files are there and delete the oldest ones
newFilename() {
if (this.prevFilename !== this.currentFilename) {
// Only do that after new file created
this.prevFilename = this.currentFilename
this.deleteOldFiles()
}
return super.newFilename()
}
deleteOldFiles() {
debug(`Checking for old log files`)
listLogFiles(this.app, (err, files) => {
if (err) {
console.error(err)
} else {
if (files.length > this.filesToKeep) {
const sortedFiles = files.sort()
const numToDelete = files.length - this.filesToKeep
debug(`Will delete ${numToDelete} files`)
for (let i = 0; i < numToDelete; i++) {
const fileName = path.join(this.fullLogDir, sortedFiles[i])
debug(`Deleting ${fileName}`)
fs.unlink(fileName, (err) => {
if (err) {
console.error(err)
} else {
debug(`${fileName} was deleted`)
}
})
}
}
}
})
}
}
function getLogger(app, discriminator = '', logdir) {
const fullLogdir = getFullLogDir(app, logdir)
if (!loggers[fullLogdir]) {
const fileName = path.join(fullLogdir, 'skserver-raw_%Y-%m-%dT%H.log')
debug(`logging to ${fileName}`)
let fileTimestampStream
if (
isUndefined(app.config.settings.keepMostRecentLogsOnly) ||
app.config.settings.keepMostRecentLogsOnly
) {
// Delete old logs
fileTimestampStream = new FileTimestampStreamWithDelete(
app,
fullLogdir,
app.config.settings.logCountToKeep,
{ path: fileName }
)
} else {
// Don't delete any logs
fileTimestampStream = new FileTimestampStream({ path: fileName })
}
loggers[fullLogdir] = fileTimestampStream
}
const logger = loggers[fullLogdir]
logger.on('error', (err) => {
console.error(`Error opening data logging file: ${err.message}`)
})
return (msg) => {
try {
logger.write(
Date.now() +
';' +
discriminator +
';' +
(msg.updates ? JSON.stringify(msg) : msg.toString()) +
'\n'
)
} catch (e) {
console.error(e)
}
}
}
function getFullLogDir(app, logdir) {
if (!logdir) {
logdir = app.config.settings.loggingDirectory || app.config.configPath
}
return path.isAbsolute(logdir)
? logdir
: path.join(app.config.configPath, logdir)
}
function listLogFiles(app, cb) {
fs.readdir(getFullLogDir(app), (err, files) => {
if (!err) {
cb(
undefined,
files.filter((filename) => filename.match(filenamePattern))
)
} else {
cb(err)
}
})
}