# indentlog -- logging with automatic call-graph indenting

Indentlog is a lightweight header-only library for console logging.

## Features

- header-only;  nothing to link
- easy-to-read format uses indenting to show call structure.
  indentation has user-controlled upper limit to preserve readability with
  deeply nested call graphs
- colorized output using vt100 color codes (ansi or xterm)
- automatically captures + displays timestamp, function name and code location.
  supports several function-name formats to reflect tradeoff readability for precision
- application code may issue logging code that contains embedded newlines and/or color escapes;
  logger preserves indentation.
- logger is 'truthy' -> only pay for formatting when entry points is enabled.
- also provides family of convenience stream-inserters

## Getting Started

### build + install `xo-cmake` dependency (cmake macros)

see [github/Rconybea/xo-cmake](https://github.com/Rconybea/xo-cmake)

Installs a few cmake ingredients,  along with a build assistant for XO projects such as this one.

### copy repository locally

Using `xo-build` (provided by `xo-cmake`):
```
$ xo-build --clone xo-indentlog
```

or equivalently:
```
$ cd ~/proj
$ git clone git@github.com:Rconybea/indentlog.git xo-indentlog
```

### build & install

Using `xo-build`:
```
$ xo-build --configure --build --install xo-indentlog
```

or equivalently:
```
$ mkdir xo-indentlog/.build
$ PREFIX=/usr/local  # for example
$ cmake -DCMAKE_INSTALL_PREFIX=${PREFIX} -S xo-indentlog -B xo-indentlog/.build
$ cmake --build xo-indentlog/.build
$ cmake --install xo-indentlog/.build
```

For some more detail see [BUILD.md](BUILD.md)

### LSP support

lsp will look for `compile_commands.json` in the root of the source tree; cmake creates it in build directory

```
$ cd xo-indentlog
$ ln -s build/compile_commands.json
```

## Examples

### 1

```
#include "indentlog/scope.hpp"

using namespace xo;

void inner(int x) {
    scope log(XO_ENTER0(always), ":x ", x);
}

void outer(int y) {
    scope log(XO_ENTER0(always), ":y ", y);

    inner(2*y);
}

int
main(int argc, char ** argv) {
    outer(123);
}
```

output:
![ex1 output](img/ex1.png)

- indentlog types are provided in the `xo` namespace.
  macros are prefixed with `XO_`
- indentation reflects call structure. We don't see anything for `main()`,
  since we didn't put any logging there

### 2 slightly more elaborate example

```
/* examples ex2/ex2.cpp */

#include "indentlog/scope.hpp"

using namespace xo;

int
fib(int n) {
    scope log(XO_ENTER0(info), ":n ", n);

    int retval = 1;

    if (n >= 2) {
        retval = fib(n - 1) + fib(n - 2);
        log && log(":n ", n);
    }

    log.end_scope("<- :retval ", retval);

    return retval;
}

int
main(int argc, char ** argv) {
    log_config::min_log_level = xo::log_level::info;
    log_config::indent_width = 4;

    int n = 4;

    scope log(XO_ENTER0(info), ":n ", 4);

    int fn = fib(n);

    log && log(":n ", n);
    log && log("<- :fib(n) ", fn);
}
```
output:
![ex2 output](img/ex2.png)

- global configuration settings live in the `xo::log_config` class.  see [log_config.hpp](include/indentlog/log_config.hpp)
- the recommended form `log && log(...)` tests whether logging at this site is enabled /before/ evaluating/formatting the log message;
  when logging is disabled,  this saves the cost of computing and formatting that message.

### 3 example exposing runtime configuration options

```
/* examples ex3/ex3.cpp */

#include "indentlog/scope.hpp"

using namespace xo;

int
fib(int n) {
    scope log(XO_ENTER0(info), tag("n", n));

    int retval = 1;

    if (n >= 2) {
        retval = fib(n - 1) + fib(n - 2);
    }

    log.end_scope(tag("n", n), " <-", xtag("retval", retval));

    return retval;
}

int
main(int argc, char ** argv) {
    log_config::min_log_level = log_level::info;
    log_config::time_enabled = true;
    log_config::time_local_flag = true;
    log_config::style = FS_Streamlined;
    log_config::indent_width = 4;
    log_config::max_indent_width = 30;
    log_config::location_tab = 80;
    log_config::encoding = CE_Xterm;
    log_config::function_entry_color = 69;
    log_config::function_exit_color = 70;
    log_config::code_location_color = 166;

    int n = 3;

    scope log(XO_ENTER0(info), ":n ", 4);

    int fn = fib(n);

    log && log(tag("n", n));
    log && log("<-", xtag("fib(n)", fn));
}

/* ex3/ex3.cpp */
```

output:
![ex3 output](img/ex3.png)

### 4 example: function signatures

```
/* @file ex4.cpp */

#include "indentlog/scope.hpp"

using namespace xo;

class Quadratic {
public:
    Quadratic(double a, double b, double c) : a_{a}, b_{b}, c_{c} {}

    double operator() (double x) const {
        scope log(XO_ENTER0(info), tag("a", a_), xtag("b", b_), xtag("c", c_), xtag("x", x));

        double retval = (a_ * x * x) + (b_ * x) + c_;

        log.end_scope("<-", xtag("retval", retval));

        return retval;
    }

private:
    double a_ = 0.0;;
    double b_ = 0.0;
    double c_ = 0.0;
};

int
main(int argc, char ** argv) {
    //log_config::style  = FS_Pretty;
    log_config::style  = FS_Streamlined;
    log_config::min_log_level = log_level::info;

    scope log(XO_ENTER0(info));

    Quadratic quadratic(2.0, -5.0, 7.0);

    double x = 3.0;
    double r = 0.0;

    log_config::style  = FS_Pretty;

    r = quadratic(x);

    log_config::style = FS_Streamlined;

    r = quadratic(x);

    log_config::style = FS_Simple;

    r = quadratic(x);
}

/* end ex4.cpp */
```

output:

![ex4 output](img/ex4.png)
