---
title: "Formats"
author: Brandon Moretz
date: "`r Sys.Date()`"
output:
  prettydoc::html_pretty:
    theme: architect
    highlight: github
vignette: >
  %\VignetteIndexEntry{Formats}
  %\VignetteEngine{knitr::rmarkdown}
  \usepackage[utf8]{inputenc}
---

# Format Objects

```{r setup}
library(dyn.log)

configs <- dyn.log::get_configurations()

# set logging config with tweaks for knitr output
init_logger(configs$knitr)
```

```{r, comment="", results="asis", echo=F}
options(crayon.enabled = TRUE)

knitr::opts_chunk$set(collapse = TRUE,
                      fig.path = "man/figures/FORMAT-",
                      comment = "")

old.hooks <- fansi::set_knit_hooks(knitr::knit_hooks)
```

## Overview

Format objects are the driver of customization in log rendering. Log layouts were developed with the [composition](https://en.wikipedia.org/wiki/Composite_pattern) design pattern in mind; a log layout is simply a series of formats that get evaluated with their associated context to form a log message.

All formats derive from the **fmt_layout** base type and have a couple of generics associated with them, specifically: **style** and **value**. The **fmt_layout** is meant to be an [abstract](https://en.wikipedia.org/wiki/Abstract_type) base type; by driving from it the logging framework can make some assumptions about how to treat a format object.

## Format Types

There are five main categories of log format objects:

* Core
  + **fmt_level_info:** the log level information.
  + **fmt_log_msg:** the log message, evaluated with standard glue format.
* System Context
  + **fmt_metric:** a 'system' context value; see below for more detail.
  + **fmt_timestamp:** the current system time with a customizable format.
* Execution Scope
  + **fmt_exec_scope:** an 'execution' context value; see below for more detail.
* Class Fields
  + **fmt_cls_field:** a field value in the encompassing **R6** class; see below for more detail.
* Literals & New Lines
  + **fmt_literal:** a literal value, which is useful for tweaking exact format specifications.
  + **fmt_newline:** a new line feed in the log message, which is useful for multi-line log messages that have a lot of contextual information in the log output.
  
### System Context

The values available for a **fmt_metric** type can be accessed via *sys_context*:

```{r, sys_context_ex}
sys_context()
```

#### System Context Example

```{r, sys_context_simple}
new_log_layout(
  format = list(
    new_fmt_log_level(),
    new_fmt_metric(crayon::green$bold, "sysname"),
    new_fmt_metric(crayon::yellow$bold, "release"),
    new_fmt_timestamp(crayon::silver$italic, "[%x %H:%M:%S]"),
    new_fmt_log_msg()
  ),
  seperator = '-',
  association = "ex-sys-layout"
)

var1 <- "abc"; var2 <- 123; var3 <- round(runif(1), digits = 6)

Logger$debug("my log message - var1: {var1}, var2: {var2}, var3: {var3}",
             layout = "ex-sys-layout")
```

As you can see, the log message has a great deal of detail, but difficult to interpret due to the amount of information jammed into one line. This is where literals and new lines come into play.

### Literals & New Lines

Literals and new lines are simple formatting objects that help you tweak the layout of a log message to something that is both informative and easy to consume. Taking the previous example, and tweaking the format slightly incorporating literals & new lines, we can produce a log message like this:

```{r, sys_context_literals}
new_log_layout(
  format = list(
    new_fmt_metric(crayon::green$bold, "sysname"),
    new_fmt_literal(crayon::magenta, "["),
    new_fmt_metric(crayon::blue$bold, "release"),
    new_fmt_literal(crayon::magenta, "]"),
    new_fmt_line_break(),
    new_fmt_log_level(),
    new_fmt_timestamp(crayon::silver$italic, "[%x %H:%M:%S]"),
    new_fmt_log_msg()
  ),
  seperator = ' ',
  association = "ex-syslit-layout"
)

var1 <- "abc"; var2 <- 123; var3 <- round(runif(1), digits = 6)

Logger$debug("my log message - var1: {var1}, var2: {var2}, var3: {var3}",
             layout = "ex-syslit-layout")
```

Which has the same information as the previous example, but much easier to consume.

### Execution Scope

Execution scope formats give you the ability to log the context around the invocation of the logger, and is a 
context object, much like *sys_context*, called **exec_context**:

```{r, set_knitter_offset, echo=F}
settings <- dyn.log::get_active_settings()
callstack_settings <- settings$callstack
```

```{r, exec_scope_ex, eval=T}
test <- function(a, b, c) {
  wrapper <- function(x, y, z) {
    outer <- function(d, e, f) {
      inner <- function(g, h, i) {
        # call_subset is used here to skip past knitr execution calls
        exec_context(max_calls = 30, call_subset = c(callstack_settings$start,
                                                     callstack_settings$stop))
      }

      inner(d, e, f)
    }

    outer(x, y, z)
  }
  wrapper(a, b, c)
}

exec_context <- test(1,2,3)
```

```{r}
exec_context
```

The evaluated exec_context gives you a structure with these 3 fields:

* **call_stack:** a named vector of calls
  + *call_1* : "global::test" - top level call
  + *call_2* : "wrapper" - ...
  + *call_3* : "outer" - ...
  + *call_4* : "inner" - inner most fn call
* **calling_fn:** name of the function enclosing the logger call.
  + *calling_fn:* inner
* **ncalls:** number of calls in the stack.
  + *ncalls:* 4

The execution scope can be accessed via the **new_fmt_exec_scope** format object, e.g.:

```{r, sysexec_example}
new_log_layout(
  format = list(
    new_fmt_metric(crayon::green$bold, 'sysname'),
    new_fmt_metric(crayon::blue$yellow, 'release'),
    new_fmt_line_break(),
    new_fmt_log_level(),
    new_fmt_timestamp(crayon::silver$italic, '[%x %H:%M:%S]'),
    new_fmt_literal(crayon::magenta$bold, 'fn('),
    new_fmt_exec_scope(crayon::magenta$bold, 'calling_fn'),
    new_fmt_literal(crayon::magenta$bold, ')'),
    new_fmt_log_msg(),
    new_fmt_line_break(),
    new_fmt_exec_scope(crayon::bgYellow$blue$bold, 'call_stack')
  ),
  seperator = '-',
  association = 'ex-sysexec-cs-layout'
)

local_fn <- function() {
  outer <- function() {
    inner <- function() {
      var1 <- "abc"; var2 <- 123; var3 <- round(runif(1), digits = 6)

      Logger$debug("my log message - var1: '{var1}', var2: '{var2}', var3: '{var3}'",
                   layout = 'ex-sysexec-cs-layout')
    }
    inner()
  }
  outer()
}

local_fn()
```

