1 | <img src="/static/logo.png" height="200px"/> |
2 |
|
3 | _Multilevel logging for advanced programs._ |
4 |
|
5 | 1. [lognestmonster](#lognestmonster) |
6 | 2. [Library Class Structure](#library-class-structure) |
7 | 1. [Semantics](#semantics) |
8 | 3. [Serialization Format](#serialization-format) |
9 | 1. [Events](#events) |
10 | 2. [Statements](#statements) |
11 | 1. [Verbosity Level Enumeration](#verbosity-level-enumeration) |
12 | 3. [Example](#example) |
13 | 4. [Temporary Data Saving](#temporary-data-saving) |
14 | 5. [Copyright](#copyright) |
15 |
|
16 | # lognestmonster |
17 |
|
18 | ## Library Class Structure |
19 | This is subject to future change over security concerns regarding pointers and memory allocation. |
20 | ``` |
21 | class lognestmonster |
22 | |
23 | enum VerbosityLevels {INFO, DEBUG, VERBOSE, VERYVERBOSE, WARNING, ERROR} |
24 | |
25 | struct QueueConfig |
26 | char * out_dir // directory to output log files |
27 | |
28 | virtual void * alloc(size_t size) // implementation defaults to cstd malloc() |
29 | virtual void free(void * block) // implementation defaults to cstd free() |
30 | virtual void * serialize(LogObject * obj) // implementation defaults to manual serialization of standard LogObject to allocated block |
31 | virtual bool write(void * serialized, size_t size, std::ostream stream, bool append) // implementation defaults to writing entire serialized block to stream |
32 | virtual int delete(char * file_name) // implementation defaults to cstd remove() |
33 | |
34 | interface LogObject |
35 | Pushable * parent |
36 | char * temp_file |
37 | virtual bool save_temp() |
38 | virtual bool delete_temp() |
39 | |
40 | interface Pushable |
41 | protected: |
42 | std::vector<LogObject *> pushed |
43 | public: |
44 | push(LogObject * obj) |
45 | push(int verbosity, char * tag, char * message) // implicitly creates a Statement and then pushes |
46 | |
47 | class Queue : Pushable |
48 | public: |
49 | struct QueueConfig * _config |
50 | constructor (struct QueueConfig * config) |
51 | write() // serializes, writes, and clears pushed LogObjects |
52 | write(LogObject * obj) // implicit push(), then write() |
53 | write(int verbosity, char * tag, char * message) // implicit Statement creation, push(), then write() |
54 | |
55 | class Event : LogObject, Pushable |
56 | public: |
57 | constructor (LogObject * obj) // implicit push() |
58 | constructor (int verbosity, char * tag, char * message) // implicit Statement creation, then push() |
59 | |
60 | class Statement : LogObject |
61 | public: |
62 | int verbosity |
63 | int timestamp |
64 | std::string * tag |
65 | std::string * message |
66 | constructor (int verbosity, char * tag, char * message) |
67 | ``` |
68 | ### Semantics |
69 | A `Queue` handles data serialization and file writing to the main logtree file. Queue writing refers to sending serialized logtree data to the outstream. Queue pushing refers to adding an Event or Statement to the queue for future writing. |
70 |
|
71 | An `Event` is a pushable list of statements or events, or the "nest". Event pushing refers to adding an Event or Statement to the parent's list. |
72 |
|
73 | A `Statement` is the data-containing log item with a timestamp, verbosity level, tag/invoker, and message. |
74 |
|
75 | In reference to data serialization, `parser` as used here is just a deserializer. |
76 |
|
77 | ## Serialization Format |
78 |
|
79 | By default the library serializes log tree information in a special format. This can be overriden by anybody that uses the library. |
80 |
|
81 | ### Metadata |
82 |
|
83 | All saved files should begin with an `unsigned char` version number and an `unsigned long long` millisecond timestamp. |
84 | ``` |
85 | unsigned char version |
86 | unsigned long long timestamp |
87 | ``` |
88 |
|
89 | ### Events |
90 | Open event with `0x2` and close with `0x3`. Statements or more events can be written inbetween these tags. |
91 | ``` |
92 | 0x2 // open event |
93 | // more events or statements |
94 | 0x3 // close event |
95 | ``` |
96 |
|
97 | ### Statements |
98 | Open statement with `0x0` and close `0x1`. |
99 |
|
100 | 1. 1 byte for an open statement tag |
101 | 2. 1 byte for a predefined verbosity level enum |
102 | 3. 4 bytes for an unsigned integer timestamp |
103 | 4. 1 byte for the length of the tag string |
104 | 5. 0-255 bytes for the tag string |
105 | 6. 2 bytes for the length of the message string |
106 | 7. 0-65535 bytes for the message string |
107 | 8. 1 byte for a close statement tag |
108 |
|
109 | ``` |
110 | 0x0 |
111 | unsigned char verbosity |
112 | unsigned long long timestamp |
113 | unsigned char tag_size |
114 | unsigned char[] tag |
115 | unsigned short message_size |
116 | unsigned char[] message |
117 | 0x1 |
118 | ``` |
119 | A close statement tag is always needed in case the serializer method is overriden and provides extra data/metadata. If a close statement tag isn't written, a parser/deserializer won't be able to read a serialized logtree with extra data. |
120 |
|
121 | #### Verbosity Level Enumeration |
122 | The 6 verbosity level enums and their byte values are: |
123 | ``` |
124 | INFO = 0 |
125 | DEBUG = 1 |
126 | VERBOSE = 2 |
127 | VERYVERBOSE = 3 |
128 | WARNING = 4 |
129 | ERROR = 5 |
130 | ``` |
131 |
|
132 | ### Example |
133 | 1 statement inside one 1 event: |
134 | ``` |
135 | 1565561768719 // timestamp 8* |
136 | 0x2 // open event 1 |
137 | 0x0 // open statement 1 |
138 | 1565561768752 // timestamp 8* |
139 | 0 // verbosity 1 |
140 | 4 // tag_size 1 |
141 | "INIT" // tag 4* |
142 | 5 // message_size 2 |
143 | "HELLO" // message 5* |
144 | 0x1 // close statement 1 |
145 | 0x3 // close event 1 |
146 | // 33 total bytes for this log tree |
147 | ``` |
148 | With the sample log tree used here, the raw byte file totals 25 bytes. In use, a parser/deserializer could take this file and create output similar to the following: |
149 | ``` |
150 | lognestmonster - log_folder/ |
151 | Size: 33 bytes | Timestamp: 1565561768719 |
152 | 1 Statement | 1 Event | 0 Unsaved Data Trees |
153 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
154 | |
155 | v 1 ITEM |
156 | 1565561768752 - INFO - INIT - HELLO |
157 | |
158 | |
159 | |
160 | ::::::::::: press q to exit | arrow keys to move, expand, collapse ::::::::::: |
161 | ``` |
162 |
|
163 | ## Temporary Data Saving |
164 |
|
165 | By the nature of a push-write logging library, there's a chance that some created Statements and Events might not be pushed and written before the program's exit, whether it hangs, crashes, throws a runtime exception, is SIGKILLed, or anything else. Seeing as the point of logging is to find and diagnose errors with ease, it'd be frustrating to lose critical last-second information like this. The solution: save temporary serialized data for every creation or change to Statements or Events. Every logtree that ends in a Statement will have its own temporary data file; when a Statement is pushed to an Event, the Statement's file will be deleted and replaced into the greater Event file. See the following example for how data is separated into files: |
166 |
|
167 | ``` |
168 | Queue queue; |
169 | Event event; |
170 | Statement state1; |
171 | Statement state2; |
172 | |
173 | // Existing files: |
174 | // statement1.raw |
175 | // statement2.raw |
176 | |
177 | event.push(state1) |
178 | |
179 | // Existing files: |
180 | // event.raw |
181 | // statement2.raw |
182 | |
183 | queue.push(event) |
184 | |
185 | // Existing files: |
186 | // event.raw |
187 | // statement2.raw |
188 | |
189 | event.push(state2) |
190 | |
191 | // Existing files: |
192 | // event.raw (all log items now exist inside the event, in the queue) |
193 | |
194 | queue.write() |
195 | |
196 | // Existing files: |
197 | // log12345.raw (consists of 2 statements inside 1 event) |
198 | ``` |
199 |
|
200 | In reality, file names will likely contain timestamps, hashes, UUIDs, or some other form of identifiable metadata. |
201 |
|
202 | ## Copyright |
203 |
|
204 | lognestmonster Copyright (c) 2019 Joshua 'joshuas3' Stockin under the [GNU General Public License v3](LICENSE). |
205 |
|
206 | The following should be present in each file. |
207 | ``` |
208 | lognestmonster Copyright (c) 2019 Joshua 'joshuas3' Stockin |
209 | <https://github.com/JoshuaS3/lognestmonster/>. |
210 | |
211 | |
212 | This file is part of lognestmonster. |
213 | |
214 | lognestmonster is free software: you can redistribute it and/or modify |
215 | it under the terms of the GNU General Public License as published by |
216 | the Free Software Foundation, either version 3 of the License, or |
217 | (at your option) any later version. |
218 | |
219 | lognestmonster is distributed in the hope that it will be useful, |
220 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
221 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
222 | GNU General Public License for more details. |
223 | |
224 | You should have received a copy of the GNU General Public License |
225 | along with lognestmonster. If not, see <https://www.gnu.org/licenses/>. |
226 | ``` |
227 |
|