node日志接入kafka(封裝kafka-node)

準備工作

  • npm i kafka-node
  • 拷貝 Logger.js 進 node 項目

查看kafka-node文檔

文檔地址https://www.npmjs.com/package/kafka-node
需求日志接入kafka存到elk上,各種要求都不高,找一段最基礎的發送message

var kafka = require('kafka-node'),
    Producer = kafka.Producer,
    KeyedMessage = kafka.KeyedMessage,
    client = new kafka.KafkaClient(),
    producer = new Producer(client),
    km = new KeyedMessage('key', 'message'),
    payloads = [
        { topic: 'topic1', messages: 'hi', partition: 0 },
        { topic: 'topic2', messages: ['hello', 'world', km] }
    ];
producer.on('ready', function () {
    producer.send(payloads, function (err, data) {
        console.log(data);
    });
});
 
producer.on('error', function (err) {})

來觀察一下這個乍看平平無奇的例子,我需要的調用方式為log.info('XXX'),
而這里使用PubSub模式實現了分布式事件,并不是像axios那樣可以直接用promise去處理異步邏輯。

producer.on('ready', function () {
    producer.send(payloads, function (err, data) {
        console.log(data);
    });
});

為了讓調用方有精神上的愉悅感(并不想:)),這塊需要處理一下

  • PubSub模式
    題外話,根據《JavaScript異步編程》,PubSub簡單實現類似于
PubSub = {handlers: {}}

PubSub.on = function(eventType, handler) { 
  if (!(eventType in this.handlers)) {this.handlers[eventType] = []; }
  this.handlers[eventType].push(handler);
  return this; 
}

// 接著,等到觸發事件的時候,再循環遍歷所有的事件處理器。
PubSub.emit = function(eventType) {
  var handlerArgs = Array.prototype.slice.call(arguments, 1); 
  for (var i = 0; i < this.handlers[eventType].length; i++) {
    this.handlers[eventType][i].apply(this, handlerArgs);
  }
  return this; 
}

Logger代碼如下

// Logger.js
var kafka = require('kafka-node'),
    Producer = kafka.Producer;
const EVENT = ['DEBUG', 'INFO', 'WARN', 'ERROR'];

function Logger(options) {
    let client = new kafka.KafkaClient({
        kafkaHost: 'logkafkafat05.test.com:9092',
    });
    this.producer = new Producer(client);
    this.ready = readyFunc(this.producer);
    this.message = createMessage(options);
}
// 發日志
Logger.prototype.sendMessage = function ({ level, layoutMessage }) {
    let wholeMes = Object.assign(this.message, {
        level,
        layoutMessage,
        timeStamp: new Date().getTime(),
    });
    let payloads = [
        {
            topic: 'test.log',
            messages: JSON.stringify(wholeMes),
        },
    ];

    return this.ready.then(() => {
        return new Promise((resolve, reject) => {
            this.producer.send(payloads, function (err, data) {
                // console.log(data);
                resolve(data);
            });
        });
    });
};

// 支持debug、warn、info、error等方法
EVENT.map((method) => {
    Logger.prototype[method.toLowerCase()] = function (mes) {
        return this.sendMessage({ level: method, layoutMessage: mes });
    };
});
function readyFunc(producer) {
    return new Promise((resolve, reject) => {
        return producer.on('ready', resolve);
    });
}
function createMessage({ appId, logName }) {
    return {
        appId,
        logName,
        level: '',
        layoutMessage: '',
        tags: {
            HOST_IP: getIPAdress(),
            catTrace: '',
        },
    };
}
// 獲取本機ip
function getIPAdress() {
    var interfaces = require('os').networkInterfaces();
    for (var devName in interfaces) {
        var iface = interfaces[devName];
        for (var i = 0; i < iface.length; i++) {
            var alias = iface[i];
            if (
                alias.family === 'IPv4' &&
                alias.address !== '127.0.0.1' &&
                !alias.internal
            ) {
                return alias.address;
            }
        }
    }
}
module.exports = Logger;

使用

  • 支持兩類調用方式
var Logger = require('./Logger.js');

var log = new Logger({ appId: 'XXXXX', logName: 'htmlquicker' });

// 調用方式1類
log.warn('warn test');
log.info('info test');
log.debug('debug test');
log.error('error test');

// 調用方式2類
log.sendMessage({ level: 'INFO', layoutMessage: 'test' });
log.sendMessage({ level: 'WARN', layoutMessage: 'test' });
log.sendMessage({ level: 'DEBUG', layoutMessage: 'test' });
log.sendMessage({ level: 'ERROR', layoutMessage: 'test' });
  • koa 示例
    let resContent = {
        Content: {
            downloadPath: `/download/${encodeURIComponent(
                fileName
            )}.html`,
            fileName: fileName,
        },
    };
    ctx.body = response.success(resContent);
    log.info(
        JSON.stringify({
            href: ctx.href,
            ...resContent,
        })
    );
    
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容