Skip to content

Commit 0626a52

Browse files
committed
Forward JS logs to Rust logs
1 parent a2f2083 commit 0626a52

File tree

8 files changed

+244
-15
lines changed

8 files changed

+244
-15
lines changed

.travis.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ env:
1010
- RUST_BACKTRACE=1
1111
matrix:
1212
- FEATURES="--no-default-features"
13-
- FEATURES="--features debug" FEATURE_DOC=true
13+
- FEATURES="" FEATURE_DOC=true
14+
- FEATURES="--features debug"
15+
- FEATURES="--features logging"
1416
- FEATURES="--features trace"
1517
- FEATURES="--features spam"
1618

Cargo.toml

+6
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@ version = "0.1.0"
1616
path = "duktape-sys"
1717
version = "*"
1818

19+
[dependencies.log]
20+
optional = true
21+
version = "*"
22+
1923
[features]
24+
default = ["debug", "logging"]
25+
logging = ["log"]
2026
debug = ["duktape-sys/debug"]
2127
spam = ["duktape-sys/spam"]
2228
trace = ["duktape-sys/trace"]

duktape-sys/Cargo.toml

-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ version = "*"
2424
bindgen = "0.18.0"
2525

2626
[features]
27-
default = ["debug"]
2827
debug = ["log"]
2928
trace = ["log"]
3029
spam = ["log"]

duktape-sys/examples/gen-wrapper.rs

+7
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,13 @@ const MACRO_CONSTANTS: &'static [(&'static str, &'static str)] = &[
233233
("long", "DUK_LEVEL_DEBUG"),
234234
("long", "DUK_LEVEL_DDEBUG"),
235235
("long", "DUK_LEVEL_DDDEBUG"),
236+
237+
("duk_int_t", "DUK_LOG_TRACE"),
238+
("duk_int_t", "DUK_LOG_DEBUG"),
239+
("duk_int_t", "DUK_LOG_INFO"),
240+
("duk_int_t", "DUK_LOG_WARN"),
241+
("duk_int_t", "DUK_LOG_ERROR"),
242+
("duk_int_t", "DUK_LOG_FATAL"),
236243
];
237244

