Browse Source

some fixes

main
churchianity 2 years ago
parent
commit
4fb7e94770
  1. 38
      README.md
  2. 5
      test.js
  3. 11
      tlog.conf.default
  4. 13
      tlog.conf.js
  5. 134
      tlog.js

38
README.md

@ -2,17 +2,17 @@
# Usage
```js
const { log, logt } = require("./log4jesus");
const { log, logt } = require("./tlog");
```
## log
send log data to targets tagged with 'all'
```js
log("use", ["exactly", "like"], `console.log ${420 * 69}`)
log("use", ["exactly", "like"], `console.log ${17 * 35}`)
```
## logt
send log data to only targets tagged with one of the tags in the array passed as a first arguement.
send log data to only targets tagged with one of the tags in the array passed as a first argument.
first argument can be a string or array of strings.
```js
// send only to targets with the 'verbose' tag
@ -23,28 +23,40 @@ first argument can be a string or array of strings.
```
# Configuration
log4jesus supports 3 types of targets for your log data:
tlog 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.
to determine what logs go to which target, edit the file `tlog.conf.js`.
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
# Details
## 'file' object details
- path: optional string path to the file to create. defaults to the `data` directory, as a `.log` file named a ISO 8601 timestamp at time of creation.
## 'console' details
- stdstream: required value of 'stdout', 'stderr', or 'stdin'. It's not usually desired to make it 'stdin'.
## 'console' object details
- stdstream: required value of 'stdout', 'stderr', or 'stdin'. It's not usually desired to make it 'stdin'. outputs sent to stdstreams are automatically de-duplicated. even if there are multiple console outputs with the tag relevant to your logs that share the same stdstream, the output will only happen once.
## '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.
## 'http' object 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'.
Whichever method you choose, the data of the log will be sent in the request body.
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'. Certain combinations of port/protocol/path/host/hostname can behave strangely - it's recommended to specify as much information as possible in the 'url' field.
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'.
The configuration file is a .js file to enable using environment variables, as you may have sensitive information in your url(s).
### Handling Http Errors
Network requests can fail at runtime, and often those failures are also something you want to log.
Specify the `errorTags` property on the http object, and errors will attempt to be written to the target(s) specified when they occur.
If the target that failed is tagged with one of the tags on its own `errorTags` property, it will skip itself.
This feature is pretty limited and needs elaboration.
# Other Configuration Options/Settings
## timestamps
all logs are prefixed with an ISO 8601 timestamp. You can change this easily by editing the `timestamp` function, but for now it is not configurable from the config file.

5
test.js

@ -1,8 +1,7 @@
const { log, logt } = require("./log4jesus");
const { log, logt } = require("./tlog");
log("use", ["exactly", "like"], `console.log ${420 * 69}`)
log("use", ["exactly", "like"], `console.log ${17 * 15}`)
const downloadProgress = 51;
const totalDownloads = 106;

11
log4jesus.json → tlog.conf.default

