Repository: slog-rs/slog (language: rust)

Issue:
Support for the {:#} formatter
Right now various macros in slog [support](https://github.com/slog-rs/slog/blob/1af5352de1277800cc48d194c6936144e21f034f/src/lib.rs#L398-L415) the `{}`, `{:?}` and `{:#?}` formatters. However, there are types which also have different format behavior with the `{:#}` formatter, for example, [`anyhow::Error`](https://docs.rs/anyhow/1.0.25/anyhow/struct.Error.html#display-representations):
> When you print an error object using "{}" or to_string(), only the outermost underlying error or context is printed, not any of the lower level causes. This is exactly as if you had called the Display impl of the error from which you constructed your anyhow::Error.
> ```
> Failed to read instrs from ./path/to/instrs.json
> ```
> To print causes as well using anyhow's default formatting of causes, use the alternate selector "{:#}".
> ```
> Failed to read instrs from ./path/to/instrs.json: No such file or directory (os error 2)
> ```

Specifically with `anyhow::Error`, it would be really awesome to have support for the `{:#}` formatter in slog, because it would make printing errors as fields much more readable.


Discussion / hints:
I guess adding `#%` couldn't hurt if we already have `#?`. Please fill a PR! :)

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5bfc3f7f6..45fcece73 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](http://keepachangelog.com/)
 and this project adheres to [Semantic Versioning](http://semver.org/).
 
+## 2.7.0 - ????-??-??
+
+* Add #% for alternate display of the value part
+
 ## 2.6.0 - 2019-10-28
 
 * Add #? for pretty-debug printing the value part
diff --git a/src/lib.rs b/src/lib.rs
index 5e486451c..9135b895d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -401,6 +401,12 @@ macro_rules! kv(
     (@ $args_ready:expr; $k:expr => %$v:expr, $($args:tt)* ) => {
         kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{}", $v))), $args_ready); $($args)* )
     };
+    (@ $args_ready:expr; $k:expr => #%$v:expr) => {
+        kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:#}", $v))), $args_ready); )
+    };
+    (@ $args_ready:expr; $k:expr => #%$v:expr, $($args:tt)* ) => {
+        kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:#}", $v))), $args_ready); $($args)* )
+    };
     (@ $args_ready:expr; $k:expr => ?$v:expr) => {
         kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:?}", $v))), $args_ready); )
     };
@@ -445,17 +451,23 @@ macro_rules! slog_kv(
     (@ $args_ready:expr; $k:expr => %$v:expr, $($args:tt)* ) => {
         slog_kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{}", $v))), $args_ready); $($args)* )
     };
+    (@ $args_ready:expr; $k:expr => #%$v:expr) => {
+        slog_kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:#}", $v))), $args_ready); )
+    };
+    (@ $args_ready:expr; $k:expr => #%$v:expr, $($args:tt)* ) => {
+        slog_kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:#}", $v))), $args_ready); $($args)* )
+    };
     (@ $args_ready:expr; $k:expr => ?$v:expr) => {
-        kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:?}", $v))), $args_ready); )
+        slog_kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:?}", $v))), $args_ready); )
     };
     (@ $args_ready:expr; $k:expr => ?$v:expr, $($args:tt)* ) => {
-        kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:?}", $v))), $args_ready); $($args)* )
+        slog_kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:?}", $v))), $args_ready); $($args)* )
     };
     (@ $args_ready:expr; $k:expr => #?$v:expr) => {
-        kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:#?}", $v))), $args_ready); )
+        slog_kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:#?}", $v))), $args_ready); )
     };
     (@ $args_ready:expr; $k:expr => #?$v:expr, $($args:tt)* ) => {
-        kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:#?}", $v))), $args_ready); $($args)* )
+        slog_kv!(@ ($crate::SingleKV::from(($k, __slog_builtin!(@format_args "{:#?}", $v))), $args_ready); $($args)* )
     };
     (@ $args_ready:expr; $k:expr => $v:expr) => {
         slog_kv!(@ ($crate::SingleKV::from(($k, $v)), $args_ready); )
@@ -712,6 +724,8 @@ macro_rules! slog_record(
 /// `%` in `k => v` expression to use it's text representation returned by
 /// `format_args!("{}", v)`. This is especially useful for errors. Not that
 /// this does not allocate any `String` since it operates on `fmt::Arguments`.
+/// You can also use the `#%` prefix to use the "alternate" form of formatting,
+/// represented by the `{:#}` formatting specifier.
 ///
 /// Similarly to use `std::fmt::Debug` value can be prefixed with `?`,
 /// or pretty-printed with `#?`.
@@ -2752,7 +2766,7 @@ pub trait SerdeValue: erased_serde::Serialize + Value {
 /// Types that implement this type implement custom serialization in the
 /// structured part of the log macros. Without an implementation of `Value` for
 /// your type you must emit using either the `?` "debug", `#?` "pretty-debug",
-/// `%` "display" or [`SerdeValue`](trait.SerdeValue.html)
+/// `%` "display", `#%` "alternate display" or [`SerdeValue`](trait.SerdeValue.html)
 /// (if you have the `nested-values` feature enabled) formatters.
 ///
 /// # Example


Test patch:
diff --git a/src/tests.rs b/src/tests.rs
index a4e5f4aef..0d96c99df 100644
--- a/src/tests.rs
+++ b/src/tests.rs
@@ -1,5 +1,4 @@
-use {Discard, Logger};
-use Never;
+use {Discard, Logger, Never, KV, Drain, OwnedKVList, Record, AsFmtSerializer};
 
 // Separate module to test lack of imports
 mod no_imports {
@@ -206,6 +205,60 @@ fn expressions_fmt() {
     info!(log, "message"; "f" => %f, "d" => ?d);
 }
 
+#[test]
+fn display_and_alternate_display() {
+    use core::fmt;
+    use core::cell::Cell;
+
+    struct Example;
+
+    impl fmt::Display for Example {
+        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+            if f.alternate() {
+                f.write_str("alternate")
+            } else {
+                f.write_str("normal")
+            }
+        }
+    }
+
+    #[derive(Clone, Default)]
+    struct CheckExample;
+
+    impl Drain for CheckExample {
+        type Ok = ();
+        type Err = Never;
+
+        fn log(&self, record: &Record, values: &OwnedKVList) -> Result<(), Never> {
+            let mut checked_n = false;
+            let mut checked_a = false;
+            let mut serializer = AsFmtSerializer(|key, fmt_args| {
+                if key == "n" {
+                    assert_eq!(format!("{}", fmt_args), "normal");
+                    checked_n = true;
+                } else if key == "a" {
+                    assert_eq!(format!("{}", fmt_args), "alternate");
+                    checked_a = true;
+                } else {
+                    panic!("Unexpected key: {}", key);
+                }
+                Ok(())
+            });
+
+            record.kv.serialize(record, &mut serializer).unwrap();
+
+            assert!(checked_n, "Expected the normal formatter to be used");
+            assert!(checked_a, "Expected the alternate formatter to be used");
+
+            Ok(())
+        }
+    }
+
+    let log = Logger::root(CheckExample, o!());
+
+    info!(log, ""; "n" => %Example, "a" => #%Example);
+}
+
 #[test]
 fn makers() {
     use ::*;