Index

lognestmonster / d4a21c0

A general-purpose single-header C logging library and parser for event-based logs. (Incomplete)

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
6028 Dec 2018 00:062f036c3Update README.mdJoshua12911N

Blob @ lognestmonster / README.md

text/plain18340 bytesdownload raw
1<img src="/static/logo.png" height="200px"/>
2
3_Advanced node.js logging for advanced programs._
4
51. [lognestmonster](#lognestmonster)
62. [Installation](#installation)
73. [Classes](#classes)
8 1. [Logger.Logger](#logger)
9 2. [Logger.Queue](#queue)
10 3. [Logger.Statement](#statement)
11 4. [Logger.Event](#event)
124. [Verbosity Levels](#verbosity-levels)
135. [Sample Usage](#sample-usage)
14 1. [Notes](#notes-about-this-example)
156. [Log Format](#log-format)
167. [Contributors](#contributors)
17 1. [Issue Reporting/Contributing](#issue-reporting-contributing)
18 2. [License](#license)
19
20# lognestmonster
21[![](https://img.shields.io/npm/dt/lognestmonster.svg)](https://www.npmjs.com/package/lognestmonster)
22[![](https://img.shields.io/github/issues/joshuas3/log-nest-monster.svg)](https://github.com/JoshuaS3/log-nest-monster/issues)
23[![](https://img.shields.io/github/issues-pr/joshuas3/log-nest-monster.svg)](https://github.com/JoshuaS3/log-nest-monster/pulls)
24[![](https://img.shields.io/npm/v/lognestmonster.svg)](https://www.npmjs.com/package/lognestmonster)
25[![](https://img.shields.io/npm/l/lognestmonster.svg)](https://www.npmjs.com/package/lognestmonster)
26
27Most loggers available only use a _linear_ method of logging; there are verbosity levels and tags to narrow searches, but everything is still on the same level or plane nonetheless. The package `lognestmonster` is a similar type of logger, but it allows you to create multiple **layers** (or "**nests**") of log statements. This is useful because, although you may have to put in some extra organizational work on the code side, the log results are much more clean and thorough, boosting workflow efficiency. The user has absolute control over the way log statements are pushed to their log files.
28
29_Why should I use this?_ There are many projects that create insane amounts of data, almost impossible to sift through without a helper program. The purpose of this logging system is to be a time-saver. Although it takes more time to put it in place, it's almost immediately made up with the performance gain through using the new log format. One thing that's unique about this logger compared to others is that it allows multiple queues to be made, in turn allowing you to split up your data. For example, a Node web server could keep one log file that records everything that the backend does while it uses another to record user or traffic information for analytics. Parsing software could be used to read either one.
30
31## Installation
32
33In your npm initiated package, you can install this package with the following command:
34```
35npm i lognestmonster
36```
37That's it! You can now `require("lognestmonster")` and begin using it.
38
39## Classes
40
41There are 4 classes offered by the package: `Logger`, the driving device that organizes everything; `Queue`, the class that actually pushes the log statements to their respective file; `Statement`, the actual log data (timestamp, verbosity, tag/invoker, message); and `Event`, a nest layer for `Statement`s or other `Event`s.
42
43The following subsections assume that `lognestmonster` has been `require`d by node.js with the following code:
44```javascript
45const Logger = require("lognestmonster");
46```
47
48### Logger
49
50Creates and organizes your queues. `Logger` is the exported package. `Logger.Logger` is the Logger class.
51
52Logger.Logger()
53```javascript
54var MyLogger = new Logger.Logger([Object config]);
55```
56Where `Object config` defaults to:
57```json
58{"name": "Logger", "locations": {"node": "./log/node"}, "compact": false}
59```
60`name` serves no purpose other than identification. `locations` is used to create new queues, taking each key as the queue name and each value as the queue output location. `compact` refers to the queue output (talked about later).
61
62Logger.Logger.queue()
63```javascript
64MyLogger.queue(string name);
65```
66This returns the appropriate `Queue` object for the provided `name`, assuming it was created with the `Logger` object.
67
68### Queue
69
70Manages log `Statement`s and `Event`s and how they're written to the final log file. Note that these are implicitly created with `Logger.Logger` when the `locations` object is properly provided in the `config` parameter of the `Logger.Logger` constructor.
71
72Logger.Queue()
73```javascript
74let MyQueue = new Logger.Queue(string name, string location[, object config]);
75// note that parameters are the same format as key-value
76// pairs in `config.locations` of `Logger.Logger(config)`
77```
78Where `Object config` defaults to:
79```json
80{"compact": false}
81```
82This creates the queue, taking `name` to be used as its ID and `location` as the path to where the log file should be created.
83
84When `compact` is set to `true` in the config argument, all keys of the Statement object are shortened when written as follows: `timestamp` becomes `tt`; `verbosity` becomes `v`; `tag` becomes `t`; `message`; becomes `m`.
85
86Logger.Queue.push()
87```javascript
88MyQueue.push(Statement statement);
89MyQueue.push(Event event);
90MyQueue.push(string verbosity, string tag, string message); // implicitly creates a `Statement` object
91```
92This adds log items or nests to the to-write queue. This returns the Queue object.
93
94Logger.Queue.write()
95```javascript
96MyQueue.write();
97MyQueue.write(Statement statement);
98MyQueue.write(Event event);
99MyQueue.write(string verbosity, string tag, string message);
100```
101This appends every queue value to the log file, emptying the queue. This returns the Queue object. If arguments are passed, they are written to the log independently of the order of the queue. This is not advised for anything but exception messages.
102
103### Statement
104
105This is the base log item where written log data is actually held.
106
107Logger.Statement()
108```javascript
109let MyStatement = Logger.Statement(string verbosity, string tag, string message);
110```
111
112The timestamp value is created automatically by the constructor.
113
114### Event
115
116This is the proper name for a nest. Essentially, it's just an array that can hold other `Event` objects and `Statement` objects, creating a tree.
117
118Logger.Event()
119```javascript
120let MyEvent = Logger.Event();
121let MyEvent = Logger.Event(Event event);
122let MyEvent = Logger.Event(Statement statement);
123let MyEvent = Logger.Event(string verbosity, string tag, string message);
124```
125Any arguments given are passed to `this.push()`.
126
127Logger.Event.push()
128```javascript
129MyEvent.push(Event event);
130MyEvent.push(Statement statement);
131MyEvent.push(string verbosity, string tag, string message);
132```
133This message pushes an `Event` or `Statement` as items in the nest. In the case that 3 strings are given as arguments, a `Statement` is implicitly created. This returns the Event object.
134
135## Verbosity Levels
136
137When creating a `Statement`, you can pass anything you'd like for the first `verbosity` string, although there are some ones preset by the package:
138
139`Logger.INFO`
140`Logger.DEBUG`
141`Logger.VERBOSE`
142`Logger.VERYVERBOSE`
143`Logger.WARNING`
144`Logger.ERROR`
145
146These verbosity levels can be used to narrow down your search results when parsing the log files.
147
148## Sample Usage
149
150Here's an example of how somebody would initiate the Logger, create and push items to the Queue, and write them to the log file. **PLEASE SEE THE NOTES ABOUT THIS EXAMPLE DOWN BELOW.** You can play with something similar in the package's included `tests/test.js` file.
151```javascript
152// Require the package
153const Logger = require("lognestmonster");
154
155// Creates the logger object. Placing the new Logger inside the package allows cross-file usage, so you only have to initiate once.
156Logger.Overseer = new Logger.Logger({
157 name: "Overseer",
158 locations: {
159 "node": "./log/node" // Creates a queue named `node` that uses the path `./log/node`
160 },
161 "compact": false // Indicates that we want our log to follow the traditional Statement format
162});
163
164// Pushes a statement directly to the `node` queue
165Logger.Overseer.queue("node").push(Logger.INFO, "PROCESS", "Process started");
166
167// Creates a new event and pushes a Statement to it
168let LoadEvent = new Logger.Event();
169LoadEvent.push(Logger.INFO, "INIT", "Acquiring needed top-level packages.");
170
171// Creates a new event and pushes multiple Statements to it
172let LowerNestedEvent = new Logger.Event();
173
174LowerNestedEvent.push(Logger.INFO, "INIT", "Loading fs...");
175LowerNestedEvent.push(Logger.DEBUG, "INIT", "fs loaded.");
176
177LowerNestedEvent.push(Logger.INFO, "INIT", "Loading http...");
178LowerNestedEvent.push(Logger.DEBUG, "INIT", "http loaded.");
179
180LowerNestedEvent.push(Logger.INFO, "INIT", "Loading jsonwebtoken...");
181LowerNestedEvent.push(Logger.DEBUG, "INIT", "jsonwebtoken loaded.");
182
183LowerNestedEvent.push(Logger.INFO, "INIT", "Loading lognestmonster...");
184LowerNestedEvent.push(Logger.DEBUG, "INIT", "lognestmonster loaded.");
185
186// Pushes the Event LowerNestedEvent to the Event LoadEvent
187LoadEvent.push(LowerNestedEvent);
188
189// Pushes another statement to LoadEvent
190LoadEvent.push(Logger.INFO, "INIT", "Finished.");
191
192// Pushes LoadEvent (a nest that consists of [Statement, Event, Statement] now) to the write queue
193Logger.Overseer.queue("node").push(LoadEvent);
194
195// Queue should now look like this: [Statement, Event]
196
197// Writes the queue, effectively emptying it into the log file
198Logger.Overseer.queue("node").write();
199```
200
201The above code creates the following JSON-like log output in the designated file:
202```json
203{"timestamp":"2018-12-26T18:08:37.654Z","verbosity":"INFO","tag":"PROCESS","message":"Process started"}
204[{"timestamp":"2018-12-26T18:08:37.655Z","verbosity":"INFO","tag":"INIT","message":"Acquiring needed top-level packages."},[{"timestamp":"2018-12-26T18:08:37.655Z","verbosity":"INFO","tag":"INIT","message":"Loading fs..."},{"timestamp":"2018-12-26T18:08:37.655Z","verbosity":"DEBUG","tag":"INIT","message":"fs loaded."},{"timestamp":"2018-12-26T18:08:37.655Z","verbosity":"INFO","tag":"INIT","message":"Loading http..."},{"timestamp":"2018-12-26T18:08:37.655Z","verbosity":"DEBUG","tag":"INIT","message":"http loaded."},{"timestamp":"2018-12-26T18:08:37.655Z","verbosity":"INFO","tag":"INIT","message":"Loading jsonwebtoken..."},{"timestamp":"2018-12-26T18:08:37.655Z","verbosity":"DEBUG","tag":"INIT","message":"jsonwebtoken loaded."},{"timestamp":"2018-12-26T18:08:37.655Z","verbosity":"INFO","tag":"INIT","message":"Loading lognestmonster..."},{"timestamp":"2018-12-26T18:08:37.655Z","verbosity":"DEBUG","tag":"INIT","message":"lognestmonster loaded."}],{"timestamp":"2018-12-26T18:08:37.655Z","verbosity":"INFO","tag":"INIT","message":"Finished."}]
205```
206
207_To see how to parse this data (putting it into proper JSON), see the Log Format section._
208
209### Notes about this example
210
211_Large projects_
212If your project spans multiple files, you could easily place your Logger object into the package itself, allowing the same Logger object to be accessed by other parts of your project. This is seen in the sample code with `Logger.Overseer = new Logger.Logger(...)`.
213
214_Repetition_
215There is some repetitive code; specifically, `Logger.Overseer.queue("node")`. Do note that this actually results in a `Queue` object, so you could easily make this its own variable like this:
216```javascript
217let NodeQueue = Logger.Overseer.queue("node");
218NodeQueue.push(...).write();
219```
220
221_Queue pushing and writing_
222When you're pushing multiple objects to a queue, be wary that they will stay there until the queue is written. Because everything is its own class, what you're really pushing is a _reference_ to the real object, so you can make changes to a pushed object after it has been pushed. As such, it is entirely possible to prematurely write a queue before an already-pushed `Event` or `Statement` is finished. It's good practice to immediately write the queue immediately after it has been pushed. Perhaps in the future there will be some functionality where a `Statement` or `Event` can be directly written instead of placed in the queue.
223
224_Push order_
225The developer has control over **everything** here. The order of your log file is the order that you push to your `Event`s and `Queue`. If I were to push a new Statement to `LoadEvent` in the middle of the pushes to `LowerNestedEvent`, it would show up on the log first because `LowerNestedEvent` isn't itself pushed to `LoadEvent` until later in the code. The order of the log file is 100% logical and in the developer's control, so it would be wise to write your code neatly with this in mind. This logging package doesn't do anything you don't tell it to.
226
227## Log Format
228
229Logs are controlled by `Queue` objects. Placed in the folder they're told, the logs should follow the ISO datetime format and have a `.log` file extension. Here's the name of a sample log file: `2018-12-26T18-08-37-653Z.log`
230
231Regarding format, logs follow a JSON-like format, as follows:
232```json
233{"timestamp":"2018-12-26T18:08:37.654Z","verbosity":"INFO","tag":"PROCESS","message":"Process started"}
234[{"timestamp":"2018-12-26T18:08:37.655Z","verbosity":"INFO","tag":"INIT","message":"Acquiring needed top-level packages."},[{"timestamp":"2018-12-26T18:08:37.655Z","verbosity":"INFO","tag":"INIT","message":"Loading fs..."},{"timestamp":"2018-12-26T18:08:37.655Z","verbosity":"DEBUG","tag":"INIT","message":"fs loaded."},{"timestamp":"2018-12-26T18:08:37.655Z","verbosity":"INFO","tag":"INIT","message":"Loading http..."},{"timestamp":"2018-12-26T18:08:37.655Z","verbosity":"DEBUG","tag":"INIT","message":"http loaded."},{"timestamp":"2018-12-26T18:08:37.655Z","verbosity":"INFO","tag":"INIT","message":"Loading jsonwebtoken..."},{"timestamp":"2018-12-26T18:08:37.655Z","verbosity":"DEBUG","tag":"INIT","message":"jsonwebtoken loaded."},{"timestamp":"2018-12-26T18:08:37.655Z","verbosity":"INFO","tag":"INIT","message":"Loading lognestmonster..."},{"timestamp":"2018-12-26T18:08:37.655Z","verbosity":"DEBUG","tag":"INIT","message":"lognestmonster loaded."}],{"timestamp":"2018-12-26T18:08:37.655Z","verbosity":"INFO","tag":"INIT","message":"Finished."}]
235```
236
237Because of the way the Queue objects push it (the most efficient way regarding computing power with changing/appending to files), you will have to do a bit of tweaking to get it in proper JSON format:
238
2391. Add a comma after every newline
2402. Place a table open-bracket (`[`) at the beginning of the file
2413. Place a table close-bracket (`]`) at the end of the file
242
243This could be easily automated. Once finished, you get proper JSON (below), where each object `{}` is a Statement and each wrapping table `[]` is an Event (excluding the outermost one).
244
245```json
246[
247 {
248 "timestamp": "2018-12-26T05:55:23.360Z",
249 "verbosity": "INFO",
250 "tag": "PROCESS",
251 "message": "Process started"
252 },
253 [
254 {
255 "timestamp": "2018-12-26T05:55:23.360Z",
256 "verbosity": "INFO",
257 "tag": "INIT",
258 "message": "Acquiring needed top-level packages."
259 },
260 [
261 {
262 "timestamp": "2018-12-26T05:55:23.360Z",
263 "verbosity": "INFO",
264 "tag": "INIT",
265 "message": "Loading fs..."
266 },
267 {
268 "timestamp": "2018-12-26T05:55:23.360Z",
269 "verbosity": "DEBUG",
270 "tag": "INIT",
271 "message": "fs loaded."
272 },
273 {
274 "timestamp": "2018-12-26T05:55:23.360Z",
275 "verbosity": "INFO",
276 "tag": "INIT",
277 "message": "Loading http..."
278 },
279 {
280 "timestamp": "2018-12-26T05:55:23.360Z",
281 "verbosity": "DEBUG",
282 "tag": "INIT",
283 "message": "http loaded."
284 },
285 {
286 "timestamp": "2018-12-26T05:55:23.360Z",
287 "verbosity": "INFO",
288 "tag": "INIT",
289 "message": "Loading jsonwebtoken..."
290 },
291 {
292 "timestamp": "2018-12-26T05:55:23.360Z",
293 "verbosity": "DEBUG",
294 "tag": "INIT",
295 "message": "jsonwebtoken loaded."
296 },
297 {
298 "timestamp": "2018-12-26T05:55:23.360Z",
299 "verbosity": "INFO",
300 "tag": "INIT",
301 "message": "Loading lognestmonster..."
302 },
303 {
304 "timestamp": "2018-12-26T05:55:23.360Z",
305 "verbosity": "DEBUG",
306 "tag": "INIT",
307 "message": "lognestmonster loaded."
308 }
309 ],
310 {
311 "timestamp": "2018-12-26T05:55:23.360Z",
312 "verbosity": "INFO",
313 "tag": "INIT",
314 "message": "Finished."
315 }
316 ]
317]
318```
319
320The above can be taken in by parsing software and create something similar to the following:
321
322```
323[[LOG FILE NAME]]
324[[LOG DATE]]
325[[LOG FILE SIZE]]
326[[LOG ITEM COUNT]]
327
328TIMESTAMP - INFO - PROCESS - Process started
329v 3 ITEMS
330 TIMESTAMP - INFO - INIT - Acquiring needed top-level packages.
331 v 6 ITEMS
332 TIMESTAMP - INFO - INIT - Loading fs...
333 TIMESTAMP - DEBUG - INIT - fs loaded.
334 TIMESTAMP - INFO - INIT - Loading http...
335 TIMESTAMP - DEBUG - INIT - http loaded.
336 TIMESTAMP - INFO - INIT - Loading jsonwebtoken...
337 TIMESTAMP - DEBUG - INIT - jsonwebtoken loaded.
338 TIMESTAMP - INFO - INIT - Finished.
339```
340
341This is exciting! You still have the ability to narrow down your results with timestamps, verbosity levels, and tags/invokers, but now you have the organization with collapsable nests to not be overwhelmed with large amounts of data at the same time.
342
343## Contributors
344
345Developer - Joshua 'joshuas3' Stockin \<joshstockin@gmail.com\> (https://www.github.com/joshuas3)
346
347Package Name - Patrik 'Patrola' Xop (https://github.com/PatrikXop)
348
349### Issue Reporting/Contributing
350
351You can report issues using GitHub's built-in repository [issue tracking feature](https://www.github.com/joshuas3/log-nest-monster/issues).
352
353You can contribute officially by [forking the repository and creating a pull request](https://www.github.com/joshuas3/log-nest-monster/compare).
354
355### License
356
357This software is licensed under version 3 of the GNU General Public License.
358
359 lognestmonster (c) 2018 Joshua 'joshuas3' Stockin
360
361 This program is free software: you can redistribute it and/or modify
362 it under the terms of the GNU General Public License as published by
363 the Free Software Foundation, either version 3 of the License, or
364 (at your option) any later version.
365
366 This program is distributed in the hope that it will be useful,
367 but WITHOUT ANY WARRANTY; without even the implied warranty of
368 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
369 GNU General Public License for more details.
370
371 You should have received a copy of the GNU General Public License
372 along with this program. If not, see <https://www.gnu.org/licenses/>.
373
374The license can be found [here](LICENSE).
375