Skip to content

Commit 311ae4c

Browse files
committed
New logger - mini log4ssjs
1 parent db0320f commit 311ae4c

File tree

1 file changed

+316
-0
lines changed

1 file changed

+316
-0
lines changed

core/lib_logger.js

Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
<script runat="server" language="javascript">
2+
3+
// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
4+
//
5+
// USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
6+
// NOT CONTROL.
7+
//
8+
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
9+
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation
10+
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11+
// and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall be included in all copies or substantial portions
14+
// of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
17+
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18+
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
19+
// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20+
// DEALINGS IN THE SOFTWARE.
21+
22+
/**
23+
* Copyright: {@link https://www.email360.io/|email360}
24+
* <br>Author: {@link https://www.linkedin.com/in/sascha-huwald/|Sascha Huwald}
25+
* <br>Since: 2022
26+
* <br>Version: 1.0.0
27+
* <br>License: MIT
28+
*
29+
* This is a mini log4ssjs version based on log4j
30+
*
31+
* A log request of level p in a logger with level q is enabled if p >= q. This rule is
32+
* at the heart of log4j. It assumes that levels are ordered. For the standard levels,
33+
* we have ALL < DEBUG < INFO < WARN < ERROR < FATAL < OFF.
34+
*
35+
* The Following example shows how we can filter all our DEBUG and INFO messages. This
36+
* program uses of logger method level(X) to set a desired logging level.
37+
*
38+
* This example would print all the messages except DEBUG and INFO:
39+
*
40+
* @example
41+
* var log = new logger('myScript');
42+
* log.level = 'WARN'
43+
*
44+
* log.trace("Trace Message!");
45+
* log.debug("Debug Message!");
46+
* log.info("Info Message!");
47+
* log.warn("Warn Message!");
48+
* log.error("Error Message!");
49+
* log.fatal("Fatal Message!");
50+
*
51+
* @description
52+
* Output from the above example
53+
* Warn Message!
54+
* Error Message!
55+
* Fatal Message!
56+
*
57+
* This is an example configuration.
58+
* @example
59+
* log.configure = {
60+
* appenders: [
61+
* {
62+
* type: "DataExtension",
63+
* level: "DEBUG",
64+
* call: {
65+
* name: 'My DataExtension'
66+
* }
67+
* }, {
68+
* type: "HTTP",
69+
* level: "ERROR",
70+
* call: {
71+
* url: "http://www.email360.io",
72+
* method: "POST",
73+
* header: "Bearer " + token,
74+
* contentType: "application/json",
75+
* payload: {
76+
* jwt: "myToken",
77+
* extra: "Payload"
78+
* }
79+
* }
80+
* }, {
81+
* type: "Console",
82+
* level: "TRACE",
83+
* call: {
84+
* target: ['console','text']
85+
* }
86+
* }
87+
* ]};
88+
*
89+
* @param {string} name - The name of the script / category used to identify the log
90+
*
91+
*/
92+
function logger(name) {
93+
if (!name) {
94+
throw "[logger] No Logger category provided.";
95+
}
96+
97+
this.category = name;
98+
this.configure = {};
99+
this.level = 'OFF';
100+
// Log4j hirachy
101+
this.levels = {
102+
ALL: 0,
103+
TRACE: 10,
104+
DEBUG: 20,
105+
INFO: 30,
106+
WARN: 40,
107+
ERROR: 50,
108+
FATAL: 60,
109+
OFF: 100
110+
};
111+
this.allowedAppenders = ['DataExtension','Console','HTTP'];
112+
this.logId = null;
113+
114+
this.trace = function(message) { this._appenders(message,'trace'); }
115+
this.debug = function(message) { this._appenders(message,'debug'); }
116+
this.info = function(message) { this._appenders(message,'info'); }
117+
this.warn = function(message) { this._appenders(message,'warn'); }
118+
this.error = function(message) { this._appenders(message,'error'); }
119+
this.fatal = function(message) { this._appenders(message,'fatal'); }
120+
121+
/**
122+
* Wrapper to decide if the message should be handled by the appender
123+
*
124+
* @param {string} message - The message itself
125+
* @param {string} messageLevel - The message level such as WARN,INFO,ERROR
126+
*/
127+
this._appenders = function(message,messageLevel) {
128+
if (isObject(this.configure) && this.configure.hasOwnProperty('appenders')) {
129+
var appenders = (!Array.isArray(this.configure.appenders)) ? [this.configure.appenders] : this.configure.appenders;
130+
131+
for (var i = 0; i < appenders.length; i++) {
132+
var appender = appenders[i];
133+
134+
// check if user given level exists otherwise keep original level
135+
if (appender.hasOwnProperty('level') && this.levels.hasOwnProperty(appender.level.toUpperCase())) {
136+
this.level = appender.level.toUpperCase();
137+
}
138+
this._log(message,messageLevel,appender);
139+
}
140+
141+
} else {
142+
this._log(message,messageLevel);
143+
}
144+
}
145+
146+
/**
147+
* The actual log function to log the message based on the given
148+
* appender.
149+
*
150+
* @param {string} message - The message to be log
151+
* @param {string} messageLevel - The message level such as WARN, INFO, ERROR
152+
* @param {object} appender - Appender object
153+
*
154+
* @param {string} appender.type - The appender type name
155+
* @param {object} [appender.call] - Call information for each appender
156+
*/
157+
this._log = function(message,messageLevel,appender) {
158+
var appender = (this.allowedAppenders.includes(appender.type)) ? appender : {type: 'Console'},
159+
logLevelNumber = this.levels[this.level],
160+
messageLevel = messageLevel.toUpperCase();
161+
messageLevelNumber = this.levels[messageLevel];
162+
163+
// message level must be greater or equal than log level but smaller than off
164+
if (messageLevelNumber >= logLevelNumber && messageLevelNumber < this.levels['OFF']) {
165+
166+
this.logId = Platform.Function.GUID();
167+
168+
switch (appender.type) {
169+
case 'DataExtension':
170+
this._appenderDataExtension(messageLevel,message,appender.call);
171+
break;
172+
case 'HTTP':
173+
this._appenderHTTP(messageLevel,message,appender.call);
174+
break;
175+
case 'Console':
176+
this._appenderConsole(messageLevel,message,appender.call);
177+
break;
178+
}
179+
}
180+
}
181+
182+
/**
183+
* Get the log time in a readable format
184+
*
185+
* @param {boolean} inMilliseconds - The given time will include milliseconds
186+
* @returns {string}
187+
*/
188+
this._getLogTime = function(inMilliseconds) {
189+
var dd = Platform.Function.SystemDateToLocalDate(new Date(getUnixTimestamp(inMilliseconds))),
190+
pad = function(number,dec) { var dec = dec || 2; return String(number).padStart(dec,'0') };
191+
192+
return dd.getFullYear() +
193+
'-' + pad(dd.getMonth() + 1) +
194+
'-' + pad(dd.getDate()) +
195+
' ' + pad(dd.getHours()) +
196+
':' + pad(dd.getMinutes()) +
197+
':' + pad(dd.getSeconds()) +
198+
'.' + pad(dd.getMilliseconds(),3);
199+
}
200+
201+
/**
202+
* DataExtension appender
203+
*
204+
* This appender will store the message in the system DataExtension
205+
*
206+
* @param {string} level - The message level such as WARN, INFO, ERROR
207+
* @param {string} message - The message to log
208+
* @param {object} appender - Appender object
209+
*
210+
* @param {array} [appender.name] - The name of the DataExtension to log
211+
*/
212+
this._appenderDataExtension = function(level,message,appender) {
213+
var settings = new settings(),
214+
de = appender.name || settings.de.log.Name,
215+
name = [],
216+
value = [];
217+
218+
var logTime = this._getLogTime(true),
219+
logId = this.logId,
220+
logCategory = this.category;
221+
222+
var log = {
223+
timestamp: logTime,
224+
id: logId,
225+
level: level,
226+
message: message,
227+
category: logCategory
228+
};
229+
230+
// prep the data for insert
231+
for( var i in log ) {
232+
name.push(i);
233+
value.push(log[i]);
234+
}
235+
236+
// write error log to DE
237+
var r = Platform.Function.InsertDE(de,name,value);
238+
}
239+
240+
/**
241+
* Console appender
242+
*
243+
* This appender will display the message in the browser console
244+
*
245+
* @param {string} level - The message level such as WARN, INFO, ERROR
246+
* @param {string} message - The message to log
247+
* @param {object} appender - Appender object
248+
*
249+
* @param {array} [appender.target] - An array to define the output of the console
250+
* Allowed values are 'console', 'text' and / or 'html'
251+
*/
252+
this._appenderConsole = function(level,message,appender) {
253+
var message = (typeof message === 'string') ? message.replace(/<script[\s\S]*?>/gi, '').replace(/<\/script>/gi, '') : message,
254+
target = (Array.isArray(appender.target)) ? appender.target : ['console'];
255+
256+
var log = '['+this._getLogTime(true)+'] ['+level+'] [' + this.category + '] ' + message;
257+
258+
if(target.includes('console')) {
259+
console_log(log);
260+
}
261+
if(target.includes('html')) {
262+
log = (typeof log == 'string') ? log.replace('\n', '<br/>').replace('\t', '&nbsp;&nbsp;&nbsp;&nbsp') : Platform.Function.Stringify(log);
263+
Platform.Response.Write('<pre style="margin:0.85em 0px;"><span style="font-size: 11px;">'+log+'</span></pre>');
264+
}
265+
if(target.includes('text')) {
266+
Platform.Response.Write('{'+Platform.Function.Stringify(log)+'}\n');
267+
}
268+
}
269+
270+
/**
271+
* Console HTTP
272+
*
273+
* This appender will send the message via a httprequest to the appender.url
274+
*
275+
* @param {string} level - The message level such as WARN, INFO, ERROR
276+
* @param {string} message - The message to log
277+
* @param {object} appender - Appender object
278+
*
279+
* @param {string} appender.url - The target URL for the HTTPRequest
280+
* @param {string} [appender.methdod=POST] - The HTTP method
281+
* @param {string} [appender.header] - The HTTP header
282+
* @param {string} [appender.contentType=application/json] - The HTTP contentType.
283+
* @param {object} [appender.paylaod] - Additional payload for the HTTP request
284+
*/
285+
this._appenderHTTP = function(level,message,appender) {
286+
if (!appender.url) {
287+
throw "[logger] No Appender URL found for appender HTTP";
288+
}
289+
290+
// log details
291+
var logTime = this._getLogTime(true),
292+
logId = this.logId,
293+
logCategory = this.category;
294+
295+
// HTTP parameter
296+
var header = appender.header || null,
297+
contentType = appender.contentType || "application/json",
298+
method = appender.method || 'POST',
299+
payload = {
300+
Timestamp: logTime,
301+
Id: logId,
302+
Level: level,
303+
Message: message,
304+
Category: logCategory
305+
};
306+
307+
payload = mergeObject(payload,appender.payload || {});
308+
309+
var resp = httpRequest(method,appender.url,contentType,payload,header);
310+
if (resp.status != '200') {
311+
throw "[logger] Failed to log to [HTTP] on [" + appender.url + "]: "+ Stringify(resp.content);
312+
}
313+
}
314+
}
315+
316+
</script>

0 commit comments

Comments
 (0)