Files
Zos/Skills/@be/node_modules/syslog-streams2/index.js

272 lines
8.3 KiB
JavaScript

'use strict';
var Transform = require('stream').Transform,
inherits = require('util').inherits,
os = require('os');
var Producer = require('@myndzi/glossy').Produce;
var clone = require('clone'),
tags = require('language-tags'),
Joi = require('joi');
var bunyanRecord = Joi.object().keys({
v: Joi.number().integer().min(0),
level: Joi.number().integer().min(0).max(100),
name: Joi.string(),
hostname: Joi.string().hostname(),
pid: Joi.number().integer().min(0),
time: Joi.alternatives(Joi.date(), Joi.string()),
msg: Joi.string()
});
var glossyRecord = Joi.object().keys({
facility: Joi.string(),
severity: Joi.string(),
host: Joi.string().hostname(),
appName: Joi.string(),
pid: Joi.number().integer().min(0),
date: Joi.date(),
message: Joi.string(),
structuredData: Joi.object()
});
var STRUCTURED_FIELDS = { timeQuality:1, origin:1, meta:1 };
var structuredData = Joi.object().keys({
timeQuality: Joi.object().keys({
tzKnown: Joi.number().integer().min(0).max(1),
isSynced: Joi.number().integer().min(0).max(1),
syncAccuracy: Joi.number().integer().min(0)
.when('isSynced', { is: 0, then: Joi.any().forbidden() })
}),
origin: Joi.object().keys({
ip: [
Joi.string().hostname(),
Joi.array().includes(
Joi.string().hostname()
)
],
enterpriseId: Joi.string().regex(/^\d+(\.\d+)*$/),
software: Joi.string().min(1).max(48),
swVersion: Joi.string().min(1).max(48)
}),
meta: Joi.object().keys({
sequenceId: Joi.number().integer().min(1).max(2147483647),
sysUpTime: Joi.number().integer().min(0),
language: Joi.string()
})
});
function SyslogStream(opts) { // jshint maxcomplexity: 20
Transform.call(this);
this._writableState.objectMode = true;
opts = opts || { };
this.decodeBuffers = opts.hasOwnProperty('decodeBuffers') ? opts.decodeBuffers : false;
this.decodeJSON = opts.hasOwnProperty('decodeJSON') ? opts.decodeJSON : false;
this.useStructuredData = opts.hasOwnProperty('useStructuredData') ? opts.useStructuredData : !opts.type;
this.defaultSeverity = opts.defaultSeverity || opts.defaultLevel || 'notice';
var PEN = parseInt(opts.PEN, 10);
this.PEN = !isNaN(PEN) ? PEN : null;
this.glossy = new Producer({
type: opts.type,
facility: typeof opts.facility === 'string' ? opts.facility.toLowerCase() : 'local0',
// severity: opts.severity || opts.level || 'info', -- this option doesn't get curried
host: opts.host || opts.hostname || os.hostname() || '-',
appName: opts.appName || opts.name || process.title || (process.argv && process.argv[0]) || '-',
msgID: opts.msgID || opts.msgId || '-',
pid: opts.pid || process.pid || '-'
});
}
inherits(SyslogStream, Transform);
SyslogStream.prototype._transform = function (_record, NA, callback) { // jshint maxstatements: 25, maxcomplexity: 12
var valid, str, record = clone(_record);
if (this.decodeBuffers && Buffer.isBuffer(record)) {
record = record.toString();
}
if (this.decodeJSON) {
try { record = JSON.parse(record); }
catch (e) { }
}
if (typeof record === 'string') {
str = this.buildStringMessage(record);
}
if (!str && record && record.msg) {
record.level = this.convertBunyanLevel(record.level);
valid = Joi.validate(record, bunyanRecord, { allowUnknown: true, convert: true });
if (valid.error === null) {
str = this.buildBunyanMessage(valid.value);
}
}
if (!str && record && record.message) {
valid = Joi.validate(record, glossyRecord, { allowUnknown: false, convert: true });
if (valid.error === null) {
str = this.buildGlossyMessage(valid.value);
}
}
if (!str || str === false) {
str = this.buildJSONMessage(clone(_record));
}
this.push(str+'\n');
callback();
};
SyslogStream.prototype.formatObject = function (obj) {
var seen = [ ];
return JSON.stringify(obj, function (key, val) {
if (!val || typeof val !== 'object') { return val; }
if (seen.indexOf(val) > -1) { return '[Circular]'; }
seen.push(val);
return val;
});
};
SyslogStream.prototype.buildStringMessage = function (str) {
return this.glossy.produce({
severity: this.defaultSeverity,
message: str
});
};
SyslogStream.prototype.buildJSONMessage = function (obj) {
return this.glossy.produce({
severity: this.defaultSeverity,
message: this.formatObject(obj)
});
};
SyslogStream.prototype.buildGlossyMessage = function (record) {
record.severity = record.severity || this.defaultSeverity;
var structured = this.useStructuredData && record.structuredData ?
this.validateStructuredData(record.structuredData) : { };
if (structured.data) {
record.structuredData = structured.data;
}
if (structured.extra) {
record.message += ' ' + this.formatObject(structured.extra);
}
return this.glossy.produce(record);
};
var BUNYAN = {
FATAL: 60,
ERROR: 50,
WARN: 40,
INFO: 30,
DEBUG: 20,
TRACE: 10
};
var SYSLOG = {
LEVEL: {
EMERG: 0, 0: 'emerg',
ALERT: 1, 1: 'alert',
CRIT: 2, 2: 'crit',
ERR: 3, 3: 'err',
WARNING: 4, 4: 'warn',
NOTICE: 5, 5: 'notice',
INFO: 6, 6: 'info',
DEBUG: 7, 7: 'debug'
}
};
var bunyanFields = { facility:1, level:1, hostname:1, name:1, pid:1, time:1, msg:1, msgId:1, v:1 };
SyslogStream.prototype.buildBunyanMessage = function (source) {
var extra = Object.keys(source).filter(function (key) {
return !(key in bunyanFields);
}).reduce(function (acc, key) {
acc[key] = source[key];
return acc;
}, { });
var structured = this.useStructuredData ?
this.validateStructuredData(extra) :
Object.keys(extra).length ? { extra: extra } : { };
return this.glossy.produce({
facility: source.facility,
severity: SYSLOG.LEVEL[source.level],
host: source.hostname,
appName: source.name,
pid: source.pid,
date: source.time,
msgID: source.msgId,
message: source.msg + (structured.extra ? ' ' + this.formatObject(structured.extra) : ''),
structuredData: structured.data
});
};
SyslogStream.prototype.convertBunyanLevel = function (level) { // jshint maxstatements: 18, maxcomplexity: 10
if (typeof level === 'string') { level = BUNYAN[level.toUpperCase()]; }
level = parseInt(level, 10);
if (isNaN(level)) { level = BUNYAN.INFO; }
if (level >= BUNYAN.FATAL) { return SYSLOG.LEVEL.EMERG; }
if (level >= BUNYAN.ERROR) { return SYSLOG.LEVEL.ERR; }
if (level >= BUNYAN.WARN) { return SYSLOG.LEVEL.WARNING; }
if (level >= BUNYAN.INFO) { return SYSLOG.LEVEL.NOTICE; }
if (level >= BUNYAN.DEBUG) { return SYSLOG.LEVEL.INFO; }
/*if (level >= 0) /*TRACE*/ return SYSLOG.LEVEL.DEBUG;
};
var INVALID_SDID = /[^\u0020-\u007e]|[@=\]"\s]/;
SyslogStream.prototype.validateStructuredData = function (obj) {
var structured = { data: { }, extra: { } };
var result = Joi.validate(obj, structuredData, { stripUnknown: true, convert: true });
if (result.error === null &&
obj.meta &&
obj.meta.language &&
!tags.check(obj.meta.language))
{
result.error = 'Invalid language tag';
}
if (result.error === null) {
structured.data = result.value;
} else {
structured.extra.SD_VALIDATION_ERROR = result.error.message || result.error;
}
Object.keys(obj).filter(function (key) {
return !(key in STRUCTURED_FIELDS);
}).forEach(function (key) {
var kv = obj[key];
if (this.PEN &&
!(INVALID_SDID.test(key)) &&
kv && typeof kv === 'object' &&
!(kv instanceof Date) &&
!(kv instanceof RegExp))
{
structured.data[key + '@' + this.PEN] = obj[key];
} else {
structured.extra[key] = obj[key];
}
}, this);
if (Object.keys(structured.extra).length === 0) { structured.extra = null; }
if (Object.keys(structured.data).length === 0) { structured.data = null; }
return structured;
};
module.exports = SyslogStream;