You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
227 lines
6.5 KiB
227 lines
6.5 KiB
|
|
const fs = require("fs");
|
|
const util = require("util");
|
|
const http = require("http");
|
|
const https = require("https");
|
|
|
|
|
|
function timestamp() {
|
|
return new Date().toISOString();
|
|
}
|
|
|
|
function formatOutput(...args) {
|
|
return `${timestamp()} - ${util.format(...args)}\n`;
|
|
}
|
|
|
|
function parseStreams(config) {
|
|
if (config.file) {
|
|
for (let i = 0; i < config.file.length; i++) {
|
|
const file = config.file[i];
|
|
let path = file.path || `${__dirname}/${timestamp()}.log`;
|
|
|
|
try {
|
|
file.stream = fs.createWriteStream(path, { flags: "a", autoClose: true });
|
|
|
|
} catch (e) {
|
|
console.error(`failed to open file at: ${path} for writing. Check the path in the config, and permissions for that directory and this process`);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (config.console) {
|
|
for (let i = 0; i < config.console.length; i++) {
|
|
const tty = config.console[i];
|
|
|
|
if (
|
|
!(tty.stdstream === "stdout"
|
|
|| tty.stdstream === "stderr"
|
|
|| tty.stdstream === "stdin")
|
|
) {
|
|
console.error(tty, "console items in config must have a key 'stdstream' set to one of the following: 'stdout', 'stderr', or 'stdin'. Check your config.")
|
|
process.exit(1);
|
|
}
|
|
|
|
tty.stream = process[tty.stdstream];
|
|
}
|
|
}
|
|
|
|
if (config.http) {
|
|
for (let i = 0; i < config.http.length; i++) {
|
|
const h = config.http[i];
|
|
|
|
if (!h.method) {
|
|
h.method = "POST";
|
|
|
|
} else if (h.method === "GET") {
|
|
console.warn(`using the http method 'GET' to send your logs is not recommended - in particular if you are intending to send a request body. Some system libraries are known to strip the payload completely off of GET requests, making this unreliable, despite the http 1.1 standard technically allowing it. See: https://github.com/whatwg/fetch/issues/551#issue-235203784`);
|
|
}
|
|
|
|
if (!h.url) {
|
|
console.error(`Please specify a 'url' on your http object in your configuration. The url should be full - the protocol and port should be included`);
|
|
process.exit(1);
|
|
}
|
|
|
|
if (!h.headers || !h.headers["content-type"]) {
|
|
h.headers = {
|
|
"content-type": "text/plain"
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function loadAndParseConfig(configFilePath) {
|
|
let config;
|
|
try {
|
|
const fileContents = fs.readFileSync(configFilePath, { encoding: "utf-8", flag: "r" });
|
|
try {
|
|
config = JSON.parse(fileContents);
|
|
|
|
} catch (e) {
|
|
console.error(`invalid JSON in your configuration file: ${configFilePath}`);
|
|
process.exit(1);
|
|
}
|
|
} catch(e) {
|
|
// no config file, warn about it, make a default
|
|
console.warn(`no configuration file found at: ${configFilePath}. Using a default config.`);
|
|
config = {
|
|
"file": [
|
|
{
|
|
"tags": "all"
|
|
},
|
|
],
|
|
"console": [
|
|
{
|
|
"tags": "error",
|
|
"stdstream": "stderr"
|
|
},
|
|
{
|
|
"tags": "warn",
|
|
"stdstream": "stdout"
|
|
},
|
|
{
|
|
"tags": "info",
|
|
"stdstream": "stdout"
|
|
},
|
|
{
|
|
"tags": "verbose",
|
|
"stdstream": "stdout"
|
|
},
|
|
{
|
|
"tags": [ "all", "log" ],
|
|
"stdstream": "stdout"
|
|
}
|
|
],
|
|
"http": [
|
|
{
|
|
"tags": "none",
|
|
"url": "http://localhost:8080"
|
|
}
|
|
]
|
|
};
|
|
}
|
|
|
|
parseStreams(config);
|
|
|
|
return config;
|
|
}
|
|
|
|
const configFilePath = __dirname + "/log4jesus.json";
|
|
const config = loadAndParseConfig(configFilePath);
|
|
|
|
function getStreamsOfTypeAndTags(tags, type) {
|
|
const streams = config[type];
|
|
const out = [];
|
|
for (let i = 0; i < streams.length; i++) {
|
|
const stream = streams[i];
|
|
|
|
if (!Array.isArray(stream.tags)) {
|
|
stream.tags = [ stream.tags ];
|
|
}
|
|
|
|
for (let j = 0; j < stream.tags.length; j++) {
|
|
for (let k = 0; k < tags.length; k++) {
|
|
if (stream.tags[j] === tags[k]) {
|
|
out.push({ type, ...stream });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function getStreamsByTags(tags) {
|
|
return [].concat(
|
|
getStreamsOfTypeAndTags(tags, "file"),
|
|
getStreamsOfTypeAndTags(tags, "console"),
|
|
getStreamsOfTypeAndTags(tags, "http")
|
|
);
|
|
}
|
|
|
|
// convienent list of streams that we always want to send our logs to, regardless of tags
|
|
const alwaysStreams = getStreamsByTags(["all"]);
|
|
|
|
function httpRequest(options, payload) {
|
|
const url = new URL(options.url);
|
|
const client = url.protocol === "https" ? https : http;
|
|
options.headers["content-length"] = payload.length;
|
|
const request = client.request(url, options, response => {
|
|
response.on("data", data => {
|
|
// @TODO, if you care what the server responds with
|
|
});
|
|
});
|
|
|
|
request.on("error", error => {
|
|
// @TODO, if you care about errors on the request
|
|
});
|
|
|
|
request.write(payload);
|
|
request.end();
|
|
}
|
|
|
|
// log by itself will only log to streams tagged 'all'. if you want to specifiy tags, call 'logt' instead
|
|
function log(...args) {
|
|
const output = formatOutput(...args);
|
|
|
|
for (let i = 0; i < alwaysStreams.length; i++) {
|
|
const s = alwaysStreams[i];
|
|
|
|
if (s.stream) {
|
|
s.stream.write(output);
|
|
|
|
} else if (s.type === "http") {
|
|
httpRequest(s, output);
|
|
}
|
|
}
|
|
}
|
|
|
|
// |tags| should be a string or array of strings identifying a key in the above configuration object
|
|
// which informs the logger 'where' to output the log
|
|
function logt(tags, ...args) {
|
|
if (!args) {
|
|
log(tags);
|
|
return;
|
|
}
|
|
|
|
if (!Array.isArray(tags)) {
|
|
tags = [ tags ];
|
|
}
|
|
|
|
const output = formatOutput(...args);
|
|
const streams = getStreamsByTags(tags);
|
|
for (let i = 0; i < streams.length; i++) {
|
|
const s = streams[i];
|
|
|
|
|
|
if (s.stream) {
|
|
s.stream.write(output);
|
|
|
|
} else if (s.type === "http") {
|
|
httpRequest(s, output);
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = { log, logt };
|
|
|