238245
const MACRO_FUNCTIONS: &'static [(&'static str, &'static str, &'static [(&'static str, &'static str)])] = &[

duktape-sys/src/ffi.rs

+6
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,12 @@ extern "C" {
324324
pub static DUK_LEVEL_DEBUG: ::std::os::raw::c_long;
325325
pub static DUK_LEVEL_DDEBUG: ::std::os::raw::c_long;
326326
pub static DUK_LEVEL_DDDEBUG: ::std::os::raw::c_long;
327+
pub static DUK_LOG_TRACE: duk_int_t;
328+
pub static DUK_LOG_DEBUG: duk_int_t;
329+
pub static DUK_LOG_INFO: duk_int_t;
330+
pub static DUK_LOG_WARN: duk_int_t;
331+
pub static DUK_LOG_ERROR: duk_int_t;
332+
pub static DUK_LOG_FATAL: duk_int_t;
327333
}
328334
extern "C" {
329335
pub fn duk_create_heap(alloc_func: duk_alloc_function,

duktape-sys/src/wrapper.c

+36
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,42 @@ const long DUK_LEVEL_DDDEBUG =
510510
#pragma pop_macro("DUK_LEVEL_DDDEBUG")
511511
DUK_LEVEL_DDDEBUG;
512512

513+
#pragma push_macro("DUK_LOG_TRACE")
514+
#undef DUK_LOG_TRACE
515+
const duk_int_t DUK_LOG_TRACE =
516+
#pragma pop_macro("DUK_LOG_TRACE")
517+
DUK_LOG_TRACE;
518+
519+
#pragma push_macro("DUK_LOG_DEBUG")
520+
#undef DUK_LOG_DEBUG
521+
const duk_int_t DUK_LOG_DEBUG =
522+
#pragma pop_macro("DUK_LOG_DEBUG")
523+
DUK_LOG_DEBUG;
524+
525+
#pragma push_macro("DUK_LOG_INFO")
526+
#undef DUK_LOG_INFO
527+
const duk_int_t DUK_LOG_INFO =
528+
#pragma pop_macro("DUK_LOG_INFO")
529+
DUK_LOG_INFO;
530+
531+
#pragma push_macro("DUK_LOG_WARN")
532+
#undef DUK_LOG_WARN
533+
const duk_int_t DUK_LOG_WARN =
534+
#pragma pop_macro("DUK_LOG_WARN")
535+
DUK_LOG_WARN;
536+
537+
#pragma push_macro("DUK_LOG_ERROR")
538+
#undef DUK_LOG_ERROR
539+
const duk_int_t DUK_LOG_ERROR =
540+
#pragma pop_macro("DUK_LOG_ERROR")
541+
DUK_LOG_ERROR;
542+
543+
#pragma push_macro("DUK_LOG_FATAL")
544+
#undef DUK_LOG_FATAL
545+
const duk_int_t DUK_LOG_FATAL =
546+
#pragma pop_macro("DUK_LOG_FATAL")
547+
DUK_LOG_FATAL;
548+
513549
#pragma push_macro("duk_create_heap_default")
514550
#undef duk_create_heap_default
515551
duk_context * duk_create_heap_default() {

duktape-sys/src/wrapper.h

+30
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,36 @@ const long DUK_LEVEL_DDEBUG;
427427
const long DUK_LEVEL_DDDEBUG;
428428
#pragma pop_macro("DUK_LEVEL_DDDEBUG")
429429

430+
#pragma push_macro("DUK_LOG_TRACE")
431+
#undef DUK_LOG_TRACE
432+
const duk_int_t DUK_LOG_TRACE;
433+
#pragma pop_macro("DUK_LOG_TRACE")
434+
435+
#pragma push_macro("DUK_LOG_DEBUG")
436+
#undef DUK_LOG_DEBUG
437+
const duk_int_t DUK_LOG_DEBUG;
438+
#pragma pop_macro("DUK_LOG_DEBUG")
439+
440+
#pragma push_macro("DUK_LOG_INFO")
441+
#undef DUK_LOG_INFO
442+
const duk_int_t DUK_LOG_INFO;
443+
#pragma pop_macro("DUK_LOG_INFO")
444+
445+
#pragma push_macro("DUK_LOG_WARN")
446+
#undef DUK_LOG_WARN
447+
const duk_int_t DUK_LOG_WARN;
448+
#pragma pop_macro("DUK_LOG_WARN")
449+
450+
#pragma push_macro("DUK_LOG_ERROR")
451+
#undef DUK_LOG_ERROR
452+
const duk_int_t DUK_LOG_ERROR;
453+
#pragma pop_macro("DUK_LOG_ERROR")
454+
455+
#pragma push_macro("DUK_LOG_FATAL")
456+
#undef DUK_LOG_FATAL
457+
const duk_int_t DUK_LOG_FATAL;
458+
#pragma pop_macro("DUK_LOG_FATAL")
459+
430460
#pragma push_macro("duk_create_heap_default")
431461
#undef duk_create_heap_default
432462
duk_context * duk_create_heap_default();

src/lib.rs

+156-13
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
//! [1]: http://duktape.org/
1414
1515
extern crate duktape_sys;
16+
#[cfg(feature = "logging")]
17+
#[macro_use]
18+
extern crate log;
1619

1720
use std::collections;
1821
use std::error;
@@ -126,7 +129,7 @@ impl Context {
126129
};
127130

128131
unsafe {
129-
duktape_sys::duk_logging_init(raw, 0);
132+
Context::setup_logging(raw);
130133
}
131134

132135
Context {
@@ -135,6 +138,50 @@ impl Context {
135138
}
136139
}
137140

141+
#[cfg(feature = "logging")]
142+
unsafe fn setup_logging(ctx: *mut duktape_sys::duk_context) {
143+
use duktape_sys::*;
144+
duk_logging_init(ctx, 0);
145+
146+
duk_push_global_object(ctx);
147+
duk_get_prop_string(ctx, -1, nul_str(b"Duktape\0"));
148+
duk_get_prop_string(ctx, -1, nul_str(b"Logger\0"));
149+
duk_get_prop_string(ctx, -1, nul_str(b"prototype\0"));
150+
// Stack: [ global .Duktape .Logger .prototype ]
151+
152+
duk_push_c_function(ctx, Some(log_handler), DUK_VARARGS);
153+
duk_set_magic(ctx, -1, DUK_LOG_TRACE);
154+
duk_put_prop_string(ctx, -2, nul_str(b"trace\0"));
155+
156+
duk_push_c_function(ctx, Some(log_handler), DUK_VARARGS);
157+
duk_set_magic(ctx, -1, DUK_LOG_DEBUG);
158+
duk_put_prop_string(ctx, -2, nul_str(b"debug\0"));
159+
160+
duk_push_c_function(ctx, Some(log_handler), DUK_VARARGS);
161+
duk_set_magic(ctx, -1, DUK_LOG_INFO);
162+
duk_put_prop_string(ctx, -2, nul_str(b"info\0"));
163+
164+
duk_push_c_function(ctx, Some(log_handler), DUK_VARARGS);
165+
duk_set_magic(ctx, -1, DUK_LOG_WARN);
166+
duk_put_prop_string(ctx, -2, nul_str(b"warn\0"));
167+
168+
duk_push_c_function(ctx, Some(log_handler), DUK_VARARGS);
169+
duk_set_magic(ctx, -1, DUK_LOG_ERROR);
170+
duk_put_prop_string(ctx, -2, nul_str(b"error\0"));
171+
172+
duk_push_c_function(ctx, Some(log_handler), DUK_VARARGS);
173+
duk_set_magic(ctx, -1, DUK_LOG_FATAL);
174+
duk_put_prop_string(ctx, -2, nul_str(b"fatal\0"));
175+
176+
// Stack: [ global .Duktape .Logger .prototype ]
177+
duk_pop_n(ctx, 4);
178+
}
179+
180+
#[cfg(not(feature = "logging"))]
181+
unsafe fn setup_logging(_: *mut duktape_sys::duk_context) {
182+
// No-op
183+
}
184+
138185
/// Evaluates the specified script string within the current
139186
/// context.
140187
///
@@ -539,11 +586,11 @@ impl Error {
539586
Some(n)
540587
});
541588
let line_number = get_number_property(ctx, index, "lineNumber")
542-
.and_then(|n| if n.is_nan() {
543-
None
544-
} else {
545-
Some(n as usize)
546-
});
589+
.and_then(|n| if n.is_nan() {
590+
None
591+
} else {
592+
Some(n as usize)
593+
});
547594
let stack = get_string_property(ctx, index, "stack");
548595

549596
Error::Js {
@@ -634,6 +681,102 @@ unsafe fn get_number_property(ctx: *mut duktape_sys::duk_context,
634681
}
635682
}
636683

684+
unsafe fn nul_str(data: &[u8]) -> *const os::raw::c_char {
685+
ffi::CStr::from_bytes_with_nul_unchecked(data).as_ptr()
686+
}
687+
688+
#[cfg(feature = "logging")]
689+
unsafe extern "C" fn log_handler(ctx: *mut duktape_sys::duk_context) -> duktape_sys::duk_ret_t {
690+
use duktape_sys::*; // Because this function is essentially only calling C stuff
691+
692+
// The function magic is the log level that this handler should handle.
693+
let level = duk_get_current_magic(ctx);
694+
if level < DUK_LOG_TRACE || level > DUK_LOG_FATAL {
695+
return 0;
696+
}
697+
698+
// Stack: [ arg0 ... argN ]
699+
let nargs = duk_get_top(ctx);
700+
701+
duk_push_this(ctx);
702+
// Stack: [ arg0 ... argN this ]
703+
704+
duk_get_prop_string(ctx, -1, nul_str(b"l\0"));
705+
// Stack: [ arg0 ... argN this loggerLevel ]
706+
707+
// Check if we should log this level with this logger
708+
let logger_level = duk_get_int(ctx, -1);
709+
if level < logger_level {
710+
return 0;
711+
}
712+
713+
let rust_level = if logger_level == DUK_LOG_TRACE {
714+
log::LogLevel::Trace
715+
} else if logger_level == DUK_LOG_DEBUG {
716+
log::LogLevel::Debug
717+
} else if logger_level == DUK_LOG_INFO {
718+
log::LogLevel::Info
719+
} else if logger_level == DUK_LOG_WARN {
720+
log::LogLevel::Warn
721+
} else {
722+
log::LogLevel::Error
723+
};
724+
725+
duk_get_prop_string(ctx,
726+
-2,
727+
nul_str(b"n\0"));
728+
// Stack: [ arg0 ... argN this loggerLevel loggerName ]
729+
duk_to_string(ctx, -1);
730+
731+
let mut total_len = 0;
732+
733+
// Replace all args with equivalent strings, and compute their lengths
734+
// Stack: [ arg0 ... argN this loggerLevel loggerName ]
735+
for i in 0..nargs {
736+
if 1 == duk_is_object(ctx, i) {
737+
duk_push_string(ctx,
738+
ffi::CStr::from_bytes_with_nul_unchecked(b"fmt\0").as_ptr());
739+
duk_dup(ctx, i);
740+
// Stack: [ arg1 ... argN this loggerLevel loggerName 'fmt' arg ]
741+
// Call: this.fmt(arg) so -5 is this
742+
duk_pcall_prop(ctx, -5, 1);
743+
duk_replace(ctx, i);
744+
}
745+
746+
let mut arg_len = mem::uninitialized();
747+
748+
duk_to_lstring(ctx, i, &mut arg_len);
749+
750+
total_len += arg_len as usize;
751+
}
752+
753+
// Stack: [ arg0String ... argNString this loggerLevel loggerName ]
754+
755+
let mut name_len = mem::uninitialized();
756+
let name_data = duk_get_lstring(ctx, -1, &mut name_len);
757+
let name_slice = slice::from_raw_parts(name_data as *const u8, name_len);
758+
let name_str = str::from_utf8(name_slice).unwrap();
759+
760+
// Allocate message space; include nargs to allocate spaces
761+
let mut msg = String::with_capacity(total_len + name_str.len() + nargs as usize + 1);
762+
msg.push_str(name_str);
763+
msg.push(':');
764+
765+
for i in 0..nargs {
766+
let mut arg_len = mem::uninitialized();
767+
let arg_data = duk_get_lstring(ctx, i, &mut arg_len);
768+
let slice = slice::from_raw_parts(arg_data as *const u8, arg_len);
769+
let arg_str = str::from_utf8(slice).unwrap();
770+
771+
msg.push(' ');
772+
msg.push_str(arg_str);
773+
}
774+
775+
log!(target: &format!("{}:{}", module_path!(), name_str), rust_level, "{}", msg);
776+
777+
0
778+
}
779+
637780
unsafe extern "C" fn fatal_handler(_: *mut os::raw::c_void, msg_raw: *const os::raw::c_char) {
638781
let msg = &*ffi::CStr::from_ptr(msg_raw).to_string_lossy();
639782
// TODO: No unwind support from C... but this "works" right now
@@ -879,7 +1022,7 @@ mod tests {
8791022
function foo() {
8801023
return 'a';
8811024
}")
882-
.unwrap();
1025+
.unwrap();
8831026
let global = ctx.global_object();
8841027
ctx.assert_clean();
8851028
let foo = global.get("foo").unwrap();
@@ -900,7 +1043,7 @@ mod tests {
9001043
}
9011044
return Array.prototype.slice.call(arguments);
9021045
}")
903-
.unwrap();
1046+
.unwrap();
9041047
let global = ctx.global_object();
9051048
ctx.assert_clean();
9061049
let value = global.call_method("foo", &[&Value::Number(4.25)]).unwrap().to_value();
@@ -919,7 +1062,7 @@ mod tests {
9191062
}
9201063
return Array.prototype.slice.call(arguments);
9211064
}")
922-
.unwrap();
1065+
.unwrap();
9231066
let global = ctx.global_object();
9241067
ctx.assert_clean();
9251068
let foo = global.get("foo").unwrap();
@@ -939,7 +1082,7 @@ mod tests {
9391082
}
9401083
return Array.prototype.slice.call(arguments);
9411084
}")
942-
.unwrap();
1085+
.unwrap();
9431086
let global = ctx.global_object();
9441087
ctx.assert_clean();
9451088
let foo = global.get("foo").unwrap();
@@ -956,7 +1099,7 @@ mod tests {
9561099
function foo() {
9571100
return 'a';
9581101
}")
959-
.unwrap();
1102+
.unwrap();
9601103
let value = ctx.call_global("foo", &[]).unwrap().to_value();
9611104
assert_eq!(Value::String("a".to_owned()), value);
9621105
ctx.assert_clean();
@@ -969,7 +1112,7 @@ mod tests {
9691112
function foo() {
9701113
return Array.prototype.slice.call(arguments);
9711114
}")
972-
.unwrap();
1115+
.unwrap();
9731116

9741117
let mut obj = collections::BTreeMap::new();
9751118
obj.insert("a".to_owned(), Value::String("a".to_owned()));
@@ -1001,7 +1144,7 @@ mod tests {
10011144
function foo() {
10021145
throw 'a';
10031146
}")
1004-
.unwrap();
1147+
.unwrap();
10051148
let mut value = ctx.call_global("foo", &[]);
10061149
clean_error(&mut value);
10071150
assert_eq!(Err(Error::Js {

0 commit comments

Comments
 (0)