From dbf3e40cd60a7c9de25e378df061e79d265ae267 Mon Sep 17 00:00:00 2001 From: Sorah Fukumori Date: Wed, 29 Nov 2017 21:58:33 +0900 Subject: [PATCH] Add Exception#display to log formatted exception https://bugs.ruby-lang.org/issues/14141 --- NEWS | 4 +++ error.c | 36 +++++++++++++++++++++ eval_error.c | 102 ++++++++++++++++++++++++++++++++++++----------------------- 3 files changed, 102 insertions(+), 40 deletions(-) diff --git a/NEWS b/NEWS index d72f05e..a4c2d52 100644 --- a/NEWS +++ b/NEWS @@ -31,6 +31,10 @@ with all sufficient information, see the ChangeLog file or Redmine * Now deprecated [Feature #3072] +* Exception + + * Exception#display [Feature #14141] + * Dir * Dir.glob provides new optional keyword argument, :base. diff --git a/error.c b/error.c index f009519..9b800ef 100644 --- a/error.c +++ b/error.c @@ -922,6 +922,40 @@ exc_to_s(VALUE exc) /* * call-seq: + * exception.display(port=$stderr) -> nil + * + * Prints formatted exception on the given port (default to $stderr). + * Format includes backtrace, exception class and exception message. + * + * This method is useful to log caught exceptions. + * + * begin + * some_work + * rescue => e + * e.display + * retry + * end + */ + +static VALUE +exc_display(int argc, VALUE *argv, VALUE exc) +{ + VALUE out; + + if (argc == 0) { + out = rb_stderr; + } + else { + rb_scan_args(argc, argv, "01", &out); + } + + rb_ec_error_write(exc, Qundef, out); + + return Qnil; +} + +/* + * call-seq: * exception.message -> string * * Returns the result of invoking exception.to_s. @@ -2182,6 +2216,8 @@ Init_Exception(void) rb_define_method(rb_eException, "initialize", exc_initialize, -1); rb_define_method(rb_eException, "==", exc_equal, 1); rb_define_method(rb_eException, "to_s", exc_to_s, 0); + rb_define_method(rb_eException, "display", exc_display, -1); + rb_define_method(rb_eException, "message", exc_message, 0); rb_define_method(rb_eException, "inspect", exc_inspect, 0); rb_define_method(rb_eException, "backtrace", exc_backtrace, 0); diff --git a/eval_error.c b/eval_error.c index 4c79bf0..7c25cd0 100644 --- a/eval_error.c +++ b/eval_error.c @@ -4,15 +4,28 @@ */ #ifdef HAVE_BUILTIN___BUILTIN_CONSTANT_P +#define warn_print_io(io, x) RB_GNUC_EXTENSION_BLOCK( \ + NIL_P(io) ? \ + warn_print(x) : ( \ + (__builtin_constant_p(x)) ? \ + rb_io_write((io), rb_str_new((x), (long)strlen(x))) : \ + rb_io_write((io), rb_str_new2(x)) \ + ) \ + ) #define warn_print(x) RB_GNUC_EXTENSION_BLOCK( \ (__builtin_constant_p(x)) ? \ rb_write_error2((x), (long)strlen(x)) : \ rb_write_error(x) \ ) #else +#define warn_print_io(io, x) NIL_P(io) ? rb_write_error((x)) : rb_io_write((io), rb_str_new2(x)) #define warn_print(x) rb_write_error(x) #endif + +#define warn_print2_io(io,x,l) NIL_P(io) ? warn_print2(x,l) : rb_io_write((io), rb_str_new((x),(l))) #define warn_print2(x,l) rb_write_error2((x),(l)) + +#define warn_print_str_io(io,x) NIL_P(io) ? rb_write_error_str(x) : rb_io_write((io), (x)) #define warn_print_str(x) rb_write_error_str(x) static VALUE error_pos_str(void); @@ -73,7 +86,7 @@ error_print(rb_execution_context_t *ec) } static void -print_errinfo(const VALUE eclass, const VALUE errat, const VALUE emesg, int colored) +print_errinfo(const VALUE eclass, const VALUE errat, const VALUE emesg, const VALUE io, int colored) { static const char underline[] = "\033[4;1m"; static const char bold[] = "\033[1m"; @@ -88,11 +101,11 @@ print_errinfo(const VALUE eclass, const VALUE errat, const VALUE emesg, int colo error_pos(); } else { - warn_print_str(mesg); - warn_print(": "); + warn_print_str_io(io, mesg); + warn_print_io(io, ": "); } - if (colored) warn_print(bold); + if (colored) warn_print_io(io, bold); if (!NIL_P(emesg)) { einfo = RSTRING_PTR(emesg); @@ -101,17 +114,17 @@ print_errinfo(const VALUE eclass, const VALUE errat, const VALUE emesg, int colo } if (eclass == rb_eRuntimeError && elen == 0) { - if (colored) warn_print(underline); - warn_print("unhandled exception\n"); + if (colored) warn_print_io(io, underline); + warn_print_io(io, "unhandled exception\n"); } else { VALUE epath; epath = rb_class_name(eclass); if (elen == 0) { - if (colored) warn_print(underline); - warn_print_str(epath); - warn_print("\n"); + if (colored) warn_print_io(io, underline); + warn_print_str_io(io, epath); + warn_print_io(io, "\n"); } else { const char *tail = 0; @@ -123,26 +136,26 @@ print_errinfo(const VALUE eclass, const VALUE errat, const VALUE emesg, int colo len = tail - einfo; tail++; /* skip newline */ } - warn_print_str(tail ? rb_str_subseq(emesg, 0, len) : emesg); + warn_print_str_io(io, tail ? rb_str_subseq(emesg, 0, len) : emesg); if (epath) { - warn_print(" ("); - if (colored) warn_print(underline); - warn_print_str(epath); - if (colored) warn_print(reset); - if (colored) warn_print(bold); - warn_print(")\n"); + warn_print_io(io, " ("); + if (colored) warn_print_io(io, underline); + warn_print_str_io(io, epath); + if (colored) warn_print_io(io, reset); + if (colored) warn_print_io(io, bold); + warn_print_io(io, ")\n"); } if (tail) { - warn_print_str(rb_str_subseq(emesg, tail - einfo, elen - len - 1)); + warn_print_str_io(io, rb_str_subseq(emesg, tail - einfo, elen - len - 1)); } - if (tail ? einfo[elen-1] != '\n' : !epath) warn_print2("\n", 1); + if (tail ? einfo[elen-1] != '\n' : !epath) warn_print2_io(io, "\n", 1); } } - if (colored) warn_print(reset); + if (colored) warn_print_io(io, reset); } static void -print_backtrace(const VALUE eclass, const VALUE errat, int reverse) +print_backtrace(const VALUE eclass, const VALUE errat, const VALUE io, int reverse) { if (!NIL_P(errat)) { long i; @@ -163,10 +176,10 @@ print_backtrace(const VALUE eclass, const VALUE errat, int reverse) if (RB_TYPE_P(line, T_STRING)) { VALUE str = rb_str_new_cstr("\t"); if (reverse) rb_str_catf(str, "%*ld: ", width, len - i); - warn_print_str(rb_str_catf(str, "from %"PRIsVALUE"\n", line)); + warn_print_str_io(io, rb_str_catf(str, "from %"PRIsVALUE"\n", line)); } if (skip && i == TRACE_HEAD && len > TRACE_MAX) { - warn_print_str(rb_sprintf("\t ... %ld levels...\n", + warn_print_str_io(io, rb_sprintf("\t ... %ld levels...\n", len - TRACE_HEAD - TRACE_TAIL)); i = len - TRACE_TAIL; } @@ -175,26 +188,16 @@ print_backtrace(const VALUE eclass, const VALUE errat, int reverse) } void -rb_ec_error_print(rb_execution_context_t * volatile ec, volatile VALUE errinfo) +rb_ec_error_write(volatile VALUE errinfo, volatile VALUE errat, volatile VALUE io) { - volatile VALUE errat = Qundef; - volatile int raised_flag = ec->raised_flag; volatile VALUE eclass = Qundef, emesg = Qundef; if (NIL_P(errinfo)) return; - rb_ec_raised_clear(ec); - EC_PUSH_TAG(ec); - if (EC_EXEC_TAG() == TAG_NONE) { - errat = rb_get_backtrace(errinfo); - } - else if (errat == Qundef) { + if (errat == Qundef) { errat = Qnil; } - else if (eclass == Qundef || emesg != Qundef) { - goto error; - } if ((eclass = CLASS_OF(errinfo)) != Qundef) { VALUE e = rb_check_funcall(errinfo, rb_intern("message"), 0, 0); if (e != Qundef) { @@ -203,15 +206,34 @@ rb_ec_error_print(rb_execution_context_t * volatile ec, volatile VALUE errinfo) } } if (rb_stderr_tty_p()) { - warn_print("\033[1mTraceback \033[m(most recent call last):\n"); - print_backtrace(eclass, errat, TRUE); - print_errinfo(eclass, errat, emesg, TRUE); + warn_print_io(io, "\033[1mTraceback \033[m(most recent call last):\n"); + print_backtrace(eclass, errat, io, TRUE); + print_errinfo(eclass, errat, emesg, io, TRUE); } else { - print_errinfo(eclass, errat, emesg, FALSE); - print_backtrace(eclass, errat, FALSE); + print_errinfo(eclass, errat, emesg, io, FALSE); + print_backtrace(eclass, errat, io, FALSE); } - error: +} + +void +rb_ec_error_print(rb_execution_context_t * volatile ec, volatile VALUE errinfo) +{ + volatile int raised_flag = ec->raised_flag; + volatile VALUE errat; + + if (NIL_P(errinfo)) + return; + rb_ec_raised_clear(ec); + + EC_PUSH_TAG(ec); + if (EC_EXEC_TAG() == TAG_NONE) { + errat = rb_get_backtrace(errinfo); + } + + rb_ec_error_write(errinfo, errat, Qnil); + +error: EC_POP_TAG(); ec->errinfo = errinfo; rb_ec_raised_set(ec, raised_flag); -- 2.10.2