@ -1,7 +1,8 @@
{
module.exports = {
"file": [
{
"tags": "all"
"tags": "httpErrors"
}
],
"console": [
@ -29,11 +30,13 @@
"http": [
{
"tags": "none",
"url": "http://localhost:8080",
"url": process.env.MY_SERVER_URL,
"errorTags": "httpErrors",
"method": "POST",
"headers": {
"content-type": "text/plain"
}
}
]
}
};

13
log4jesus.default.json → tlog.conf.js

@ -1,7 +1,8 @@
{
module.exports = {
"file": [
{
"tags": "all"
"tags": "httpErrors"
}
],
"console": [
@ -29,11 +30,13 @@
"http": [
{
"tags": "none",
"url": "https://localhost:8181",
"url": process.env.MY_SERVER_URL,
"errorTags": "httpErrors",
"method": "POST",
"headers": {
"content-type": "application/json"
"content-type": "text/plain"
}
}
]
}
};

134
log4jesus.js → tlog.js

@ -3,6 +3,7 @@ const fs = require("fs");
const util = require("util");
const http = require("http");
const https = require("https");
const crypto = require("crypto");
function timestamp() {
@ -17,7 +18,9 @@ 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`;
file.id = crypto.randomBytes(8).toString("hex");
let path = file.path || `${__dirname}/data/${timestamp()}.log`;
try {
file.stream = fs.createWriteStream(path, { flags: "a", autoClose: true });
@ -42,6 +45,7 @@ function parseStreams(config) {
process.exit(1);
}
tty.id = tty.stdstream;
tty.stream = process[tty.stdstream];
}
}
@ -50,11 +54,13 @@ function parseStreams(config) {
for (let i = 0; i < config.http.length; i++) {
const h = config.http[i];
h.id = crypto.randomBytes(8).toString("hex");
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`);
console.warn(`using the http method 'GET' to send your logs is not recommended. Unfortunately, sending payloads with a GET request can be unreliable.\nSee:\nhttps://github.com/whatwg/fetch/issues/551#issue-235203784\nRemove this line in tlog.js to suppress this warning.`);
}
if (!h.url) {
@ -67,68 +73,20 @@ function parseStreams(config) {
"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);
if (!h.errorTags) {
h.errorTags = [];
} catch (e) {
console.error(`invalid JSON in your configuration file: ${configFilePath}`);
process.exit(1);
} else if (!Array.isArray(h.errorTags)) {
h.errorTags = [ h.errorTags ];
}
}
} 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);
const config = parseStreams(require("./tlog.conf.js"));
function getStreamsOfTypeAndTags(tags, type) {
const streams = config[type];
@ -162,9 +120,10 @@ function getStreamsByTags(tags) {
// convienent list of streams that we always want to send our logs to, regardless of tags
const alwaysStreams = getStreamsByTags(["all"]);
function httpRequest(options, payload) {
function httpRequest(options, ...args) {
const url = new URL(options.url);
const client = url.protocol === "https" ? https : http;
const payload = formatOutput(...args);
options.headers["content-length"] = payload.length;
const request = client.request(url, options, response => {
response.on("data", data => {
@ -173,26 +132,41 @@ function httpRequest(options, payload) {
});
request.on("error", error => {
// @TODO, if you care about errors on the request
const streams = getStreamsByTags(options.errorTags);
const usedStreams = {};
for (let i = 0; i < streams.length; i++) {
const stream = streams[i];
if (usedStreams[stream.id]) {
continue;
}
usedStreams[stream.id] = true;
if (stream.id !== options.id) {
write(stream, "error writing to http: ", ...args);
}
}
});
request.write(payload);
request.write(formatOutput(...args));
request.end();
}
function write(s, ...args) {
if (s.stream) {
s.stream.write(formatOutput(...args));
} else if (s.type === "http") {
httpRequest(s, ...args);
}
}
// 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);
}
write(alwaysStreams[i], ...args);
}
}
@ -200,7 +174,6 @@ function log(...args) {
// which informs the logger 'where' to output the log
function logt(tags, ...args) {
if (!args) {
log(tags);
return;
}
@ -208,18 +181,29 @@ function logt(tags, ...args) {
tags = [ tags ];
}
const output = formatOutput(...args);
const usedStreams = {};
const streams = getStreamsByTags(tags);
for (let i = 0; i < streams.length; i++) {
const s = streams[i];
if (usedStreams[s.id]) {
continue;
}
if (s.stream) {
s.stream.write(output);
write(s, ...args);
usedStreams[s.id] = true;
}
} else if (s.type === "http") {
httpRequest(s, output);
// now send it to the 'all' streams. we need to check if we've already done the job, because in the case a stream is tagged with both 'all' and another tag, we'd double send if we didn't de-dupe.
for (let i = 0; i < alwaysStreams.length; i++) {
const s = alwaysStreams[i];
if (usedStreams[s.id]) {
continue;
}
write(s, ...args);
usedStreams[s.id] = true;
}
}
Loading…
Cancel
Save