Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement indentation #80

Closed
wants to merge 0 commits into from
Closed

implement indentation #80

wants to merge 0 commits into from

Conversation

oli-obk
Copy link

@oli-obk oli-obk commented Apr 16, 2016

fixes #79

This is a feature I'll definitely use in serde-xml, and probably implement it for json, too. Basically anything that processes tree-structures benefits enormously from it.

example run with miri:

Interpreting: factorial_recursive
 TRACE:miri::interpreter: // bb0
 TRACE:miri::interpreter: return = factorial_recursive::fact(const 10i64) -> bb1
  TRACE:miri::interpreter: // bb0
  TRACE:miri::interpreter: var0 = arg0
  TRACE:miri::interpreter: tmp1 = var0
  TRACE:miri::interpreter: tmp0 = Eq(tmp1, const 0i64)
  TRACE:miri::interpreter: if(tmp0) -> [true: bb2, false: bb3]
  TRACE:miri::interpreter: // bb3
  TRACE:miri::interpreter: tmp2 = var0
  TRACE:miri::interpreter: tmp5 = var0
  TRACE:miri::interpreter: tmp4 = Sub(tmp5, const 1i64)
  TRACE:miri::interpreter: tmp3 = factorial_recursive::fact(tmp4) -> bb4
   TRACE:miri::interpreter: // bb0
   TRACE:miri::interpreter: var0 = arg0
   TRACE:miri::interpreter: tmp1 = var0
   TRACE:miri::interpreter: tmp0 = Eq(tmp1, const 0i64)
   TRACE:miri::interpreter: if(tmp0) -> [true: bb2, false: bb3]
   TRACE:miri::interpreter: // bb3
   TRACE:miri::interpreter: tmp2 = var0
   TRACE:miri::interpreter: tmp5 = var0
   TRACE:miri::interpreter: tmp4 = Sub(tmp5, const 1i64)
   TRACE:miri::interpreter: tmp3 = factorial_recursive::fact(tmp4) -> bb4
    TRACE:miri::interpreter: // bb0
    TRACE:miri::interpreter: var0 = arg0
    TRACE:miri::interpreter: tmp1 = var0
    TRACE:miri::interpreter: tmp0 = Eq(tmp1, const 0i64)
    TRACE:miri::interpreter: if(tmp0) -> [true: bb2, false: bb3]
    TRACE:miri::interpreter: // bb3
    TRACE:miri::interpreter: tmp2 = var0
    TRACE:miri::interpreter: tmp5 = var0
    TRACE:miri::interpreter: tmp4 = Sub(tmp5, const 1i64)
    TRACE:miri::interpreter: tmp3 = factorial_recursive::fact(tmp4) -> bb4
    |TRACE:miri::interpreter: // bb0
    |TRACE:miri::interpreter: var0 = arg0
    |TRACE:miri::interpreter: tmp1 = var0
    |TRACE:miri::interpreter: tmp0 = Eq(tmp1, const 0i64)
    |TRACE:miri::interpreter: if(tmp0) -> [true: bb2, false: bb3]
    |TRACE:miri::interpreter: // bb3
    |TRACE:miri::interpreter: tmp2 = var0
    |TRACE:miri::interpreter: tmp5 = var0
    |TRACE:miri::interpreter: tmp4 = Sub(tmp5, const 1i64)
    |TRACE:miri::interpreter: tmp3 = factorial_recursive::fact(tmp4) -> bb4
    | TRACE:miri::interpreter: // bb0
    | TRACE:miri::interpreter: var0 = arg0
    | TRACE:miri::interpreter: tmp1 = var0
    | TRACE:miri::interpreter: tmp0 = Eq(tmp1, const 0i64)
    | TRACE:miri::interpreter: if(tmp0) -> [true: bb2, false: bb3]
    | TRACE:miri::interpreter: // bb3
    | TRACE:miri::interpreter: tmp2 = var0
    | TRACE:miri::interpreter: tmp5 = var0
    | TRACE:miri::interpreter: tmp4 = Sub(tmp5, const 1i64)
    | TRACE:miri::interpreter: tmp3 = factorial_recursive::fact(tmp4) -> bb4
    |  TRACE:miri::interpreter: // bb0
    |  TRACE:miri::interpreter: var0 = arg0
    |  TRACE:miri::interpreter: tmp1 = var0
    |  TRACE:miri::interpreter: tmp0 = Eq(tmp1, const 0i64)
    |  TRACE:miri::interpreter: if(tmp0) -> [true: bb2, false: bb3]
    |  TRACE:miri::interpreter: // bb3
    |  TRACE:miri::interpreter: tmp2 = var0
    |  TRACE:miri::interpreter: tmp5 = var0
    |  TRACE:miri::interpreter: tmp4 = Sub(tmp5, const 1i64)
    |  TRACE:miri::interpreter: tmp3 = factorial_recursive::fact(tmp4) -> bb4
    |   TRACE:miri::interpreter: // bb0
    |   TRACE:miri::interpreter: var0 = arg0
    |   TRACE:miri::interpreter: tmp1 = var0
    |   TRACE:miri::interpreter: tmp0 = Eq(tmp1, const 0i64)
    |   TRACE:miri::interpreter: if(tmp0) -> [true: bb2, false: bb3]
    |   TRACE:miri::interpreter: // bb3
    |   TRACE:miri::interpreter: tmp2 = var0
    |   TRACE:miri::interpreter: tmp5 = var0
    |   TRACE:miri::interpreter: tmp4 = Sub(tmp5, const 1i64)
    |   TRACE:miri::interpreter: tmp3 = factorial_recursive::fact(tmp4) -> bb4
    |    TRACE:miri::interpreter: // bb0
    |    TRACE:miri::interpreter: var0 = arg0
    |    TRACE:miri::interpreter: tmp1 = var0
    |    TRACE:miri::interpreter: tmp0 = Eq(tmp1, const 0i64)
    |    TRACE:miri::interpreter: if(tmp0) -> [true: bb2, false: bb3]
    |    TRACE:miri::interpreter: // bb3
    |    TRACE:miri::interpreter: tmp2 = var0
    |    TRACE:miri::interpreter: tmp5 = var0
    |    TRACE:miri::interpreter: tmp4 = Sub(tmp5, const 1i64)
    |    TRACE:miri::interpreter: tmp3 = factorial_recursive::fact(tmp4) -> bb4
    |    |TRACE:miri::interpreter: // bb0
    |    |TRACE:miri::interpreter: var0 = arg0
    |    |TRACE:miri::interpreter: tmp1 = var0
    |    |TRACE:miri::interpreter: tmp0 = Eq(tmp1, const 0i64)
    |    |TRACE:miri::interpreter: if(tmp0) -> [true: bb2, false: bb3]
    |    |TRACE:miri::interpreter: // bb3
    |    |TRACE:miri::interpreter: tmp2 = var0
    |    |TRACE:miri::interpreter: tmp5 = var0
    |    |TRACE:miri::interpreter: tmp4 = Sub(tmp5, const 1i64)
    |    |TRACE:miri::interpreter: tmp3 = factorial_recursive::fact(tmp4) -> bb4
    |    | TRACE:miri::interpreter: // bb0
    |    | TRACE:miri::interpreter: var0 = arg0
    |    | TRACE:miri::interpreter: tmp1 = var0
    |    | TRACE:miri::interpreter: tmp0 = Eq(tmp1, const 0i64)
    |    | TRACE:miri::interpreter: if(tmp0) -> [true: bb2, false: bb3]
    |    | TRACE:miri::interpreter: // bb3
    |    | TRACE:miri::interpreter: tmp2 = var0
    |    | TRACE:miri::interpreter: tmp5 = var0
    |    | TRACE:miri::interpreter: tmp4 = Sub(tmp5, const 1i64)
    |    | TRACE:miri::interpreter: tmp3 = factorial_recursive::fact(tmp4) -> bb4
    |    |  TRACE:miri::interpreter: // bb0
    |    |  TRACE:miri::interpreter: var0 = arg0
    |    |  TRACE:miri::interpreter: tmp1 = var0
    |    |  TRACE:miri::interpreter: tmp0 = Eq(tmp1, const 0i64)
    |    |  TRACE:miri::interpreter: if(tmp0) -> [true: bb2, false: bb3]
    |    |  TRACE:miri::interpreter: // bb2
    |    |  TRACE:miri::interpreter: return = const 1i64
    |    |  TRACE:miri::interpreter: goto -> bb1
    |    |  TRACE:miri::interpreter: // bb1
    |    |  TRACE:miri::interpreter: return
    |    | TRACE:miri::interpreter: // bb4
    |    | TRACE:miri::interpreter: return = Mul(tmp2, tmp3)
    |    | TRACE:miri::interpreter: goto -> bb1
    |    | TRACE:miri::interpreter: // bb1
    |    | TRACE:miri::interpreter: return
    |    |TRACE:miri::interpreter: // bb4
    |    |TRACE:miri::interpreter: return = Mul(tmp2, tmp3)
    |    |TRACE:miri::interpreter: goto -> bb1
    |    |TRACE:miri::interpreter: // bb1
    |    |TRACE:miri::interpreter: return
    |    TRACE:miri::interpreter: // bb4
    |    TRACE:miri::interpreter: return = Mul(tmp2, tmp3)
    |    TRACE:miri::interpreter: goto -> bb1
    |    TRACE:miri::interpreter: // bb1
    |    TRACE:miri::interpreter: return
    |   TRACE:miri::interpreter: // bb4
    |   TRACE:miri::interpreter: return = Mul(tmp2, tmp3)
    |   TRACE:miri::interpreter: goto -> bb1
    |   TRACE:miri::interpreter: // bb1
    |   TRACE:miri::interpreter: return
    |  TRACE:miri::interpreter: // bb4
    |  TRACE:miri::interpreter: return = Mul(tmp2, tmp3)
    |  TRACE:miri::interpreter: goto -> bb1
    |  TRACE:miri::interpreter: // bb1
    |  TRACE:miri::interpreter: return
    | TRACE:miri::interpreter: // bb4
    | TRACE:miri::interpreter: return = Mul(tmp2, tmp3)
    | TRACE:miri::interpreter: goto -> bb1
    | TRACE:miri::interpreter: // bb1
    | TRACE:miri::interpreter: return
    |TRACE:miri::interpreter: // bb4
    |TRACE:miri::interpreter: return = Mul(tmp2, tmp3)
    |TRACE:miri::interpreter: goto -> bb1
    |TRACE:miri::interpreter: // bb1
    |TRACE:miri::interpreter: return
    TRACE:miri::interpreter: // bb4
    TRACE:miri::interpreter: return = Mul(tmp2, tmp3)
    TRACE:miri::interpreter: goto -> bb1
    TRACE:miri::interpreter: // bb1
    TRACE:miri::interpreter: return
   TRACE:miri::interpreter: // bb4
   TRACE:miri::interpreter: return = Mul(tmp2, tmp3)
   TRACE:miri::interpreter: goto -> bb1
   TRACE:miri::interpreter: // bb1
   TRACE:miri::interpreter: return
  TRACE:miri::interpreter: // bb4
  TRACE:miri::interpreter: return = Mul(tmp2, tmp3)
  TRACE:miri::interpreter: goto -> bb1
  TRACE:miri::interpreter: // bb1
  TRACE:miri::interpreter: return
 TRACE:miri::interpreter: // bb1
 TRACE:miri::interpreter: return

