---
title: "Writing Custom Logger Extensions"
vignette: >
  %\VignetteIndexEntry{Writing Custom Logger Extensions}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

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

In this vignette I suppose that you are already familiar with [Customizing the format and destination of log records](https://daroczig.github.io/logger/articles/customize_logger.html) vignette.

## Custom log message formatter functions

The log message formatter function should be able to take any number of R objects and convert those into a character vector that is safe to pass to the layout function.

This transformer function can be as simple as calling `paste`, `glue` or `sprintf` or something complex as well, eg looking up user attributes of a user id mentioned in a log record etc.

When writing a custom formatter function, it should also accept the original logging function call as `.logcall`, the parent call as `.topcall` and the log request's environment as `.topenv`, which can be used to find the relevant variables in your formatter.

If you are writing such a function or a generator function returning a log message formatter function, please keep the actual call resulting in the formatter function (eg `match.call()` in the generator function or the quoted function call) recorded in the `generator` attribute of the function so that `?log_formatter` can pretty-print that instead of the unnamed function body. See the [`formatters.R`](https://github.com/daroczig/logger/blob/master/R/formatters.R) file for more examples.

## Custom log layout rendering functions

The layout functions return the log record and take at least two arguments:

- the log level and
- a message already formatted as a string by a log message formatter function
- the namespace (as `namespace`), calling function (as `.topcall`) and its environment (as `.topenv`) of the log request, and the actual log call (as `.logcall`) automatically collected by `?log_level`

Such layout rendering function can be as simple as the default `?layout_simple`:

```r
layout_simple <- function(level, msg, ...) {
    paste0(attr(level, 'level'), ' [', format(Sys.time(), "%Y-%m-%d %H:%M:%S"), '] ', msg)
}
```

Or much more complex, eg looking up the hostname of the machine, public IP address etc and logging all these automatically with the message of the log request.

Your easiest option to set up a custom layout is calling `?layout_glue_generator` that comes with a nice API being able to access a bunch of meta-information on the log request via `?get_logger_meta_variables`. For such example, see the [Customizing the format and destination of log records](https://daroczig.github.io/logger/articles/customize_logger.html) vignette.

If you are writing such a function or a generator function returning a log message formatter function, please keep the actual call resulting in the formatter function (eg `match.call()` in the generator function or the quoted function call) recorded in the `generator` attribute of the function so that `?log_layout` can pretty-print that instead of the unnamed function body. See the [`layouts.R`](https://github.com/daroczig/logger/blob/master/R/layouts.R) file for more examples.

## Custom log record appenders

The appender functions take log records and delivers those to the desired destination. This can be as simple as writing to the console (`?appender_console`) or to a local file (`?appender_file`), or delivering the log record via an API request to a remote service, streaming somewhere or sending a Slack message (`?appender_slack`).

If you are writing such a function or a generator function returning a log message formatter function, please keep the actual call resulting in the formatter function (eg `match.call()` in the generator function or the quoted function call) recorded in the `generator` attribute of the function so that `?log_appender` can pretty-print that instead of the unnamed function body. See the [`appenders.R`](https://github.com/daroczig/logger/blob/master/R/appenders.R) file for more examples.

An example for a custom appender delivering log messages to a database table:

```r
## the dbr package provides and easy and secure way of connecting to databased from R
## although if you want to minimize the dependencies, feel free to stick with DBI etc.
library(dbr)
## init a persistent connection to the database using a yaml config in the background thanks to dbr
## NOTE that this is optional and a temporarily connection could be also used
##      for higher reliability but lower throughput
con <- db_connect('mydb')
## define custom function writing the log message to a table
log_appender(function(lines) {
    db_append(
        df = data.frame(timestamp = Sys.time(), message = lines),
        table = 'logs', db = con)
})
```

```{r cleanup, include = FALSE}
logger:::namespaces_reset()
```
