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


```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

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

```{r, comment = "", results = "asis", echo = F}
options(crayon.enabled = TRUE)
knitr::opts_chunk$set(collapse = TRUE, comment = "")
old.hooks <- fansi::set_knit_hooks(knitr::knit_hooks)
```

## Configurations

The dyn.log package is configuration-driven to provide the client flexibility with both the look & feel of log layouts/formatting (aesthetics), and how it interacts with its container environment. This vignette will focus less on *how the logger works*, and more on *how to make it work for you*.

There are two main ways to set the logging configuration:

* Explicitly pass a configuration file to the *init_logger* function.
  + init_logger(file_path = "~/package/config.yaml")
* Global Option
    + options("dyn.log.config" = "~/package/config.yaml")

If you work on a large project (i.e., comprised of many packages) setting the global option in your .Rprofile is the suggested approach; it will save you a great deal of time.

## Templates

The package comes with a few preconfigured logging setups that are common starting points for your bespoke configuration. To see the configuration templates call the *get_configurations* method:


```{r, eval = F}
configs <- get_configurations()
configs
```
```{r, echo = F}
configs <- get_configurations()
pander::pander(sapply(configs, basename))
```

This will return a list of configurations and the yaml file path on your local system.

There are two main ways to utilize these 'OTB' templates, both having the basic
idea of copying a pre-configured template from the package to your local
working environment, and then setting the logger to look for that file going-forward.

### Package

To use a custom logging configuration in a package:

```{r, eval=FALSE}
file.copy(from = configs$default, to = "inst/logging.yaml")
```

Which will make the new template part of your package, to load it, you can:

Put this in your packages' ".onLoad" handler:

```{r, eval=FALSE}
init_logger(config_file = system.file("logging.yaml", package = "yourpkg"))
```

Or, put this line in your **.Rprofile**, and whenver you attach the **dyn.log** package, the logger will automatically load your custom configuration.

```{r, eval=FALSE}
options("dyn.log.config" = system.file("logging.yaml", package = "yourpkg"))
```


### Script Directory

Usage of a custom configuration in a directory/project is basically the same:

```{r, eval=F}
# use here to get your workspace root
file.copy(from = configs$default, 
          to = file.path(here::here(), "logging.yaml"))
```

then in your **.Rprofile**:

```{r, eval=F}
library(dyn.log)

init_logger("logging.yaml")
```

Or,

```{r, eval=FALSE}
options("dyn.log.config" = file.path(here::here(), "logging.yaml"))
```

And whenever the **dyn.log** package gets attached, it will be auto-configure for you.

## Integration

Now we'll take look at customizing log layouts for types in your project.

If your client code leverages the use of R6 classes, you can specify a custom layout for standalone objects or objects that are part of a hierarchy by using the *association* property. For example, [TestObject](https://github.com/bmoretz/dyn.log/blob/master/tests/testthat/helper-objects.R) is a helper object defined in the testthat folder of dyn.log:

The object hierarchy is declared as follows:

```{r}
TestObject <- R6::R6Class(
  classname = "TestObject",

  public = list(

    cls_name = NULL,

    initialize = function() {
      private$id <- private$generate_id()
      self$cls_name <- private$get_class_name()
    },

    identifier = function() {
      invisible(private$id)
    },

    class_name = function() {
      invisible(self$cls_name)
    },

    invoke_logger = function() {
      a <- "test"; b <- 123; c <- 100L

      Logger$trace("these are some variables: {a} - {b} - {c}")
    }
  ),

  private = list(
    id = NULL,

    generate_id = function(n = 15) {
      paste0(sample(LETTERS, n, TRUE), collapse =  "")
    },

    get_class_name = function() {
      calls <- as.character(sys.calls())
      calls <- calls[max(which(stringr::str_detect(calls, "\\$new\\(.*\\)")))]
      stopifnot(length(calls) == 1)
      invisible(stringr::str_remove(calls, "\\$new\\(.*\\)"))
    }
  )
)

DerivedTestObject <- R6::R6Class(
  classname = "DerivedTestObject",
  inherit = TestObject,
  public = list(

    initialize = function() {
      super$initialize()
    },

    invoke_logger = function() {
      a <- "derived test"; b <- 321; c <- 200L

      Logger$trace("variables in derived: {a} - {b} - {c}")
    }
  )
)
```

The layout configuration for "TestObject" looks like this:

```yaml
layouts:
- association: TestObject
  seperator: ' '
  new_line: \n
  formats: !expr list(
           new_fmt_literal(crayon::cyan$bold, 'Class:'),
           new_fmt_cls_field(crayon::bgCyan$silver$bold, 'cls_name'),
           new_fmt_literal(crayon::cyan$bold, 'Object Id:'),
           new_fmt_cls_field(crayon::bgCyan$silver$bold, 'id'),
           new_fmt_line_break(),
           new_fmt_log_level(),
           new_fmt_timestamp(crayon::silver$italic),
           new_fmt_exec_scope(crayon::magenta$bold, 'calling_fn'),
           new_fmt_log_msg())
```

```{r}
init_logger(configs$object)

test_obj <- DerivedTestObject$new()

test_obj$invoke_logger()
```

As you can see, the logger knows to log contextual information about any object derived from "TestObject." TestObject exposes two properties, one public and one private:

+ **id**: private property which is a random string unique to the object instance.
+ **cls_name**": a public property which is calculated when the object is instantiated to get the correct run-time class name (since it is derived).

This can be a powerful mechanic for templatizing important contextual information in your client applications or scripts.