@sfackler
Copy link
Member

Are there any existing logging frameworks that support a feature like this?

This seems to me like something that individual loggers (e.g. env-logger) would deal with themselves rather than giving log itself knowledge of indentation.

@oli-obk
Copy link
Author

oli-obk commented Apr 16, 2016

That would be possible, but then you need to have some kind of handle to the logger, which you can't really get without putting an arc into the logger

@sfackler
Copy link
Member

static INDENT_LEVEL: AtomicUsize = ATOMIC_USIZE_INIT;

pub fn change_indent(l: usize) {
    INDENT_LEVEL.store(l, Ordering::SeqCst);
}

impl Log for IndentingLogger {
    fn log(&self, record: &LogRecord) {
        let level = INDENT_LEVEL.load(Ordering::SeqCst);
        // ...
    }
}

@oli-obk
Copy link
Author

oli-obk commented Apr 18, 2016

yea that would work, but then libraries need to know about the logger that's going to be used.

libraries set the indentation level, executables use it to print indented (or not if they don't care)

@sfackler
Copy link
Member

My concern is that control over indentation is a very niche use case. Again, I am not aware of any logging framework that supports something like this.

In addition, having a single indentation level for the entire process seems like it'd have some serious issues with logging coming from multiple threads as they're all trying to first adjust the indentation and then log.

@oli-obk
Copy link
Author

oli-obk commented Apr 18, 2016

Ok, the thread thing is a serious issue. But we can fix that with a tls. Just because no logging framework has done it doesn't mean it's niche/rare. I've seen a few handwritten implementations. Whenever you have some sort of recursion or hierarchy, regular logging will become confusing. I have wished for indentation or something for a while when debugging rustc

@xitep
Copy link

xitep commented Apr 18, 2016

this looks nice! though i see the usage limited to debugging/understanding a single threaded application with a console/file based logger. isn't the more general idea of what you really want sort of this?

@oli-obk
Copy link
Author

oli-obk commented Apr 19, 2016

@xitep yea, except that I don't want the logging statement to add the indentation, but the logger, so that even logging statements that don't know about indentation are indented. I doesn't matter to me whether it's numbers or indentation, that's a decision of the logger.

@sfackler
Copy link
Member

I don't doubt that this is a thing you would find useful, but I also don't want this library to become the union of all features that anyone has ever found useful.

I would also note that rustc does not use this library, but rather its own version.

@oli-obk
Copy link
Author

oli-obk commented Apr 22, 2016

I would also note that rustc does not use this library, but rather its own version.

I know, but that is another issue.

I have an idea: A fn setting(name: &'static str, value: Box<Any+Send+'static>) (plus a macro that allows any library user to simply send a name + value pair to the logger). The logger can then choose to accept the setting depending on the name and whether the value is downcastable to the expected type).

