5 #include "duk_internal.h"
7 /* 3-letter log level strings */
8 DUK_LOCAL
const duk_uint8_t duk__log_level_strings
[] = {
9 (duk_uint8_t
) DUK_ASC_UC_T
, (duk_uint8_t
) DUK_ASC_UC_R
, (duk_uint8_t
) DUK_ASC_UC_C
,
10 (duk_uint8_t
) DUK_ASC_UC_D
, (duk_uint8_t
) DUK_ASC_UC_B
, (duk_uint8_t
) DUK_ASC_UC_G
,
11 (duk_uint8_t
) DUK_ASC_UC_I
, (duk_uint8_t
) DUK_ASC_UC_N
, (duk_uint8_t
) DUK_ASC_UC_F
,
12 (duk_uint8_t
) DUK_ASC_UC_W
, (duk_uint8_t
) DUK_ASC_UC_R
, (duk_uint8_t
) DUK_ASC_UC_N
,
13 (duk_uint8_t
) DUK_ASC_UC_E
, (duk_uint8_t
) DUK_ASC_UC_R
, (duk_uint8_t
) DUK_ASC_UC_R
,
14 (duk_uint8_t
) DUK_ASC_UC_F
, (duk_uint8_t
) DUK_ASC_UC_T
, (duk_uint8_t
) DUK_ASC_UC_L
18 DUK_INTERNAL duk_ret_t
duk_bi_logger_constructor(duk_context
*ctx
) {
19 duk_hthread
*thr
= (duk_hthread
*) ctx
;
22 /* Calling as a non-constructor is not meaningful. */
23 if (!duk_is_constructor_call(ctx
)) {
24 return DUK_RET_TYPE_ERROR
;
27 nargs
= duk_get_top(ctx
);
35 /* Automatic defaulting of logger name from caller. This would
36 * work poorly with tail calls, but constructor calls are currently
37 * never tail calls, so tail calls are not an issue now.
40 if (thr
->callstack_top
>= 2) {
41 duk_activation
*act_caller
= thr
->callstack
+ thr
->callstack_top
- 2;
42 duk_hobject
*func_caller
;
44 func_caller
= DUK_ACT_GET_FUNC(act_caller
);
46 /* Stripping the filename might be a good idea
47 * ("/foo/bar/quux.js" -> logger name "quux"),
48 * but now used verbatim.
50 duk_push_hobject(ctx
, func_caller
);
51 duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_FILE_NAME
);
56 /* the stack is unbalanced here on purpose; we only rely on the
57 * initial two values: [ name this ].
60 if (duk_is_string(ctx
, 0)) {
62 duk_put_prop_stridx(ctx
, 1, DUK_STRIDX_LC_N
);
64 /* don't set 'n' at all, inherited value is used as name */
69 return 0; /* keep default instance */
72 /* Default function to format objects. Tries to use toLogString() but falls
73 * back to toString(). Any errors are propagated out without catching.
75 DUK_INTERNAL duk_ret_t
duk_bi_logger_prototype_fmt(duk_context
*ctx
) {
76 if (duk_get_prop_stridx(ctx
, 0, DUK_STRIDX_TO_LOG_STRING
)) {
77 /* [ arg toLogString ] */
80 duk_call_method(ctx
, 0);
86 /* [ arg undefined ] */
88 duk_to_string(ctx
, 0);
92 /* Default function to write a formatted log line. Writes to stderr,
93 * appending a newline to the log line.
95 * The argument is a buffer whose visible size contains the log message.
96 * This function should avoid coercing the buffer to a string to avoid
97 * string table traffic.
99 DUK_INTERNAL duk_ret_t
duk_bi_logger_prototype_raw(duk_context
*ctx
) {
107 #ifdef DUK_USE_FILE_IO
108 data
= (const char *) duk_require_buffer(ctx
, 0, &data_len
);
109 DUK_FWRITE((const void *) data
, 1, data_len
, DUK_STDERR
);
110 DUK_FPUTC((int) '\n', DUK_STDERR
);
111 DUK_FFLUSH(DUK_STDERR
);
118 /* Log frontend shared helper, magic value indicates log level. Provides
119 * frontend functions: trace(), debug(), info(), warn(), error(), fatal().
120 * This needs to have small footprint, reasonable performance, minimal
123 DUK_INTERNAL duk_ret_t
duk_bi_logger_prototype_log_shared(duk_context
*ctx
) {
124 duk_hthread
*thr
= (duk_hthread
*) ctx
;
126 duk_small_int_t entry_lev
= duk_get_current_magic(ctx
);
127 duk_small_int_t logger_lev
;
131 const duk_uint8_t
*arg_str
;
133 duk_uint8_t
*buf
, *p
;
134 const duk_uint8_t
*q
;
135 duk_uint8_t date_buf
[DUK_BI_DATE_ISO8601_BUFSIZE
];
139 DUK_ASSERT(entry_lev
>= 0 && entry_lev
<= 5);
142 /* XXX: sanitize to printable (and maybe ASCII) */
143 /* XXX: better multiline */
146 * Logger arguments are:
148 * magic: log level (0-5)
150 * stack: plain log args
152 * We want to minimize memory churn so a two-pass approach
153 * is used: first pass formats arguments and computes final
154 * string length, second pass copies strings either into a
155 * pre-allocated and reused buffer (short messages) or into a
156 * newly allocated fixed buffer. If the backend function plays
157 * nice, it won't coerce the buffer to a string (and thus
161 nargs
= duk_get_top(ctx
);
163 /* [ arg1 ... argN this ] */
171 duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_LC_L
);
172 logger_lev
= (duk_small_int_t
) duk_get_int(ctx
, -1);
173 if (entry_lev
< logger_lev
) {
176 /* log level could be popped but that's not necessary */
178 now
= DUK_USE_DATE_GET_NOW(ctx
);
179 duk_bi_date_format_timeval(now
, date_buf
);
180 date_len
= DUK_STRLEN((const char *) date_buf
);
182 duk_get_prop_stridx(ctx
, -2, DUK_STRIDX_LC_N
);
183 duk_to_string(ctx
, -1);
184 DUK_ASSERT(duk_is_string(ctx
, -1));
186 /* [ arg1 ... argN this loggerLevel loggerName ] */
192 /* Line format: <time> <entryLev> <loggerName>: <msg> */
195 tot_len
+= 3 + /* separators: space, space, colon */
196 3 + /* level string */
197 date_len
+ /* time */
198 duk_get_length(ctx
, -1); /* loggerName */
200 for (i
= 0; i
< nargs
; i
++) {
201 /* When formatting an argument to a string, errors may happen from multiple
202 * causes. In general we want to catch obvious errors like a toLogString()
203 * throwing an error, but we don't currently try to catch every possible
204 * error. In particular, internal errors (like out of memory or stack) are
205 * not caught. Also, we expect Error toString() to not throw an error.
207 if (duk_is_object(ctx
, i
)) {
208 /* duk_pcall_prop() may itself throw an error, but we're content
209 * in catching the obvious errors (like toLogString() throwing an
212 duk_push_hstring_stridx(ctx
, DUK_STRIDX_FMT
);
214 /* [ arg1 ... argN this loggerLevel loggerName 'fmt' arg ] */
215 /* call: this.fmt(arg) */
216 rc
= duk_pcall_prop(ctx
, -5 /*obj_index*/, 1 /*nargs*/);
218 /* Keep the error as the result (coercing it might fail below,
219 * but we don't catch that now).
225 (void) duk_to_lstring(ctx
, i
, &arg_len
);
226 tot_len
++; /* sep (even before first one) */
234 /* XXX: There used to be a shared log buffer here, but it was removed
235 * when dynamic buffer spare was removed. The problem with using
236 * bufwriter is that, without the spare, the buffer gets passed on
237 * as an argument to the raw() call so it'd need to be resized
238 * (reallocated) anyway. If raw() call convention is changed, this
239 * could be made more efficient.
242 buf
= (duk_uint8_t
*) duk_push_fixed_buffer(ctx
, tot_len
);
243 DUK_ASSERT(buf
!= NULL
);
246 DUK_MEMCPY((void *) p
, (void *) date_buf
, date_len
);
248 *p
++ = (duk_uint8_t
) DUK_ASC_SPACE
;
250 q
= duk__log_level_strings
+ (entry_lev
* 3);
251 DUK_MEMCPY((void *) p
, (void *) q
, (duk_size_t
) 3);
254 *p
++ = (duk_uint8_t
) DUK_ASC_SPACE
;
256 arg_str
= (const duk_uint8_t
*) duk_get_lstring(ctx
, -2, &arg_len
);
257 DUK_MEMCPY((void *) p
, (const void *) arg_str
, arg_len
);
260 *p
++ = (duk_uint8_t
) DUK_ASC_COLON
;
262 for (i
= 0; i
< nargs
; i
++) {
263 *p
++ = (duk_uint8_t
) DUK_ASC_SPACE
;
265 arg_str
= (const duk_uint8_t
*) duk_get_lstring(ctx
, i
, &arg_len
);
266 DUK_ASSERT(arg_str
!= NULL
);
267 DUK_MEMCPY((void *) p
, (const void *) arg_str
, arg_len
);
270 DUK_ASSERT(buf
+ tot_len
== p
);
272 /* [ arg1 ... argN this loggerLevel loggerName buffer ] */
274 #if defined(DUK_USE_DEBUGGER_SUPPORT) && defined(DUK_USE_DEBUGGER_FWD_LOGGING)
275 /* Do debugger forwarding before raw() because the raw() function
276 * doesn't get the log level right now.
278 if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr
->heap
)) {
281 log_buf
= (const char *) duk_get_buffer(ctx
, -1, &sz_buf
);
282 DUK_ASSERT(log_buf
!= NULL
);
283 duk_debug_write_notify(thr
, DUK_DBG_CMD_LOG
);
284 duk_debug_write_int(thr
, (duk_int32_t
) entry_lev
);
285 duk_debug_write_string(thr
, (const char *) log_buf
, sz_buf
);
286 duk_debug_write_eom(thr
);
290 /* Call this.raw(msg); look up through the instance allows user to override
291 * the raw() function in the instance or in the prototype for maximum
294 duk_push_hstring_stridx(ctx
, DUK_STRIDX_RAW
);
296 /* [ arg1 ... argN this loggerLevel loggerName buffer 'raw' buffer ] */
297 duk_call_prop(ctx
, -6, 1); /* this.raw(buffer) */