Browse Source

initial

main
churchianity 2 years ago
commit
2fb1cab0e9
  1. 47
      README.md
  2. 39
      log4jesus.default.json
  3. 229
      log4jesus.js
  4. 39
      log4jesus.json

47
README.md

@ -0,0 +1,47 @@
# Usage
```js
const { log, logt } = require("log4jesus");
```
## log
send log data to targets tagged with 'all'
```js
log("use", ["exactly", "like"], `console.log ${420 * 69}`)
```
## logt
send log data to only targets tagged with one of the tags in the array passed as a first arguement.
first argument can be a string or array of strings.
```js
// send only to targets with the 'verbose' tag
logt("verbose", "Download progress:", downloadProgress / totalDownloads);
// send to targets with the 'info' and 'error' tags
log(["error", "info"], "failed to parse json at: ", filePath)
```
# Configuration
log4jesus supports 3 types of targets for your log data:
- file
- console
- http
to determine what logs go to which target, edit the file `log4jesus.json`.
there are three arrays defined in that file, associated with a key of either 'file', 'console' or 'http.
in each case, the array should be an array of objects with a `tags` property (which should be a string or array of strings), as well as some other details about how this target should operate.
## 'file' details
- path: optional string to the file to create
## 'console' details
- stdstream: required value of 'stdout', 'stderr', or 'stdin'. It's not usually desired to make it 'stdin'.
## 'http' details
our http object is an extension of the [nodejs http/https options object](https://nodejs.org/api/http.html#httprequesturl-options-callback). The object is passed faithfully to http/s.request, so anything you add that is valid for http/s.request will be valid here.
Add a 'url' field to the object to specify the url for the request, which should include both the port and protocol - you usually won't need to set 'hostname' or 'host', or 'path' or 'port' or 'protocol'.
You may commonly have to set the 'headers' object, and the 'content-type' header inside of that object, depending on your server. If it's not provided it's set to 'text/plain'.

39
log4jesus.default.json

@ -0,0 +1,39 @@
{
"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": "https://localhost:8181",
"method": "POST",
"headers": {
"content-type": "application/json"
}
}
]
}

229
log4jesus.js

@ -0,0 +1,229 @@
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.txt`;
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) {
if (!Array.isArray(tags)) {
tags = [ 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 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 };

39
log4jesus.json

@ -0,0 +1,39 @@
{
"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": "https://localhost:8181",
"method": "POST",
"headers": {
"content-type": "application/json"
}
}
]
}
Loading…
Cancel
Save