This basically allows any kind of user extension, without cluttering the crate with additional features.

@oli-obk
Copy link
Author

oli-obk commented Apr 29, 2016

I updated this PR to a general "settings" version.

@oli-obk
Copy link
Author

oli-obk commented May 19, 2016

ping @sfackler

I also don't want this library to become the union of all features that anyone has ever found useful.

I can totally see that. The indentation part can be removed entirely, since it can be done in user code with the other changes.

The main question is whether settings of any kind should be allowed, and whether the design I proposed is feasible.

@sfackler
Copy link
Member

I'm a bit concerned about identifying settings by string names - it seems like there's a high chance of naming conflicts between settings used by various logging backends, and of typos on the user side when interacting with them. Something type-based might be better? It might be too annoying to need to have to pull in some crate with a single struct in it though.

I'm still not sure that there will be a wide enough set of settings shared between the various logging frameworks that calling log::set_setting("indentation", 1) won't be effectively equivalent to extern crate env_logger; env_logger::set_indentation(1). This could potentially be prototyped in a separate crate - log_settings or something like that?

cc @rust-lang/libs

@oli-obk
Copy link
Author

oli-obk commented May 19, 2016

Good points, especially about separating out settings into an extra crate. I'll post here when I have something to show

@brson
Copy link
Contributor

