Nodejs 日志websocket实时输出

作者: 大猪大猪 | 来源:发表于2018-10-31 15:41 被阅读34次

有这样一个需求,想要通过websocket查看某些日志文件的输出(新的文件,或者是新添加的内容,可以按行输出),并且需要实时的,可采用系统的tail并结合spawn命令进行。

使用方法

package.json

{
  "name": "logs",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "koa": "^2.5.1",
    "koa-body": "^4.0.3",
    "koa-route": "^3.2.0",
    "koa-websocket": "^5.0.1",
    "uuid": "^3.3.2",
    "watch": "^1.0.2"
  }
}

安装

npm install

index.js

let fs = require('fs');
let spawn = require('child_process').spawn;
let watch = require('watch');
let uuid = require('uuid/v4');
const Koa = require('koa'),
    route = require('koa-route'),
    websockify = require('koa-websocket');
let dataFormat = require('./format.js');
const logPath = './logdir';
const port = 3001;

const app = websockify(new Koa());
let wsClients = new Map();
var tail = null;
let fun = function () {
    let dayPath = "20181023/127.0.0.1";
    if (tail != null) {
        try {
            tail.kill('SIGHUP');
        } catch (e) {
            console.log("tail程序关闭异常");
        }
    }
    if (fs.existsSync(`${logPath}/${dayPath}`)) {

        let filenames = fs.readdirSync(`${logPath}/${dayPath}`).filter(name => {
            return name.startsWith(("0" + new Date().getHours()).slice(-2) + "_")
        }).map(function (file) {
            return `${logPath}/${dayPath}/${file}`
        });

        tail = spawn("tail", ["-f"].concat(filenames));
        tail.stdout.on("data", function (data) {
            for (let filter of wsClients.values()) {
                try {
                    filter(data.toString("utf-8"))
                } catch (e) {
                    console.log(e);
                }
            }
        });
    } else {
        console.log(logPath + '/' + dayPath + '路径不存在');
    }
};

let preCreateTime = 0;
watch.watchTree(logPath, function (f, curr, prev) {
    if (typeof f == "object" && prev === null && curr === null) {
    } else if (prev === null) {
        if ((new Date() - preCreateTime) > 1000) {
            fun();
        }
        preCreateTime = new Date().getTime()
    } 
});

app.ws.use(function (ctx, next) {
    return next(ctx);
});

app.ws.use(route.all('/logs/:appKey/:openId', function (ctx) {
    let appKey = ctx.url.split("/")[2];
    let openId = ctx.url.split("/")[3];
    if (openId.indexOf("*") != -1 || appKey.indexOf("*") != -1) {
        ctx.websocket.send(JSON.stringify({status: 'fail', msg: '(openId|appKey)禁止使用正则表达式,请检查'}));
        ctx.websocket.close();
        return;
    }
    let clientId = uuid();
    wsClients.set(clientId, function (data) {
        if (data && data.indexOf(appKey) != -1 && data.indexOf(openId) != -1) {
            data.split("\n").forEach(line => {
                if (line && line.indexOf(appKey) != -1 && line.indexOf(openId) != -1) {
                    try {
                        ctx.websocket.send(line);
                    } catch (e) {
                        console.log('发送异常');
                    }
                }
            })
        }
    });
    ctx.websocket.on('close', function () {
        wsClients.delete(clientId);
    });

    ctx.websocket.on('error', function () {
        wsClients.delete(clientId);
        console.log('连接异常.');
    });

    ctx.websocket.on('message', function (message) {
        if (message === 'online') {
            ctx.websocket.send(JSON.stringify({status: 'success', msg: '当前在线人数:' + wsClients.size}));
        } else if (message === 'id') {
            ctx.websocket.send("你的id为:" + clientId);
        }
    });
}));

app.listen(port, function () {
    console.log(`files online tail listener port ${port}`);
    fun();
});

假设logdir目录结构如下

├── 20181023
│   └── 127.0.0.1
│       ├── 19_1.log
│       └── 20_2.log

使用chrome的socket client即可查看新增文件或修改文件的内容
可以说,由于调用系统的tail命令,基本是零延迟输出的。

相关文章

网友评论

    本文标题:Nodejs 日志websocket实时输出

    本文链接:https://www.haomeiwen.com/subject/fxuatqtx.html