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