brson commented May 31, 2016

Can this be done in a logger implementation instead of directly in the log crate? Indentation is a property of the 'view' layer in my opinion, not the log lines themselves.

Just as an off-the cuff-idea, a custom logger crate could support a thread-local indentation level that was stored independently of the main logging information. So you might write something like:

indent_log(|| {
   info!(...);
})

@oli-obk
Copy link
Author

oli-obk commented May 31, 2016

I implemented it in the log_settings crate. Working on getting docs uploaded...

Indentation is a property of the 'view' layer in my opinion, not the log lines themselves.

I want other crates logs using the log crate to be indented, too. So the logger needs to read the value and crates that are indentation aware can change the indentation. Right now it's very rudimentary, but I'm considering adding something like you suggested.

@dpc
Copy link

dpc commented May 8, 2017

@oli-obk Seems to me that you're hitting the ceiling of what's possible with non-structured logging. Have you seen slog's compact term logger format example (screenshot on https://github.com/slog-rs/slog)? Even if it's not exactly what you desire, you would have much easier time fitting your exact use-case into slog than log.

@oli-obk
Copy link
Author

oli-obk commented May 9, 2017

Cool! I'll have a look at it.

EFanZh pushed a commit to EFanZh/log that referenced this pull request Jul 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

add a way to set an indentation level
5 participants