├── .Rbuildignore
├── .covrignore
├── .github
├── .gitignore
├── ISSUE_TEMPLATE
│ └── bug_report.md
└── workflows
│ ├── R-CMD-check.yaml
│ ├── lint.yaml
│ ├── pkgdown.yaml
│ ├── recheck.yaml
│ └── test-coverage.yaml
├── .gitignore
├── .lintr
├── DESCRIPTION
├── LICENSE
├── LICENSE.md
├── NAMESPACE
├── NEWS.md
├── R
├── appenders.R
├── color.R
├── formatters.R
├── helpers.R
├── hooks.R
├── layouts.R
├── levels.R
├── logger-meta.R
├── logger-package.R
├── logger.R
├── try.R
├── utils.R
└── zzz.R
├── README.Rmd
├── README.md
├── _pkgdown.yml
├── codecov.yml
├── inst
└── demo-packages
│ └── logger-tester-package
│ ├── DESCRIPTION
│ ├── LICENSE
│ ├── NAMESPACE
│ ├── R
│ └── tester.R
│ └── man
│ ├── logger_info_tester_function.Rd
│ └── logger_tester_function.Rd
├── man
├── appender_async.Rd
├── appender_console.Rd
├── appender_file.Rd
├── appender_kinesis.Rd
├── appender_pushbullet.Rd
├── appender_slack.Rd
├── appender_stdout.Rd
├── appender_syslog.Rd
├── appender_syslognet.Rd
├── appender_tee.Rd
├── appender_telegram.Rd
├── appender_void.Rd
├── as.loglevel.Rd
├── colorize_by_log_level.Rd
├── delete_logger_index.Rd
├── deparse_to_one_line.Rd
├── fail_on_missing_package.Rd
├── figures
│ ├── colors.png
│ └── logo.png
├── formatter_cli.Rd
├── formatter_glue.Rd
├── formatter_glue_or_sprintf.Rd
├── formatter_glue_safe.Rd
├── formatter_json.Rd
├── formatter_logging.Rd
├── formatter_pander.Rd
├── formatter_paste.Rd
├── formatter_sprintf.Rd
├── get_logger_meta_variables.Rd
├── grapes-except-grapes.Rd
├── layout_blank.Rd
├── layout_glue.Rd
├── layout_glue_colors.Rd
├── layout_glue_generator.Rd
├── layout_json.Rd
├── layout_json_parser.Rd
├── layout_logging.Rd
├── layout_simple.Rd
├── layout_syslognet.Rd
├── log_appender.Rd
├── log_chunk_time.Rd
├── log_elapsed.Rd
├── log_errors.Rd
├── log_eval.Rd
├── log_failure.Rd
├── log_formatter.Rd
├── log_indices.Rd
├── log_layout.Rd
├── log_level.Rd
├── log_levels.Rd
├── log_messages.Rd
├── log_namespaces.Rd
├── log_separator.Rd
├── log_shiny_input_changes.Rd
├── log_threshold.Rd
├── log_tictoc.Rd
├── log_warnings.Rd
├── log_with_separator.Rd
├── logger-package.Rd
├── logger.Rd
├── skip_formatter.Rd
└── with_log_threshold.Rd
├── pkgdown
└── favicon
│ ├── apple-touch-icon-120x120.png
│ ├── apple-touch-icon-152x152.png
│ ├── apple-touch-icon-180x180.png
│ ├── apple-touch-icon-60x60.png
│ ├── apple-touch-icon-76x76.png
│ ├── apple-touch-icon.png
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ └── favicon.ico
├── tests
├── testthat.R
└── testthat
│ ├── _snaps
│ ├── formatters.md
│ ├── helpers.md
│ ├── hooks.md
│ ├── layouts.md
│ ├── logger.md
│ ├── try.md
│ └── utils.md
│ ├── helper.R
│ ├── test-appenders.R
│ ├── test-eval.R
│ ├── test-formatters.R
│ ├── test-helpers.R
│ ├── test-hooks.R
│ ├── test-layouts.R
│ ├── test-logger-meta.R
│ ├── test-logger.R
│ ├── test-return.R
│ ├── test-try.R
│ └── test-utils.R
└── vignettes
├── Intro.Rmd
├── anatomy.Rmd
├── customize_logger.Rmd
├── logger_structure.svg
├── migration.Rmd
├── migration.html
├── performance.Rmd
├── r_packages.Rmd
└── write_custom_extensions.Rmd
/.Rbuildignore:
--------------------------------------------------------------------------------
1 | .travis.yml
2 | _pkgdown.yml
3 | .editorconfig
4 | .gitignore
5 | .lintr
6 | .*.sh$
7 | .backup
8 | .*.graphml$
9 | logo.R
10 | cran-comments.md
11 | .covrignore
12 | dev-resources
13 | ^doc$
14 | ^Meta$
15 | ^_pkgdown\.yml$
16 | ^docs$
17 | ^pkgdown$
18 | ^\.github$
19 | revdep
20 | ^codecov\.yml$
21 | ^README\.Rmd$
22 | ^LICENSE\.md$
23 |
--------------------------------------------------------------------------------
/.covrignore:
--------------------------------------------------------------------------------
1 | R/zzz.R
--------------------------------------------------------------------------------
/.github/.gitignore:
--------------------------------------------------------------------------------
1 | *.html
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: File a bug report with a reproducible example
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 |
12 | Please be as clear and thorough as possible.
13 |
14 | **Reproducible example**
15 |
16 | Share a short code chunk reproducing the bug, ideally using `reprex`. To make sure that the log output is visible, either switch the appender to `stdout`, or use the `std_out_err` option of `reprex`. For example, you could copy one of the below code chunks and run `reprex` to get a markdown chunk that you can paste in the bug report:
17 |
18 | 1. For using `reprex::reprex()` (updating the appender function as part of the example):
19 |
20 | ```r
21 | library(logger)
22 | log_appender(appender_stdout)
23 | log_info(42)
24 | ```
25 |
26 | 2. For using `reprex::reprex(std_out_err = TRUE)` (without updating the appender function):
27 |
28 | ```r
29 | logger::log_info(42)
30 | ```
31 |
32 | **Session info**
33 |
34 |
35 | ``` r
36 | # Paste here the output of `sessionInfo()`
37 | ```
38 |
39 |
--------------------------------------------------------------------------------
/.github/workflows/R-CMD-check.yaml:
--------------------------------------------------------------------------------
1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3 | #
4 | # NOTE: This workflow is overkill for most R packages and
5 | # check-standard.yaml is likely a better choice.
6 | # usethis::use_github_action("check-standard") will install it.
7 | on:
8 | push:
9 | branches: [main, master]
10 | pull_request:
11 | branches: [main, master]
12 |
13 | name: R-CMD-check.yaml
14 |
15 | permissions: read-all
16 |
17 | jobs:
18 | R-CMD-check:
19 | runs-on: ${{ matrix.config.os }}
20 |
21 | name: ${{ matrix.config.os }} (${{ matrix.config.r }})
22 |
23 | strategy:
24 | fail-fast: false
25 | matrix:
26 | config:
27 | - {os: macos-latest, r: 'release'}
28 |
29 | - {os: windows-latest, r: 'release'}
30 | # use 4.0 or 4.1 to check with rtools40's older compiler
31 | - {os: windows-latest, r: 'oldrel-4'}
32 |
33 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'}
34 | - {os: ubuntu-latest, r: 'release'}
35 | - {os: ubuntu-latest, r: 'oldrel-1'}
36 | - {os: ubuntu-latest, r: 'oldrel-2'}
37 | - {os: ubuntu-latest, r: 'oldrel-3'}
38 | - {os: ubuntu-latest, r: 'oldrel-4'}
39 |
40 | env:
41 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
42 | R_KEEP_PKG_SOURCE: yes
43 |
44 | steps:
45 | - uses: actions/checkout@v4
46 |
47 | - uses: r-lib/actions/setup-pandoc@v2
48 |
49 | - uses: r-lib/actions/setup-r@v2
50 | with:
51 | r-version: ${{ matrix.config.r }}
52 | http-user-agent: ${{ matrix.config.http-user-agent }}
53 | use-public-rspm: true
54 |
55 | - uses: r-lib/actions/setup-r-dependencies@v2
56 | with:
57 | extra-packages: any::rcmdcheck
58 | needs: check
59 | pak-version: devel # can remove once pak 0.7.3 is released
60 |
61 | - uses: r-lib/actions/check-r-package@v2
62 | with:
63 | upload-snapshots: true
64 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")'
65 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yaml:
--------------------------------------------------------------------------------
1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3 | on:
4 | push:
5 | branches: [main, master]
6 | pull_request:
7 | branches: [main, master]
8 |
9 | name: lint.yaml
10 |
11 | permissions: read-all
12 |
13 | jobs:
14 | lint:
15 | runs-on: ubuntu-latest
16 | env:
17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
18 | steps:
19 | - uses: actions/checkout@v4
20 |
21 | - uses: r-lib/actions/setup-r@v2
22 | with:
23 | use-public-rspm: true
24 |
25 | - uses: r-lib/actions/setup-r-dependencies@v2
26 | with:
27 | extra-packages: any::lintr, local::.
28 | needs: lint
29 |
30 | - name: Lint
31 | run: lintr::lint_package()
32 | shell: Rscript {0}
33 | env:
34 | LINTR_ERROR_ON_LINT: true
35 |
--------------------------------------------------------------------------------
/.github/workflows/pkgdown.yaml:
--------------------------------------------------------------------------------
1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3 | on:
4 | push:
5 | branches: [main, master]
6 | pull_request:
7 | branches: [main, master]
8 | release:
9 | types: [published]
10 | workflow_dispatch:
11 |
12 | name: pkgdown.yaml
13 |
14 | permissions: read-all
15 |
16 | jobs:
17 | pkgdown:
18 | runs-on: ubuntu-latest
19 | # Only restrict concurrency for non-PR jobs
20 | concurrency:
21 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }}
22 | env:
23 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
24 | permissions:
25 | contents: write
26 | steps:
27 | - uses: actions/checkout@v4
28 |
29 | - uses: r-lib/actions/setup-pandoc@v2
30 |
31 | - uses: r-lib/actions/setup-r@v2
32 | with:
33 | use-public-rspm: true
34 |
35 | - uses: r-lib/actions/setup-r-dependencies@v2
36 | with:
37 | extra-packages: any::pkgdown, local::.
38 | needs: website
39 |
40 | - name: Build site
41 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE)
42 | shell: Rscript {0}
43 |
44 | - name: Deploy to GitHub pages 🚀
45 | if: github.event_name != 'pull_request'
46 | uses: JamesIves/github-pages-deploy-action@v4.5.0
47 | with:
48 | clean: false
49 | branch: gh-pages
50 | folder: docs
51 |
--------------------------------------------------------------------------------
/.github/workflows/recheck.yaml:
--------------------------------------------------------------------------------
1 | on:
2 | workflow_dispatch:
3 | inputs:
4 | which:
5 | type: choice
6 | description: Which dependents to check
7 | options:
8 | - strong
9 | - most
10 |
11 | name: Reverse dependency check
12 |
13 | jobs:
14 | revdep_check:
15 | name: Reverse check ${{ inputs.which }} dependents
16 | uses: r-devel/recheck/.github/workflows/recheck.yml@v1
17 | with:
18 | which: ${{ inputs.which }}
19 | subdirectory: ''
20 |
--------------------------------------------------------------------------------
/.github/workflows/test-coverage.yaml:
--------------------------------------------------------------------------------
1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3 | on:
4 | push:
5 | branches: [main, master]
6 | pull_request:
7 | branches: [main, master]
8 |
9 | name: test-coverage.yaml
10 |
11 | permissions: read-all
12 |
13 | jobs:
14 | test-coverage:
15 | runs-on: ubuntu-latest
16 | env:
17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
18 |
19 | steps:
20 | - uses: actions/checkout@v4
21 |
22 | - uses: r-lib/actions/setup-r@v2
23 | with:
24 | use-public-rspm: true
25 |
26 | - uses: r-lib/actions/setup-r-dependencies@v2
27 | with:
28 | extra-packages: any::covr, any::xml2
29 | needs: coverage
30 |
31 | - name: Test coverage
32 | run: |
33 | cov <- covr::package_coverage(
34 | quiet = FALSE,
35 | clean = FALSE,
36 | install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package")
37 | )
38 | covr::to_cobertura(cov)
39 | shell: Rscript {0}
40 |
41 | - uses: codecov/codecov-action@v4
42 | with:
43 | fail_ci_if_error: ${{ github.event_name != 'pull_request' && true || false }}
44 | file: ./cobertura.xml
45 | plugin: noop
46 | disable_search: true
47 | token: ${{ secrets.CODECOV_TOKEN }}
48 |
49 | - name: Show testthat output
50 | if: always()
51 | run: |
52 | ## --------------------------------------------------------------------
53 | find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true
54 | shell: bash
55 |
56 | - name: Upload test results
57 | if: failure()
58 | uses: actions/upload-artifact@v4
59 | with:
60 | name: coverage-test-failures
61 | path: ${{ runner.temp }}/package
62 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | docs/
2 | README.html
3 |
4 | /.quarto/
5 |
--------------------------------------------------------------------------------
/.lintr:
--------------------------------------------------------------------------------
1 | linters: linters_with_defaults(
2 | line_length_linter(120),
3 | object_usage_linter(interpret_glue = TRUE),
4 | object_name_linter(styles = c("snake_case", "symbols", "UPPERCASE"))
5 | )
6 | exclude: '# nolint'
7 | exclude_start: '# nolint start'
8 | exclude_end: '# nolint end'
9 | exclusions: list(
10 | "inst/demo-packages/logger-tester-package/R/tester.R",
11 | "vignettes/migration.Rmd"
12 | )
13 |
--------------------------------------------------------------------------------
/DESCRIPTION:
--------------------------------------------------------------------------------
1 | Type: Package
2 | Package: logger
3 | Title: A Lightweight, Modern and Flexible Logging Utility
4 | Version: 0.4.0.9000
5 | Date: 2024-10-19
6 | Authors@R: c(
7 | person("Gergely", "Daróczi", , "daroczig@rapporter.net", role = c("aut", "cre"),
8 | comment = c(ORCID = "0000-0003-3149-8537")),
9 | person("Hadley", "Wickham", , "hadley@posit.co", role = "aut",
10 | comment = c(ORCID = "0000-0003-4757-117X")),
11 | person("System1", role = "fnd")
12 | )
13 | Description: Inspired by the the 'futile.logger' R package and 'logging'
14 | Python module, this utility provides a flexible and extensible way of
15 | formatting and delivering log messages with low overhead.
16 | License: MIT + file LICENSE
17 | URL: https://daroczig.github.io/logger/
18 | BugReports: https://github.com/daroczig/logger/issues
19 | Depends:
20 | R (>= 4.0.0)
21 | Imports:
22 | utils
23 | Suggests:
24 | botor,
25 | cli,
26 | covr,
27 | crayon,
28 | devtools,
29 | glue,
30 | jsonlite,
31 | knitr,
32 | mirai (>= 1.3.0),
33 | pander,
34 | parallel,
35 | R.utils,
36 | rmarkdown,
37 | roxygen2,
38 | RPushbullet,
39 | rsyslog,
40 | shiny,
41 | slackr (>= 1.4.1),
42 | syslognet,
43 | telegram,
44 | testthat (>= 3.0.0),
45 | withr
46 | Enhances:
47 | futile.logger,
48 | log4r,
49 | logging
50 | VignetteBuilder:
51 | knitr
52 | Config/testthat/edition: 3
53 | Config/testthat/parallel: TRUE
54 | Encoding: UTF-8
55 | Roxygen: list(markdown = TRUE)
56 | RoxygenNote: 7.3.2
57 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | YEAR: 2024
2 | COPYRIGHT HOLDER: logger authors
3 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # MIT License
2 |
3 | Copyright (c) 2024 logger authors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/NAMESPACE:
--------------------------------------------------------------------------------
1 | # Generated by roxygen2: do not edit by hand
2 |
3 | S3method(as.loglevel,character)
4 | S3method(as.loglevel,default)
5 | S3method(as.loglevel,integer)
6 | S3method(as.loglevel,numeric)
7 | S3method(print,loglevel)
8 | export("%except%")
9 | export(DEBUG)
10 | export(ERROR)
11 | export(FATAL)
12 | export(INFO)
13 | export(OFF)
14 | export(SUCCESS)
15 | export(TRACE)
16 | export(WARN)
17 | export(appender_async)
18 | export(appender_console)
19 | export(appender_file)
20 | export(appender_kinesis)
21 | export(appender_pushbullet)
22 | export(appender_slack)
23 | export(appender_stderr)
24 | export(appender_stdout)
25 | export(appender_syslog)
26 | export(appender_syslognet)
27 | export(appender_tee)
28 | export(appender_telegram)
29 | export(appender_void)
30 | export(as.loglevel)
31 | export(colorize_by_log_level)
32 | export(delete_logger_index)
33 | export(deparse_to_one_line)
34 | export(fail_on_missing_package)
35 | export(formatter_cli)
36 | export(formatter_glue)
37 | export(formatter_glue_or_sprintf)
38 | export(formatter_glue_safe)
39 | export(formatter_json)
40 | export(formatter_logging)
41 | export(formatter_pander)
42 | export(formatter_paste)
43 | export(formatter_sprintf)
44 | export(get_logger_meta_variables)
45 | export(grayscale_by_log_level)
46 | export(layout_blank)
47 | export(layout_glue)
48 | export(layout_glue_colors)
49 | export(layout_glue_generator)
50 | export(layout_json)
51 | export(layout_json_parser)
52 | export(layout_logging)
53 | export(layout_simple)
54 | export(layout_syslognet)
55 | export(log_appender)
56 | export(log_chunk_time)
57 | export(log_debug)
58 | export(log_elapsed)
59 | export(log_elapsed_start)
60 | export(log_error)
61 | export(log_errors)
62 | export(log_eval)
63 | export(log_failure)
64 | export(log_fatal)
65 | export(log_formatter)
66 | export(log_indices)
67 | export(log_info)
68 | export(log_layout)
69 | export(log_level)
70 | export(log_messages)
71 | export(log_namespaces)
72 | export(log_separator)
73 | export(log_shiny_input_changes)
74 | export(log_success)
75 | export(log_threshold)
76 | export(log_tictoc)
77 | export(log_trace)
78 | export(log_warn)
79 | export(log_warnings)
80 | export(log_with_separator)
81 | export(logger)
82 | export(skip_formatter)
83 | export(with_log_threshold)
84 | importFrom(utils,assignInMyNamespace)
85 | importFrom(utils,assignInNamespace)
86 | importFrom(utils,capture.output)
87 | importFrom(utils,compareVersion)
88 | importFrom(utils,getFromNamespace)
89 | importFrom(utils,packageVersion)
90 | importFrom(utils,str)
91 |
--------------------------------------------------------------------------------
/NEWS.md:
--------------------------------------------------------------------------------
1 | # logger (development version)
2 |
3 | * Support renaming meta fields (#217, @atusy)
4 | * Added `log_elapsed()` to show cumulative elapsed running time (#221, @thomasp85)
5 | * `log_errors()` gains a `traceback` argument that toggles whether the error traceback should be logged along with the message (fix #86 via #223, @thomasp85)
6 | * File and line location of the log call is now available to the layouts (fix #110 via #224, @thomasp85)
7 | * New `formatter_cli()` allows you to use the syntax from the cli package to create log messages (fix #210 via #225, @thomasp85)
8 | * New `log_chunk_time()` helper function to automatically log the execution time of knitr code chunks (fix #222 via #227, @thomasp85)
9 | * Allow user to overwrite the timestamp during logging if needed (fix #230, @thomasp85)
10 |
11 | # logger 0.4.0 (2024-10-19)
12 |
13 | A lot of internal code quality improvements and standardization,
14 | improved documentations, modernized tests, performance speedups.
15 |
16 | ## New features
17 |
18 | * logo 😻 (#196, @hadley)
19 | * computing metadata lazily, so various expensive computations are only performed if you actually add them to the log (#105, @hadley)
20 | * `log_appender()`, `log_layout()` and `log_formatter()` now check that you are calling them with a function, and return the previously set value (#170, @hadley)
21 | * new function to return number of log indices (#194, @WurmPeter)
22 | * `appender_async` is now using `mirai` instead of a custom background process and queue system (#214, @hadley @shikokuchuo)
23 |
24 | ## Fixes
25 |
26 | * `eval` scoping and lazy eval (#178, @hadley)
27 |
28 | ## Housekeeping
29 |
30 | * update `pkgdown` site to Bootstrap 5 and related revamp, e.g. reference index and run/show examples (#159 #165 #193, @hadley)
31 | * roxygen updated to use markdown, general cleanup (#160 #161 #191 #201, @hadley)
32 | * testing improvements, e.g. move to `testthat` v3 and snapshots, syntactic sugar (#163 #167 #168 #169 #171 #192, @hadley)
33 | * README tweaks (#162 #176, @hadley)
34 | * modernize GitHub Actions (#171, @hadley)
35 | * drop support for R versions below 4.0.0 (#177, @hadley)
36 | * internal function tweaks (#181 #187 #197, @hadley)
37 | * restyle sources (#185 #186 #191 #199, @daroczig and @hadley)
38 |
39 | # logger 0.3.0 (2024-03-03)
40 |
41 | Many unrelated small features, fixes and documentation updates collected over 2+ years.
42 |
43 | ## New features
44 |
45 | * update `log_*` functions to invisibly return the formatted log message and record (#26, @r2evans)
46 | * add `namespace` argument to `log_shiny_input_changes` (#93, @kpagacz)
47 | * optionally suppress messages in `globalCallingHandlers` after being logged (#100, @DanChaltiel)
48 | * `as.loglevel` helper to convert string/number to loglevel (requested by @tentacles-from-outer-space)
49 | * new formatter function: `formatter_glue_safe` (#126, @terashim)
50 | * support `OFF` log level (#138, @pawelru)
51 | * override default `INFO` log level via env var (#145, requested by sellorm)
52 |
53 | ## Fixes
54 |
55 | * handle zero-length messages in `formatter_glue_or_sprintf` (#74, @deeenes)
56 | * generalize `log_separator` to work with all layout functions (#96, @Polkas)
57 | * support log levels in `log_shiny_input_changes` (#103, @taekeharkema)
58 | * fix fn name lookup/reference with nested calls (#120, reported by @averissimo)
59 | * force the `file` argument of `appender_tee` (#124, reported by @dbontemps)
60 | * don't allow stacking logger hooks on messages/warnings/errors (reported by @jkeuskamp)
61 | * improve fragile test case when `Hmisc` loaded (#131, @r2evans)
62 | * pass `index`, `namespace` etc from `log_` functions down to `log_level` (#143, @MichaelChirico)
63 | * refer to the caller function in global message logger hooks (#146, reported by @gabesolomon10)
64 |
65 | # logger 0.2.2 (2021-10-10)
66 |
67 | Maintenance release:
68 |
69 | * fix unbalanced code chunk delimiters in vignette (yihui/knitr#2057)
70 |
71 | # logger 0.2.1 (2021-07-06)
72 |
73 | Maintenance release:
74 |
75 | * update `appender_slack` to use `slackr_msg` instead of `text_slackr`
76 |
77 | # logger 0.2.0 (2021-03-03)
78 |
79 | ## Breaking changes
80 |
81 | * `appender_console` writes to `stderr` by default instead of `stdout` (#28)
82 |
83 | ## Fixes
84 |
85 | * default date format in `glue` layouts (#44, @burgikukac)
86 | * `fn` reference in loggers will not to a Cartesian join on the log lines and message, but merge (and clean up) the `fn` even for large anonymous functions (#20)
87 |
88 | ## New features
89 |
90 | * allow defining the log level threshold as a string (#13, @artemklevtsov)
91 | * allow updating the log level threshold, formatter, layout and appender in all namespaces with a single call (#50)
92 | * new argument to `appender_file` to optionally truncate before appending (#24, @eddelbuettel)
93 | * new arguments to `appender_file` to optionally rotate the log files after appending (#42)
94 | * new meta variables for logging in custom layouts: R version and calling package's version
95 | * improved performance by not evaluating arguments when the log record does not meet the log level threshold (#27, @jozefhajnala)
96 | * `logger` in now part of the Mikata Project: https://mikata.dev
97 |
98 | ## New helper functions
99 |
100 | * `%except%`: evaluate an expression with fallback
101 | * `log_separator`: logging with separator lines (#16)
102 | * `log_tictoc`: tic-toc logging (#16, @nfultz)
103 | * `log_failure`: log error before failing (#19, @amy17519)
104 | * `log_messages`, `log_warnings`, `log_errors`: optionally auto-log messages, warnings and errors using `globalCallingHandlers` on R 4.0.0 and above, and injecting `logger` calls to `message`, `warnings` and `stop` below R 4.0.0
105 | * `log_shiny_input_changes`: auto-log input changes in Shiny apps (#25)
106 |
107 | ## New formatter functions
108 |
109 | * `layout_pander`: transform R objects into markdown before logging (#22)
110 |
111 | ## New layout functions
112 |
113 | * `layout_blank`: blank log messages without any modification
114 | * `layout_json_parser`: render the layout as a JSON blob after merging with requested meta fields
115 |
116 | ## New appender functions
117 |
118 | * `appender_telegram`: deliver log records to Telegram (#14, @artemklevtsov)
119 | * `appender_syslog`: deliver log records to syslog (#30, @atheriel)
120 | * `appender_kinesis`: deliver log records to Amazon Kinesis (#35)
121 | * `appender_async`: wrapper function for other appender functions to deliver log records in a background process asynchronously without blocking the master process (#35)
122 |
123 | # logger 0.1 (2018-12-20)
124 |
125 | Initial CRAN release after collecting feedback for a month on Twitter at `https://twitter.com/daroczig/status/1067461632677330944`:
126 |
127 | * finalized design of a log request defined by
128 |
129 | * a log level `threshold`,
130 | * a `formatter` function preparing the log message,
131 | * a `layout` function rendering the actual log records and
132 | * an `appender` function delivering to the log destination
133 |
134 | * detailed documentation with 7 vignettes and a lot of examples, even some benchmarks
135 | * ~75% code coverage for unit tests
136 | * 5 `formatter` functions mostly using `paste`, `sprintf` and `glue`
137 | * 6 `layout` functions with convenient wrappers to let users define custom layouts via `glue` or `JSON`, including colorized output
138 | * 5 `appender` functions delivering log records to the console, files, Pushbullet and Slack
139 | * helper function to evaluate an expressions with auto-logging both the expression and its result
140 | * helper function to temporarily update the log level threshold
141 | * helper function to skip running the formatter function on a log message
142 | * mostly backward compatibly with the `logging` and `futile.logger` packages
143 |
--------------------------------------------------------------------------------
/R/color.R:
--------------------------------------------------------------------------------
1 | #' Color string by the related log level
2 | #'
3 | #' Color log messages according to their severity with either a rainbow
4 | #' or grayscale color scheme. The greyscale theme assumes a dark background on
5 | #' the terminal.
6 | #'
7 | #' @param msg String to color.
8 | #' @param level see [log_levels()]
9 | #' @return A string with ANSI escape codes.
10 | #' @export
11 | #' @examplesIf requireNamespace("crayon")
12 | #' cat(colorize_by_log_level("foobar", FATAL), "\n")
13 | #' cat(colorize_by_log_level("foobar", ERROR), "\n")
14 | #' cat(colorize_by_log_level("foobar", WARN), "\n")
15 | #' cat(colorize_by_log_level("foobar", SUCCESS), "\n")
16 | #' cat(colorize_by_log_level("foobar", INFO), "\n")
17 | #' cat(colorize_by_log_level("foobar", DEBUG), "\n")
18 | #' cat(colorize_by_log_level("foobar", TRACE), "\n")
19 | #'
20 | #' cat(grayscale_by_log_level("foobar", FATAL), "\n")
21 | #' cat(grayscale_by_log_level("foobar", ERROR), "\n")
22 | #' cat(grayscale_by_log_level("foobar", WARN), "\n")
23 | #' cat(grayscale_by_log_level("foobar", SUCCESS), "\n")
24 | #' cat(grayscale_by_log_level("foobar", INFO), "\n")
25 | #' cat(grayscale_by_log_level("foobar", DEBUG), "\n")
26 | #' cat(grayscale_by_log_level("foobar", TRACE), "\n")
27 | colorize_by_log_level <- function(msg, level) {
28 | fail_on_missing_package("crayon")
29 |
30 | color <- switch(attr(level, "level"),
31 | "FATAL" = crayon::combine_styles(crayon::bold, crayon::make_style("red1")),
32 | "ERROR" = crayon::make_style("red4"),
33 | "WARN" = crayon::make_style("darkorange"),
34 | "SUCCESS" = crayon::combine_styles(crayon::bold, crayon::make_style("green4")),
35 | "INFO" = crayon::reset,
36 | "DEBUG" = crayon::make_style("deepskyblue4"),
37 | "TRACE" = crayon::make_style("dodgerblue4"),
38 | stop("Unknown log level")
39 | )
40 |
41 | paste0(color(msg), crayon::reset(""))
42 | }
43 |
44 | #' @export
45 | #' @rdname colorize_by_log_level
46 | grayscale_by_log_level <- function(msg, level) {
47 | fail_on_missing_package("crayon")
48 |
49 | color <- switch(attr(level, "level"),
50 | "FATAL" = crayon::make_style("gray100"),
51 | "ERROR" = crayon::make_style("gray90"),
52 | "WARN" = crayon::make_style("gray80"),
53 | "SUCCESS" = crayon::make_style("gray70"),
54 | "INFO" = crayon::make_style("gray60"),
55 | "DEBUG" = crayon::make_style("gray50"),
56 | "TRACE" = crayon::make_style("gray40"),
57 | stop("Unknown log level")
58 | )
59 |
60 | paste0(color(msg), crayon::reset(""))
61 | }
62 |
--------------------------------------------------------------------------------
/R/hooks.R:
--------------------------------------------------------------------------------
1 | #' Injects a logger call to standard messages
2 | #'
3 | #' This function uses [trace()] to add a [log_info()] function call when
4 | #' `message` is called to log the informative messages with the
5 | #' `logger` layout and appender.
6 | #' @export
7 | #' @examples \dontrun{
8 | #' log_messages()
9 | #' message("hi there")
10 | #' }
11 | log_messages <- function() {
12 | if (any(sapply(
13 | globalCallingHandlers()[names(globalCallingHandlers()) == "message"],
14 | attr,
15 | which = "implements"
16 | ) == "log_messages")) {
17 | warning("Ignoring this call to log_messages as it was registered previously.")
18 | } else {
19 | globalCallingHandlers(
20 | message = structure(function(m) {
21 | logger::log_level(logger::INFO, m$message, .topcall = m$call)
22 | }, implements = "log_messages")
23 | )
24 | }
25 | }
26 |
27 |
28 | #' Injects a logger call to standard warnings
29 | #'
30 | #' This function uses `trace` to add a `log_warn` function call when
31 | #' `warning` is called to log the warning messages with the `logger`
32 | #' layout and appender.
33 | #' @param muffle if TRUE, the warning is not shown after being logged
34 | #' @export
35 | #' @examples \dontrun{
36 | #' log_warnings()
37 | #' for (i in 1:5) {
38 | #' Sys.sleep(runif(1))
39 | #' warning(i)
40 | #' }
41 | #' }
42 | log_warnings <- function(muffle = getOption("logger_muffle_warnings", FALSE)) {
43 | if (any(sapply(
44 | globalCallingHandlers()[names(globalCallingHandlers()) == "warning"],
45 | attr,
46 | which = "implements"
47 | ) == "log_warnings")) {
48 | warning("Ignoring this call to log_warnings as it was registered previously.")
49 | } else {
50 | globalCallingHandlers(
51 | warning = structure(function(m) {
52 | logger::log_level(logger::WARN, m$message, .topcall = m$call)
53 | if (isTRUE(muffle)) {
54 | invokeRestart("muffleWarning")
55 | }
56 | }, implements = "log_warnings")
57 | )
58 | }
59 | }
60 |
61 | #' Injects a logger call to standard errors
62 | #'
63 | #' This function uses [trace()] to add a [log_error()] function call when
64 | #' [stop()] is called to log the error messages with the `logger` layout
65 | #' and appender.
66 | #' @param muffle if TRUE, the error is not thrown after being logged
67 | #' @param traceback if TRUE the error traceback is logged along with the error
68 | #' message
69 | #' @export
70 | #' @examples \dontrun{
71 | #' log_errors()
72 | #' stop("foobar")
73 | #' }
74 | log_errors <- function(muffle = getOption("logger_muffle_errors", FALSE), traceback = FALSE) {
75 | if (any(sapply(
76 | globalCallingHandlers()[names(globalCallingHandlers()) == "error"],
77 | attr,
78 | which = "implements"
79 | ) == "log_errors")) {
80 | warning("Ignoring this call to log_errors as it was registered previously.")
81 | } else {
82 | globalCallingHandlers(
83 | error = structure(function(m) {
84 | logger::log_level(logger::ERROR, m$message, .topcall = m$call)
85 | if (traceback) {
86 | bt <- .traceback(3L)
87 | logger::log_level(logger::ERROR, "Traceback:", .topcall = m$call)
88 | for (i in seq_along(bt)) {
89 | msg <- paste0(length(bt) - i + 1L, ": ", bt[[i]])
90 | ref <- attr(bt[[i]], "srcref")
91 | file <- attr(ref, "srcfile")
92 | if (inherits(file, "srcfile")) {
93 | file <- basename(file$filename)
94 | line <- paste(unique(c(ref[1L], ref[3L])), collapse = "-")
95 | msg <- paste0(msg, " at ", file, " #", line)
96 | }
97 | logger::log_level(logger::ERROR, skip_formatter(msg), .topcall = m$call)
98 | }
99 | }
100 | if (isTRUE(muffle)) {
101 | invokeRestart("abort")
102 | }
103 | }, implements = "log_errors")
104 | )
105 | }
106 | }
107 |
108 |
109 | #' Auto logging input changes in Shiny app
110 | #'
111 | #' This is to be called in the `server` section of the Shiny app.
112 | #' @export
113 | #' @param input passed from Shiny's `server`
114 | #' @param level log level
115 | #' @param excluded_inputs character vector of input names to exclude from logging
116 | #' @param namespace the name of the namespace
117 | #' @importFrom utils assignInMyNamespace assignInNamespace
118 | #' @examples \dontrun{
119 | #' library(shiny)
120 | #'
121 | #' ui <- bootstrapPage(
122 | #' numericInput("mean", "mean", 0),
123 | #' numericInput("sd", "sd", 1),
124 | #' textInput("title", "title", "title"),
125 | #' textInput("foo", "This is not used at all, still gets logged", "foo"),
126 | #' passwordInput("password", "Password not to be logged", "secret"),
127 | #' plotOutput("plot")
128 | #' )
129 | #'
130 | #' server <- function(input, output) {
131 | #' logger::log_shiny_input_changes(input, excluded_inputs = "password")
132 | #'
133 | #' output$plot <- renderPlot({
134 | #' hist(rnorm(1e3, input$mean, input$sd), main = input$title)
135 | #' })
136 | #' }
137 | #'
138 | #' shinyApp(ui = ui, server = server)
139 | #' }
140 | log_shiny_input_changes <- function(input,
141 | level = INFO,
142 | namespace = NA_character_,
143 | excluded_inputs = character()) {
144 | fail_on_missing_package("shiny")
145 | fail_on_missing_package("jsonlite")
146 |
147 | session <- shiny::getDefaultReactiveDomain()
148 | ns <- ifelse(!is.null(session), session$ns(character(0)), "")
149 |
150 | if (!(shiny::isRunning() || inherits(session, "MockShinySession") || inherits(session, "session_proxy"))) {
151 | stop("No Shiny app running, it makes no sense to call this function outside of a Shiny app")
152 | }
153 |
154 | input_values <- shiny::isolate(shiny::reactiveValuesToList(input))
155 | assignInMyNamespace("shiny_input_values", input_values)
156 | log_level(level, skip_formatter(trimws(paste(
157 | ns,
158 | "Default Shiny inputs initialized:",
159 | as.character(jsonlite::toJSON(input_values, auto_unbox = TRUE))
160 | ))), namespace = namespace)
161 |
162 | shiny::observe({
163 | old_input_values <- shiny_input_values
164 | new_input_values <- shiny::reactiveValuesToList(input)
165 | names <- unique(c(names(old_input_values), names(new_input_values)))
166 | names <- setdiff(names, excluded_inputs)
167 | for (name in names) {
168 | old <- old_input_values[name]
169 | new <- new_input_values[name]
170 | if (!identical(old, new)) {
171 | message <- trimws("{ns} Shiny input change detected in {name}: {old} -> {new}")
172 | log_level(level, message, namespace = namespace)
173 | }
174 | }
175 | assignInNamespace("shiny_input_values", new_input_values, ns = "logger")
176 | })
177 | }
178 | shiny_input_values <- NULL
179 |
--------------------------------------------------------------------------------
/R/levels.R:
--------------------------------------------------------------------------------
1 | log_levels_supported <- c("OFF", "FATAL", "ERROR", "WARN", "SUCCESS", "INFO", "DEBUG", "TRACE")
2 |
3 | #' Log levels
4 | #'
5 | #' The standard Apache logj4 log levels plus a custom level for
6 | #' `SUCCESS`. For the full list of these log levels and suggested
7 | #' usage, check the below Details.
8 | #'
9 | #' List of supported log levels:
10 | #'
11 | #' * `OFF` No events will be logged
12 | #' * `FATAL` Severe error that will prevent the application from continuing
13 | #' * `ERROR` An error in the application, possibly recoverable
14 | #' * `WARN` An event that might possible lead to an error
15 | #' * `SUCCESS` An explicit success event above the INFO level that you want to log
16 | #' * `INFO` An event for informational purposes
17 | #' * `DEBUG` A general debugging event
18 | #' * `TRACE` A fine-grained debug message, typically capturing the flow through the application.
19 | #' @references ,
20 | #'
21 | #' @name log_levels
22 | NULL
23 |
24 | #' @rdname log_levels
25 | #' @export
26 | #' @format NULL
27 | OFF <- structure(0L, level = "OFF", class = c("loglevel", "integer"))
28 | #' @export
29 | #' @rdname log_levels
30 | #' @format NULL
31 | FATAL <- structure(100L, level = "FATAL", class = c("loglevel", "integer"))
32 | #' @export
33 | #' @rdname log_levels
34 | #' @format NULL
35 | ERROR <- structure(200L, level = "ERROR", class = c("loglevel", "integer"))
36 | #' @export
37 | #' @rdname log_levels
38 | #' @format NULL
39 | WARN <- structure(300L, level = "WARN", class = c("loglevel", "integer"))
40 | #' @export
41 | #' @rdname log_levels
42 | #' @format NULL
43 | SUCCESS <- structure(350L, level = "SUCCESS", class = c("loglevel", "integer"))
44 | #' @export
45 | #' @rdname log_levels
46 | #' @format NULL
47 | INFO <- structure(400L, level = "INFO", class = c("loglevel", "integer"))
48 | #' @export
49 | #' @rdname log_levels
50 | #' @format NULL
51 | DEBUG <- structure(500L, level = "DEBUG", class = c("loglevel", "integer"))
52 | #' @export
53 | #' @rdname log_levels
54 | #' @format NULL
55 | TRACE <- structure(600L, level = "TRACE", class = c("loglevel", "integer"))
56 |
57 | #' @export
58 | print.loglevel <- function(x, ...) {
59 | cat("Log level: ", attr(x, "level"), "\n", sep = "")
60 | }
61 |
62 |
63 | #' Convert R object into a logger log-level
64 | #' @param x string or integer
65 | #' @return pander log-level, e.g. `INFO`
66 | #' @export
67 | #' @examples
68 | #' as.loglevel(INFO)
69 | #' as.loglevel(400L)
70 | #' as.loglevel(400)
71 | as.loglevel <- function(x) { # nolint
72 | UseMethod("as.loglevel", x)
73 | }
74 |
75 |
76 | #' @export
77 | as.loglevel.default <- function(x) {
78 | stop(paste(
79 | "Do not know how to convert",
80 | shQuote(class(x)[1]),
81 | "to a logger log-level."
82 | ))
83 | }
84 |
85 |
86 | #' @export
87 | as.loglevel.character <- function(x) {
88 | stopifnot(
89 | length(x) == 1,
90 | x %in% log_levels_supported
91 | )
92 | getFromNamespace(x, "logger")
93 | }
94 |
95 |
96 | #' @export
97 | as.loglevel.integer <- function(x) {
98 | loglevels <- mget(log_levels_supported, envir = asNamespace("logger"))
99 | stopifnot(
100 | length(x) == 1,
101 | x %in% as.integer(loglevels)
102 | )
103 | loglevels[[which(loglevels == x)]]
104 | }
105 |
106 |
107 | #' @export
108 | as.loglevel.numeric <- as.loglevel.integer
109 |
--------------------------------------------------------------------------------
/R/logger-meta.R:
--------------------------------------------------------------------------------
1 | logger_meta_env <- function(log_level = NULL,
2 | namespace = NA_character_,
3 | .logcall = sys.call(),
4 | .topcall = sys.call(-1),
5 | .topenv = parent.frame(),
6 | .timestamp = Sys.time(),
7 | parent = emptyenv()) {
8 | env <- new.env(parent = parent)
9 | env$ns <- namespace
10 | env$ans <- fallback_namespace(namespace)
11 |
12 | force(.topcall)
13 | force(.topenv)
14 | delayedAssign("fn", deparse_to_one_line(.topcall[[1]]), assign.env = env)
15 | delayedAssign("call", deparse_to_one_line(.topcall), assign.env = env)
16 | delayedAssign("topenv", top_env_name(.topenv), assign.env = env)
17 | delayedAssign("location", log_call_location(.logcall), assign.env = env)
18 |
19 | env$time <- .timestamp
20 | env$levelr <- log_level
21 | env$level <- attr(log_level, "level")
22 |
23 | delayedAssign("pid", Sys.getpid(), assign.env = env)
24 |
25 | # R and ns package versions
26 | delayedAssign(
27 | "ns_pkg_version",
28 | tryCatch(as.character(packageVersion(namespace)), error = function(e) NA_character_),
29 | assign.env = env
30 | )
31 | delayedAssign("r_version", as.character(getRversion()), assign.env = env)
32 |
33 | # stuff from Sys.info
34 | delayedAssign(".sysinfo", Sys.info())
35 | delayedAssign("node", .sysinfo[["nodename"]], assign.env = env)
36 | delayedAssign("arch", .sysinfo[["machine"]], assign.env = env)
37 | delayedAssign("os_name", .sysinfo[["sysname"]], assign.env = env)
38 | delayedAssign("os_release", .sysinfo[["release"]], assign.env = env)
39 | delayedAssign("os_version", .sysinfo[["version"]], assign.env = env)
40 | delayedAssign("user", .sysinfo[["user"]], assign.env = env)
41 |
42 | env
43 | }
44 |
--------------------------------------------------------------------------------
/R/logger-package.R:
--------------------------------------------------------------------------------
1 | #' @keywords internal
2 | "_PACKAGE"
3 |
4 | ## usethis namespace: start
5 | ## usethis namespace: end
6 | NULL
7 |
--------------------------------------------------------------------------------
/R/try.R:
--------------------------------------------------------------------------------
1 | #' Try to evaluate an expressions and evaluate another expression on
2 | #' exception
3 | #' @param try R expression
4 | #' @param except fallback R expression to be evaluated if `try` fails
5 | #' @export
6 | #' @note Suppress log messages in the `except` namespace if you don't
7 | #' want to throw a `WARN` log message on the exception branch.
8 | #' @examples
9 | #' everything %except% 42
10 | #' everything <- "640kb"
11 | #' everything %except% 42
12 | #'
13 | #' FunDoesNotExist(1:10) %except% sum(1:10) / length(1:10)
14 | #' FunDoesNotExist(1:10) %except% (sum(1:10) / length(1:10))
15 | #' FunDoesNotExist(1:10) %except% MEAN(1:10) %except% mean(1:10)
16 | #' FunDoesNotExist(1:10) %except% (MEAN(1:10) %except% mean(1:10))
17 | `%except%` <- function(try, except) {
18 | ## Need to capture these in the evaluation frame of `%except%` but only want
19 | ## to do the work if there's an error
20 | delayedAssign("call", sys.call(-1))
21 | delayedAssign("env", parent.frame())
22 | delayedAssign("except_text", deparse(substitute(except)))
23 | delayedAssign("try_text", deparse(substitute(try)))
24 |
25 | tryCatch(try,
26 | error = function(e) {
27 | log_level(
28 | WARN,
29 | paste0("Running '", except_text, "' as '", try_text, "' failed: '", e$message, "'"),
30 | namespace = "except",
31 | .topcall = call,
32 | .topenv = env
33 | )
34 | except
35 | }
36 | )
37 | }
38 |
--------------------------------------------------------------------------------
/R/utils.R:
--------------------------------------------------------------------------------
1 | #' Check if R package can be loaded and fails loudly otherwise
2 | #' @param pkg string
3 | #' @param min_version optional minimum version needed
4 | #' @param call Call to include in error message.
5 | #' @export
6 | #' @importFrom utils packageVersion compareVersion
7 | #' @examples
8 | #' f <- function() fail_on_missing_package("foobar")
9 | #' try(f())
10 | #' g <- function() fail_on_missing_package("stats")
11 | #' g()
12 | fail_on_missing_package <- function(pkg, min_version, call = NULL) {
13 | pc <- call %||% sys.call(which = 1)
14 | if (!requireNamespace(pkg, quietly = TRUE)) {
15 | stop(
16 | sprintf(
17 | "Please install the '%s' package to use %s",
18 | pkg,
19 | deparse(pc[[1]])
20 | ),
21 | call. = FALSE
22 | )
23 | }
24 | if (!missing(min_version)) {
25 | if (compareVersion(min_version, as.character(packageVersion(pkg))) == 1) {
26 | stop(
27 | sprintf(
28 | "Please install min. %s version of %s to use %s",
29 | min_version,
30 | pkg,
31 | deparse(pc[[1]])
32 | ),
33 | call. = FALSE
34 | )
35 | }
36 | }
37 | }
38 |
39 |
40 | #' Returns the name of the top level environment from which the logger was called
41 | #' @return string
42 | #' @noRd
43 | #' @param .topenv call environment
44 | top_env_name <- function(.topenv = parent.frame()) {
45 | environmentName(topenv(.topenv))
46 | }
47 |
48 | #' Finds the location of the logger call (file and line)
49 | #' @return list with path and line element
50 | #' @noRd
51 | #' @param .logcall The call that emitted the log
52 | log_call_location <- function(.logcall) {
53 | call_string <- deparse(.logcall)
54 | loc <- list(
55 | path = "",
56 | line = ""
57 | )
58 | for (trace in .traceback(0)) {
59 | if (identical(call_string, as.vector(trace))) {
60 | ref <- attr(trace, "srcref")
61 | loc$line <- ref[1L]
62 | file <- attr(ref, "srcfile")
63 | if (!is.null(file)) {
64 | loc$path <- normalizePath(file$filename, winslash = "/")
65 | }
66 | break
67 | }
68 | }
69 | loc
70 | }
71 |
72 | #' Deparse and join all lines into a single line
73 | #'
74 | #' Calling `deparse` and joining all the returned lines into a
75 | #' single line, separated by whitespace, and then cleaning up all the
76 | #' duplicated whitespace (except for excessive whitespace in strings
77 | #' between single or double quotes).
78 | #' @param x object to `deparse`
79 | #' @return string
80 | #' @export
81 | deparse_to_one_line <- function(x) {
82 | gsub('\\s+(?=(?:[^\\\'"]*[\\\'"][^\\\'"]*[\\\'"])*[^\\\'"]*$)', " ",
83 | paste(deparse(x), collapse = " "),
84 | perl = TRUE
85 | )
86 | }
87 |
88 |
89 | #' Catch the log header
90 | #' @return string
91 | #' @param level see [log_levels()]
92 | #' @param namespace string
93 | #' @noRd
94 | #' @examples
95 | #' \dontshow{old <- logger:::namespaces_set()}
96 | #' catch_base_log(INFO, NA_character_)
97 | #' logger <- layout_glue_generator(format = "{node}/{pid}/{namespace}/{fn} {time} {level}: {msg}")
98 | #' log_layout(logger)
99 | #' catch_base_log(INFO, NA_character_)
100 | #' fun <- function() catch_base_log(INFO, NA_character_)
101 | #' fun()
102 | #' catch_base_log(INFO, NA_character_, .topcall = call("funLONG"))
103 | #' \dontshow{logger:::namespaces_set(old)}
104 | catch_base_log <- function(level, namespace, .topcall = sys.call(-1), .topenv = parent.frame()) {
105 | namespace <- fallback_namespace(namespace)
106 |
107 | old <- log_appender(appender_console, namespace = namespace)
108 | on.exit(log_appender(old, namespace = namespace))
109 |
110 | # catch error, warning or message
111 | capture.output(
112 | log_level(
113 | level = level,
114 | "",
115 | namespace = namespace,
116 | .topcall = .topcall,
117 | .topenv = .topenv
118 | ),
119 | type = "message"
120 | )
121 | }
122 |
123 | `%||%` <- function(x, y) {
124 | if (is.null(x)) y else x
125 | }
126 |
127 | in_pkgdown <- function() {
128 | identical(Sys.getenv("IN_PKGDOWN"), "true")
129 | }
130 |
131 | is_testing <- function() {
132 | identical(Sys.getenv("TESTTHAT"), "true")
133 | }
134 |
135 | is_checking_logger <- function() {
136 | Sys.getenv("_R_CHECK_PACKAGE_NAME_", "") == "logger"
137 | }
138 |
139 | needs_stdout <- function() {
140 | in_pkgdown() || is_testing() || is_checking_logger()
141 | }
142 |
143 | # allow mocking
144 | Sys.time <- NULL # nolint
145 | proc.time <- NULL # nolint
146 |
--------------------------------------------------------------------------------
/R/zzz.R:
--------------------------------------------------------------------------------
1 | ## init storage for all logger settings
2 | namespaces <- new.env(parent = emptyenv())
3 |
4 | .onLoad <- function(libname, pkgname) {
5 | namespaces_set(namespaces_default())
6 | }
7 |
8 | namespaces_reset <- function() {
9 | rm(list = ls(namespaces), envir = namespaces)
10 | namespaces_set(namespaces_default())
11 | }
12 |
13 | namespaces_default <- function() {
14 | has_glue <- requireNamespace("glue", quietly = TRUE)
15 |
16 | list(
17 | global = list(
18 | default = list(
19 | threshold = as.loglevel(Sys.getenv("LOGGER_LOG_LEVEL", unset = "INFO")),
20 | layout = layout_simple,
21 | formatter = if (has_glue) formatter_glue else formatter_sprintf,
22 | appender = if (needs_stdout()) appender_stdout else appender_console
23 | )
24 | ),
25 | .logger = list(
26 | default = list(
27 | threshold = ERROR,
28 | layout = layout_simple,
29 | formatter = formatter_sprintf,
30 | appender = if (needs_stdout()) appender_stdout else appender_console
31 | )
32 | )
33 | )
34 | }
35 |
36 | namespaces_set <- function(new = namespaces_default()) {
37 | old <- as.list(namespaces)
38 |
39 | rm(list = ls(namespaces), envir = namespaces)
40 | list2env(new, namespaces)
41 |
42 | invisible(old)
43 | }
44 |
45 | .onAttach <- function(libname, pkgname) {
46 | ## warn user about using sprintf instead of glue due to missing dependency
47 | if (!requireNamespace("glue", quietly = TRUE)) {
48 | packageStartupMessage(
49 | paste(
50 | 'logger: As the "glue" R package is not installed,',
51 | 'using "sprintf" as the default log message formatter instead of "glue".'
52 | )
53 | )
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/README.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | output: github_document
3 | ---
4 |
5 |
6 |
7 | ```{r, include = FALSE}
8 | knitr::opts_chunk$set(
9 | collapse = TRUE,
10 | comment = "#>",
11 | fig.path = "man/figures/README-",
12 | out.width = "100%"
13 | )
14 | ```
15 |
16 | # logger
17 |
18 |
19 | [](https://www.repostatus.org/#active) [](https://cran.r-project.org/package=logger) [](https://github.com/daroczig/logger/actions) [](https://app.codecov.io/gh/daroczig/logger) [](https://mikata.dev)
20 |
21 |
22 | A lightweight, modern and flexible logging utility for R -- heavily inspired by the `futile.logger` R package and `logging` Python module.
23 |
24 | ## Installation
25 |
26 | [](https://cran.r-project.org/package=logger)
27 |
28 | ```{r}
29 | #| eval: false
30 | install.packages("logger")
31 | ```
32 |
33 | The most recent, development version of `logger` can also be installed from GitHub:
34 |
35 | ```{r}
36 | #| eval: false
37 | # install.packages("pak")
38 | pak::pak("daroczig/logger")
39 | ```
40 |
41 | ## Quick example
42 |
43 | Setting the log level threshold to something low and logging various messages in ad-hoc and programmatic ways:
44 |
45 | ```{r}
46 | #| include: false
47 | library(logger)
48 | log_appender(appender_stdout)
49 | ```
50 |
51 | ```{r}
52 | library(logger)
53 | log_threshold(DEBUG)
54 | log_info("Script starting up...")
55 |
56 | pkgs <- available.packages()
57 | log_info("There are {nrow(pkgs)} R packages hosted on CRAN!")
58 |
59 | for (letter in letters) {
60 | lpkgs <- sum(grepl(letter, pkgs[, "Package"], ignore.case = TRUE))
61 | log_level(
62 | if (lpkgs < 5000) TRACE else DEBUG,
63 | "{lpkgs} R packages including the {shQuote(letter)} letter"
64 | )
65 | }
66 |
67 | log_warn("There might be many, like {1:2} or more warnings!!!")
68 | ```
69 |
70 | You can even use a custom log layout to render the log records with colors, as you can see in `layout_glue_colors()`:
71 |
72 |
73 |
74 | But you could set up any custom colors and layout, eg using custom colors only for the log levels, make it grayscale, include the calling function or R package namespace with specific colors etc. For more details, see `vignette("write_custom_extensions")`.
75 |
76 | ## Related work
77 |
78 | There are many other logging packages available on CRAN:
79 |
80 | - [`futile.logger`](https://cran.r-project.org/package=futile.logger): probably the most popular `log4j` variant (and I'm a big fan)
81 | - [`logging`](https://cran.r-project.org/package=logging): just like Python's `logging` package
82 | - [`lgr`](https://cran.r-project.org/package=lgr): built on top of R6.
83 | - [`loggit`](https://cran.r-project.org/package=loggit): capture `message`, `warning` and `stop` function messages in a JSON file
84 | - [`log4r`](https://cran.r-project.org/package=log4r): `log4j`-based, object-oriented logger
85 | - [`rsyslog`](https://cran.r-project.org/package=rsyslog): logging to `syslog` on 'POSIX'-compatible operating systems
86 | - [`lumberjack`](https://cran.r-project.org/package=lumberjack): provides a special operator to log changes in data
87 |
88 | Why use logger? I decided to write the `n+1`th extensible `log4j` logger that fits my liking --- and hopefully yours as well --- with the aim to:
89 |
90 | - Keep it close to `log4j`.
91 | - Respect the modern function/variable naming conventions and general R coding style.
92 | - By default, rely on `glue()` when it comes to formatting / rendering log messages, but keep it flexible if others prefer `sprintf()` (e.g. for performance reasons) or other functions.
93 | - Support vectorization (eg passing a vector to be logged on multiple lines).
94 | - Make it easy to extend with new features (e.g. custom layouts, message formats and output).
95 | - Prepare for writing to various services, streams etc
96 | - Provide support for namespaces, preferably automatically finding and creating a custom namespace for all R packages writing log messages, each with optionally configurable log level threshold, message and output formats.
97 | - Allow stacking loggers to implement logger hierarchy -- even within a namespace, so that the very same `log` call can write all the `TRACE` log messages to the console, while only pushing `ERROR`s to DataDog and eg `INFO` messages to CloudWatch.
98 | - Optionally colorize log message based on the log level.
99 | - Make logging fun!
100 |
101 | Welcome to the [Bazaar](https://en.wikipedia.org/wiki/The_Cathedral_and_the_Bazaar)! If you already use any of the above packages for logging, you might find `vignette("migration")` useful.
102 |
103 | ## Interested in more details?
104 |
105 | ::: .pkgdown-hide
106 |
107 | Check out the main documentation site at or the vignettes on the below topics:
108 |
109 | * [Introduction to logger](https://daroczig.github.io/logger/articles/Intro.html)
110 | * [The Anatomy of a Log Request](https://daroczig.github.io/logger/articles/anatomy.html)
111 | * [Customizing the Format and the Destination of a Log Record](https://daroczig.github.io/logger/articles/customize_logger.html)
112 | * [Writing Custom Logger Extensions](https://daroczig.github.io/logger/articles/write_custom_extensions.html)
113 | * [Migration Guide from other logging packages](https://daroczig.github.io/logger/articles/migration.html)
114 | * [Logging from R Packages](https://daroczig.github.io/logger/articles/r_packages.html)
115 | * [Simple Benchmarks on Performance](https://daroczig.github.io/logger/articles/performance.html)
116 |
117 | :::
118 |
119 | If you prefer visual content, you can watch the video recording of the "Getting things logged" talk at RStudio::conf(2020):
120 |
121 | [](https://www.youtube.com/watch?v=_rUuBbml9dU)
122 |
--------------------------------------------------------------------------------
/_pkgdown.yml:
--------------------------------------------------------------------------------
1 | url: https://daroczig.github.io/logger
2 |
3 | template:
4 | bootstrap: 5
5 |
6 | authors:
7 | System1:
8 | href: https://system1.com
9 | html:
10 | toc:
11 | depth: 3
12 |
13 | reference:
14 |
15 | - title: Key logging functions
16 | contents:
17 | - log_level
18 | - TRACE
19 | - log_threshold
20 |
21 | - title: Other logging helpers
22 | contents:
23 | - log_eval
24 | - log_failure
25 | - log_tictoc
26 | - log_elapsed
27 | - log_separator
28 | - log_with_separator
29 | - with_log_threshold
30 | - log_chunk_time
31 |
32 | - title: Appenders
33 | desc: >
34 | Log appenders define where logging output should be sent to.
35 | contents:
36 | - log_appender
37 | - starts_with("appender_")
38 |
39 | - title: Formatters
40 | desc: >
41 | Log formatters control how the inputs to the `log_` functions are converted
42 | to a string. The default is `formatter_glue()` when `glue` is installed.
43 | contents:
44 | - log_formatter
45 | - starts_with("formatter_")
46 | - skip_formatter
47 |
48 | - title: Layouts
49 | desc: >
50 | Logging layouts control what is sent to the appender. They always include
51 | the logged string, but might also include the timestamp, log level, etc.
52 | contents:
53 | - log_layout
54 | - starts_with("layout_")
55 |
56 | - title: Hooks for automated logging
57 | contents:
58 | - log_shiny_input_changes
59 | - log_messages
60 | - log_warnings
61 | - log_errors
62 |
63 | - title: Other helpers
64 | contents:
65 | - colorize_by_log_level
66 | - logger
67 | - delete_logger_index
68 | - "%except%"
69 |
70 | - title: Dev tools
71 | contents:
72 | - as.loglevel
73 | - deparse_to_one_line
74 | - fail_on_missing_package
75 | - get_logger_meta_variables
76 | - log_namespaces
77 | - log_indices
78 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | comment: false
2 |
3 | coverage:
4 | status:
5 | project:
6 | default:
7 | target: auto
8 | threshold: 1%
9 | informational: true
10 | patch:
11 | default:
12 | target: auto
13 | threshold: 1%
14 | informational: true
15 |
--------------------------------------------------------------------------------
/inst/demo-packages/logger-tester-package/DESCRIPTION:
--------------------------------------------------------------------------------
1 | Type: Package
2 | Package: logger.tester
3 | Authors@R: c(
4 | person("Gergely", "Daroczi", , "daroczig@rapporter.net", role = c("aut", "cre")),
5 | person("System1", role = c("fnd"))
6 | )
7 | Title: Dummy package providing testing functions for logger
8 | Description: Dummy package providing testing functions for logger
9 | Version: 0.1
10 | Date: 2018-07-04
11 | URL: https://github.com/daroczig/logger
12 | RoxygenNote: 7.3.2
13 | License: MIT + file LICENSE
14 | Imports:
15 | logger
16 |
--------------------------------------------------------------------------------
/inst/demo-packages/logger-tester-package/LICENSE:
--------------------------------------------------------------------------------
1 | YEAR: 2024
2 | COPYRIGHT HOLDER: logger authors
3 |
--------------------------------------------------------------------------------
/inst/demo-packages/logger-tester-package/NAMESPACE:
--------------------------------------------------------------------------------
1 | # Generated by roxygen2: do not edit by hand
2 |
3 | export(logger_info_tester_function)
4 | export(logger_tester_function)
5 | importFrom(logger,log_info)
6 | importFrom(logger,log_level)
7 |
--------------------------------------------------------------------------------
/inst/demo-packages/logger-tester-package/R/tester.R:
--------------------------------------------------------------------------------
1 | #' Testing logging from package
2 | #' @param level foo
3 | #' @param msg bar
4 | #' @export
5 | #' @importFrom logger log_level
6 | logger_tester_function <- function(level, msg) {
7 | set.seed(1014)
8 | x <- runif(1)
9 | log_level(level, "{msg} {x}")
10 | }
11 |
12 | #' Testing logging INFO from package
13 | #' @param msg bar
14 | #' @export
15 | #' @importFrom logger log_info
16 | logger_info_tester_function <- function(msg) {
17 | everything <- 42
18 | log_info("{msg} {everything}")
19 | }
20 |
--------------------------------------------------------------------------------
/inst/demo-packages/logger-tester-package/man/logger_info_tester_function.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tester.R
3 | \name{logger_info_tester_function}
4 | \alias{logger_info_tester_function}
5 | \title{Testing logging INFO from package}
6 | \usage{
7 | logger_info_tester_function(msg)
8 | }
9 | \arguments{
10 | \item{msg}{bar}
11 | }
12 | \description{
13 | Testing logging INFO from package
14 | }
15 |
--------------------------------------------------------------------------------
/inst/demo-packages/logger-tester-package/man/logger_tester_function.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tester.R
3 | \name{logger_tester_function}
4 | \alias{logger_tester_function}
5 | \title{Testing logging from package}
6 | \usage{
7 | logger_tester_function(level, msg)
8 | }
9 | \arguments{
10 | \item{level}{foo}
11 |
12 | \item{msg}{bar}
13 | }
14 | \description{
15 | Testing logging from package
16 | }
17 |
--------------------------------------------------------------------------------
/man/appender_async.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/appenders.R
3 | \name{appender_async}
4 | \alias{appender_async}
5 | \title{Delays executing the actual appender function to the future in a
6 | background process to avoid blocking the main R session}
7 | \usage{
8 | appender_async(
9 | appender,
10 | namespace = "async_logger",
11 | init = function() log_info("Background process started")
12 | )
13 | }
14 | \arguments{
15 | \item{appender}{a \code{\link[=log_appender]{log_appender()}} function with a \code{generator}
16 | attribute (TODO note not required, all fn will be passed if
17 | not)}
18 |
19 | \item{namespace}{\code{logger} namespace to use for logging messages on
20 | starting up the background process}
21 |
22 | \item{init}{optional function to run in the background process that
23 | is useful to set up the environment required for logging, eg if
24 | the \code{appender} function requires some extra packages to be
25 | loaded or some environment variables to be set etc}
26 | }
27 | \value{
28 | function taking \code{lines} argument
29 | }
30 | \description{
31 | Delays executing the actual appender function to the future in a
32 | background process to avoid blocking the main R session
33 | }
34 | \note{
35 | This functionality depends on the \pkg{mirai} package.
36 | }
37 | \examples{
38 | \dontrun{
39 | appender_file_slow <- function(file) {
40 | force(file)
41 | function(lines) {
42 | Sys.sleep(1)
43 | cat(lines, sep = "\n", file = file, append = TRUE)
44 | }
45 | }
46 |
47 | ## log what's happening in the background
48 | log_threshold(TRACE, namespace = "async_logger")
49 | log_appender(appender_console, namespace = "async_logger")
50 |
51 | ## start async appender
52 | t <- tempfile()
53 | log_info("Logging in the background to {t}")
54 |
55 | ## use async appender
56 | log_appender(appender_async(appender_file_slow(file = t)))
57 | log_info("Was this slow?")
58 | system.time(for (i in 1:25) log_info(i))
59 |
60 | readLines(t)
61 | Sys.sleep(10)
62 | readLines(t)
63 |
64 | }
65 | }
66 | \seealso{
67 | Other log_appenders:
68 | \code{\link{appender_console}()},
69 | \code{\link{appender_file}()},
70 | \code{\link{appender_kinesis}()},
71 | \code{\link{appender_pushbullet}()},
72 | \code{\link{appender_slack}()},
73 | \code{\link{appender_stdout}()},
74 | \code{\link{appender_syslog}()},
75 | \code{\link{appender_tee}()},
76 | \code{\link{appender_telegram}()}
77 | }
78 | \concept{log_appenders}
79 |
--------------------------------------------------------------------------------
/man/appender_console.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/appenders.R
3 | \name{appender_console}
4 | \alias{appender_console}
5 | \alias{appender_stderr}
6 | \title{Append log record to stderr}
7 | \usage{
8 | appender_console(lines)
9 |
10 | appender_stderr(lines)
11 | }
12 | \arguments{
13 | \item{lines}{character vector}
14 | }
15 | \description{
16 | Append log record to stderr
17 | }
18 | \seealso{
19 | Other log_appenders:
20 | \code{\link{appender_async}()},
21 | \code{\link{appender_file}()},
22 | \code{\link{appender_kinesis}()},
23 | \code{\link{appender_pushbullet}()},
24 | \code{\link{appender_slack}()},
25 | \code{\link{appender_stdout}()},
26 | \code{\link{appender_syslog}()},
27 | \code{\link{appender_tee}()},
28 | \code{\link{appender_telegram}()}
29 | }
30 | \concept{log_appenders}
31 |
--------------------------------------------------------------------------------
/man/appender_file.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/appenders.R
3 | \name{appender_file}
4 | \alias{appender_file}
5 | \title{Append log messages to a file}
6 | \usage{
7 | appender_file(
8 | file,
9 | append = TRUE,
10 | max_lines = Inf,
11 | max_bytes = Inf,
12 | max_files = 1L
13 | )
14 | }
15 | \arguments{
16 | \item{file}{path}
17 |
18 | \item{append}{boolean passed to \code{cat} defining if the file should
19 | be overwritten with the most recent log message instead of
20 | appending}
21 |
22 | \item{max_lines}{numeric specifying the maximum number of lines
23 | allowed in a file before rotating}
24 |
25 | \item{max_bytes}{numeric specifying the maximum number of bytes
26 | allowed in a file before rotating}
27 |
28 | \item{max_files}{integer specifying the maximum number of files to
29 | be used in rotation}
30 | }
31 | \value{
32 | function taking \code{lines} argument
33 | }
34 | \description{
35 | Log messages are written to a file with basic log rotation: when
36 | max number of lines or bytes is defined to be other than \code{Inf},
37 | then the log file is renamed with a \code{.1} suffix and a new log file
38 | is created. The renaming happens recursively (eg \code{logfile.1}
39 | renamed to \code{logfile.2}) until the specified \code{max_files}, then the
40 | oldest file (\code{logfile.{max_files-1}}) is deleted.
41 | }
42 | \examples{
43 | \dontshow{old <- logger:::namespaces_set()}
44 | ## ##########################################################################
45 | ## simple example logging to a file
46 | t <- tempfile()
47 | log_appender(appender_file(t))
48 | for (i in 1:25) log_info(i)
49 | readLines(t)
50 |
51 | ## ##########################################################################
52 | ## more complex example of logging to file
53 | ## rotated after every 3rd line up to max 5 files
54 |
55 | ## create a folder storing the log files
56 | t <- tempfile()
57 | dir.create(t)
58 | f <- file.path(t, "log")
59 |
60 | ## define the file logger with log rotation enabled
61 | log_appender(appender_file(f, max_lines = 3, max_files = 5L))
62 |
63 | ## enable internal logging to see what's actually happening in the logrotate steps
64 | log_threshold(TRACE, namespace = ".logger")
65 | ## log 25 messages
66 | for (i in 1:25) log_info(i)
67 |
68 | ## see what was logged
69 | lapply(list.files(t, full.names = TRUE), function(t) {
70 | cat("\n##", t, "\n")
71 | cat(readLines(t), sep = "\n")
72 | })
73 |
74 | \dontshow{logger:::namespaces_set(old)}
75 | }
76 | \seealso{
77 | Other log_appenders:
78 | \code{\link{appender_async}()},
79 | \code{\link{appender_console}()},
80 | \code{\link{appender_kinesis}()},
81 | \code{\link{appender_pushbullet}()},
82 | \code{\link{appender_slack}()},
83 | \code{\link{appender_stdout}()},
84 | \code{\link{appender_syslog}()},
85 | \code{\link{appender_tee}()},
86 | \code{\link{appender_telegram}()}
87 | }
88 | \concept{log_appenders}
89 |
--------------------------------------------------------------------------------
/man/appender_kinesis.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/appenders.R
3 | \name{appender_kinesis}
4 | \alias{appender_kinesis}
5 | \title{Send log messages to a Amazon Kinesis stream}
6 | \usage{
7 | appender_kinesis(stream)
8 | }
9 | \arguments{
10 | \item{stream}{name of the Kinesis stream}
11 | }
12 | \value{
13 | function taking \code{lines} and optional \code{partition_key}
14 | argument
15 | }
16 | \description{
17 | Send log messages to a Amazon Kinesis stream
18 | }
19 | \note{
20 | This functionality depends on the \pkg{botor} package.
21 | }
22 | \seealso{
23 | Other log_appenders:
24 | \code{\link{appender_async}()},
25 | \code{\link{appender_console}()},
26 | \code{\link{appender_file}()},
27 | \code{\link{appender_pushbullet}()},
28 | \code{\link{appender_slack}()},
29 | \code{\link{appender_stdout}()},
30 | \code{\link{appender_syslog}()},
31 | \code{\link{appender_tee}()},
32 | \code{\link{appender_telegram}()}
33 | }
34 | \concept{log_appenders}
35 |
--------------------------------------------------------------------------------
/man/appender_pushbullet.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/appenders.R
3 | \name{appender_pushbullet}
4 | \alias{appender_pushbullet}
5 | \title{Send log messages to Pushbullet}
6 | \usage{
7 | appender_pushbullet(...)
8 | }
9 | \arguments{
10 | \item{...}{parameters passed to \link[RPushbullet:pbPost]{RPushbullet::pbPost}, such as \code{recipients}
11 | or \code{apikey}, although it's probably much better to set all these
12 | in the \verb{~/.rpushbullet.json} as per package docs at
13 | \url{http://dirk.eddelbuettel.com/code/rpushbullet.html}}
14 | }
15 | \description{
16 | Send log messages to Pushbullet
17 | }
18 | \note{
19 | This functionality depends on the \pkg{RPushbullet} package.
20 | }
21 | \seealso{
22 | Other log_appenders:
23 | \code{\link{appender_async}()},
24 | \code{\link{appender_console}()},
25 | \code{\link{appender_file}()},
26 | \code{\link{appender_kinesis}()},
27 | \code{\link{appender_slack}()},
28 | \code{\link{appender_stdout}()},
29 | \code{\link{appender_syslog}()},
30 | \code{\link{appender_tee}()},
31 | \code{\link{appender_telegram}()}
32 | }
33 | \concept{log_appenders}
34 |
--------------------------------------------------------------------------------
/man/appender_slack.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/appenders.R
3 | \name{appender_slack}
4 | \alias{appender_slack}
5 | \title{Send log messages to a Slack channel}
6 | \usage{
7 | appender_slack(
8 | channel = Sys.getenv("SLACK_CHANNEL"),
9 | username = Sys.getenv("SLACK_USERNAME"),
10 | icon_emoji = Sys.getenv("SLACK_ICON_EMOJI"),
11 | api_token = Sys.getenv("SLACK_API_TOKEN"),
12 | preformatted = TRUE
13 | )
14 | }
15 | \arguments{
16 | \item{channel}{Slack channel name with a hashtag prefix for public
17 | channel and no prefix for private channels}
18 |
19 | \item{username}{Slack (bot) username}
20 |
21 | \item{icon_emoji}{optional override for the bot icon}
22 |
23 | \item{api_token}{Slack API token}
24 |
25 | \item{preformatted}{use code tags around the message?}
26 | }
27 | \value{
28 | function taking \code{lines} argument
29 | }
30 | \description{
31 | Send log messages to a Slack channel
32 | }
33 | \note{
34 | This functionality depends on the \pkg{slackr} package.
35 | }
36 | \seealso{
37 | Other log_appenders:
38 | \code{\link{appender_async}()},
39 | \code{\link{appender_console}()},
40 | \code{\link{appender_file}()},
41 | \code{\link{appender_kinesis}()},
42 | \code{\link{appender_pushbullet}()},
43 | \code{\link{appender_stdout}()},
44 | \code{\link{appender_syslog}()},
45 | \code{\link{appender_tee}()},
46 | \code{\link{appender_telegram}()}
47 | }
48 | \concept{log_appenders}
49 |
--------------------------------------------------------------------------------
/man/appender_stdout.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/appenders.R
3 | \name{appender_stdout}
4 | \alias{appender_stdout}
5 | \title{Append log record to stdout}
6 | \usage{
7 | appender_stdout(lines)
8 | }
9 | \arguments{
10 | \item{lines}{character vector}
11 | }
12 | \description{
13 | Append log record to stdout
14 | }
15 | \seealso{
16 | Other log_appenders:
17 | \code{\link{appender_async}()},
18 | \code{\link{appender_console}()},
19 | \code{\link{appender_file}()},
20 | \code{\link{appender_kinesis}()},
21 | \code{\link{appender_pushbullet}()},
22 | \code{\link{appender_slack}()},
23 | \code{\link{appender_syslog}()},
24 | \code{\link{appender_tee}()},
25 | \code{\link{appender_telegram}()}
26 | }
27 | \concept{log_appenders}
28 |
--------------------------------------------------------------------------------
/man/appender_syslog.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/appenders.R
3 | \name{appender_syslog}
4 | \alias{appender_syslog}
5 | \title{Send log messages to the POSIX system log}
6 | \usage{
7 | appender_syslog(identifier, ...)
8 | }
9 | \arguments{
10 | \item{identifier}{A string identifying the process.}
11 |
12 | \item{...}{Further arguments passed on to \code{\link[rsyslog:syslog]{rsyslog::open_syslog()}}.}
13 | }
14 | \value{
15 | function taking \code{lines} argument
16 | }
17 | \description{
18 | Send log messages to the POSIX system log
19 | }
20 | \note{
21 | This functionality depends on the \pkg{rsyslog} package.
22 | }
23 | \examples{
24 | \dontrun{
25 | if (requireNamespace("rsyslog", quietly = TRUE)) {
26 | log_appender(appender_syslog("test"))
27 | log_info("Test message.")
28 | }
29 | }
30 | }
31 | \seealso{
32 | Other log_appenders:
33 | \code{\link{appender_async}()},
34 | \code{\link{appender_console}()},
35 | \code{\link{appender_file}()},
36 | \code{\link{appender_kinesis}()},
37 | \code{\link{appender_pushbullet}()},
38 | \code{\link{appender_slack}()},
39 | \code{\link{appender_stdout}()},
40 | \code{\link{appender_tee}()},
41 | \code{\link{appender_telegram}()}
42 | }
43 | \concept{log_appenders}
44 |
--------------------------------------------------------------------------------
/man/appender_syslognet.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/appenders.R
3 | \name{appender_syslognet}
4 | \alias{appender_syslognet}
5 | \title{Send log messages to a network syslog server}
6 | \usage{
7 | appender_syslognet(identifier, server, port = 601L)
8 | }
9 | \arguments{
10 | \item{identifier}{program/function identification (string).}
11 |
12 | \item{server}{machine where syslog daemon runs (string).}
13 |
14 | \item{port}{port where syslog daemon listens (integer).}
15 | }
16 | \value{
17 | A function taking a \code{lines} argument.
18 | }
19 | \description{
20 | Send log messages to a network syslog server
21 | }
22 | \note{
23 | This functionality depends on the \pkg{syslognet} package.
24 | }
25 | \examples{
26 | \dontrun{
27 | if (requireNamespace("syslognet", quietly = TRUE)) {
28 | log_appender(appender_syslognet("test_app", "remoteserver"))
29 | log_info("Test message.")
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/man/appender_tee.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/appenders.R
3 | \name{appender_tee}
4 | \alias{appender_tee}
5 | \title{Append log messages to a file and stdout as well}
6 | \usage{
7 | appender_tee(
8 | file,
9 | append = TRUE,
10 | max_lines = Inf,
11 | max_bytes = Inf,
12 | max_files = 1L
13 | )
14 | }
15 | \arguments{
16 | \item{file}{path}
17 |
18 | \item{append}{boolean passed to \code{cat} defining if the file should
19 | be overwritten with the most recent log message instead of
20 | appending}
21 |
22 | \item{max_lines}{numeric specifying the maximum number of lines
23 | allowed in a file before rotating}
24 |
25 | \item{max_bytes}{numeric specifying the maximum number of bytes
26 | allowed in a file before rotating}
27 |
28 | \item{max_files}{integer specifying the maximum number of files to
29 | be used in rotation}
30 | }
31 | \value{
32 | function taking \code{lines} argument
33 | }
34 | \description{
35 | This appends log messages to both console and a file. The same
36 | rotation options are available as in \code{\link[=appender_file]{appender_file()}}.
37 | }
38 | \seealso{
39 | Other log_appenders:
40 | \code{\link{appender_async}()},
41 | \code{\link{appender_console}()},
42 | \code{\link{appender_file}()},
43 | \code{\link{appender_kinesis}()},
44 | \code{\link{appender_pushbullet}()},
45 | \code{\link{appender_slack}()},
46 | \code{\link{appender_stdout}()},
47 | \code{\link{appender_syslog}()},
48 | \code{\link{appender_telegram}()}
49 | }
50 | \concept{log_appenders}
51 |
--------------------------------------------------------------------------------
/man/appender_telegram.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/appenders.R
3 | \name{appender_telegram}
4 | \alias{appender_telegram}
5 | \title{Send log messages to a Telegram chat}
6 | \usage{
7 | appender_telegram(
8 | chat_id = Sys.getenv("TELEGRAM_CHAT_ID"),
9 | bot_token = Sys.getenv("TELEGRAM_BOT_TOKEN"),
10 | parse_mode = NULL
11 | )
12 | }
13 | \arguments{
14 | \item{chat_id}{Unique identifier for the target chat or username of
15 | the target channel (in the format @channelusername)}
16 |
17 | \item{bot_token}{Telegram Authorization token}
18 |
19 | \item{parse_mode}{Message parse mode. Allowed values: Markdown or
20 | HTML}
21 | }
22 | \value{
23 | function taking \code{lines} argument
24 | }
25 | \description{
26 | Send log messages to a Telegram chat
27 | }
28 | \note{
29 | This functionality depends on the \pkg{telegram} package.
30 | }
31 | \seealso{
32 | Other log_appenders:
33 | \code{\link{appender_async}()},
34 | \code{\link{appender_console}()},
35 | \code{\link{appender_file}()},
36 | \code{\link{appender_kinesis}()},
37 | \code{\link{appender_pushbullet}()},
38 | \code{\link{appender_slack}()},
39 | \code{\link{appender_stdout}()},
40 | \code{\link{appender_syslog}()},
41 | \code{\link{appender_tee}()}
42 | }
43 | \concept{log_appenders}
44 |
--------------------------------------------------------------------------------
/man/appender_void.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/appenders.R
3 | \name{appender_void}
4 | \alias{appender_void}
5 | \title{Dummy appender not delivering the log record to anywhere}
6 | \usage{
7 | appender_void(lines)
8 | }
9 | \arguments{
10 | \item{lines}{character vector}
11 | }
12 | \description{
13 | Dummy appender not delivering the log record to anywhere
14 | }
15 |
--------------------------------------------------------------------------------
/man/as.loglevel.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/levels.R
3 | \name{as.loglevel}
4 | \alias{as.loglevel}
5 | \title{Convert R object into a logger log-level}
6 | \usage{
7 | as.loglevel(x)
8 | }
9 | \arguments{
10 | \item{x}{string or integer}
11 | }
12 | \value{
13 | pander log-level, e.g. \code{INFO}
14 | }
15 | \description{
16 | Convert R object into a logger log-level
17 | }
18 | \examples{
19 | as.loglevel(INFO)
20 | as.loglevel(400L)
21 | as.loglevel(400)
22 | }
23 |
--------------------------------------------------------------------------------
/man/colorize_by_log_level.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/color.R
3 | \name{colorize_by_log_level}
4 | \alias{colorize_by_log_level}
5 | \alias{grayscale_by_log_level}
6 | \title{Color string by the related log level}
7 | \usage{
8 | colorize_by_log_level(msg, level)
9 |
10 | grayscale_by_log_level(msg, level)
11 | }
12 | \arguments{
13 | \item{msg}{String to color.}
14 |
15 | \item{level}{see \code{\link[=log_levels]{log_levels()}}}
16 | }
17 | \value{
18 | A string with ANSI escape codes.
19 | }
20 | \description{
21 | Color log messages according to their severity with either a rainbow
22 | or grayscale color scheme. The greyscale theme assumes a dark background on
23 | the terminal.
24 | }
25 | \examples{
26 | \dontshow{if (requireNamespace("crayon")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
27 | cat(colorize_by_log_level("foobar", FATAL), "\n")
28 | cat(colorize_by_log_level("foobar", ERROR), "\n")
29 | cat(colorize_by_log_level("foobar", WARN), "\n")
30 | cat(colorize_by_log_level("foobar", SUCCESS), "\n")
31 | cat(colorize_by_log_level("foobar", INFO), "\n")
32 | cat(colorize_by_log_level("foobar", DEBUG), "\n")
33 | cat(colorize_by_log_level("foobar", TRACE), "\n")
34 |
35 | cat(grayscale_by_log_level("foobar", FATAL), "\n")
36 | cat(grayscale_by_log_level("foobar", ERROR), "\n")
37 | cat(grayscale_by_log_level("foobar", WARN), "\n")
38 | cat(grayscale_by_log_level("foobar", SUCCESS), "\n")
39 | cat(grayscale_by_log_level("foobar", INFO), "\n")
40 | cat(grayscale_by_log_level("foobar", DEBUG), "\n")
41 | cat(grayscale_by_log_level("foobar", TRACE), "\n")
42 | \dontshow{\}) # examplesIf}
43 | }
44 |
--------------------------------------------------------------------------------
/man/delete_logger_index.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/logger.R
3 | \name{delete_logger_index}
4 | \alias{delete_logger_index}
5 | \title{Delete an index from a logger namespace}
6 | \usage{
7 | delete_logger_index(namespace = "global", index)
8 | }
9 | \arguments{
10 | \item{namespace}{logger namespace}
11 |
12 | \item{index}{index of the logger within the namespace}
13 | }
14 | \description{
15 | Delete an index from a logger namespace
16 | }
17 |
--------------------------------------------------------------------------------
/man/deparse_to_one_line.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{deparse_to_one_line}
4 | \alias{deparse_to_one_line}
5 | \title{Deparse and join all lines into a single line}
6 | \usage{
7 | deparse_to_one_line(x)
8 | }
9 | \arguments{
10 | \item{x}{object to \code{deparse}}
11 | }
12 | \value{
13 | string
14 | }
15 | \description{
16 | Calling \code{deparse} and joining all the returned lines into a
17 | single line, separated by whitespace, and then cleaning up all the
18 | duplicated whitespace (except for excessive whitespace in strings
19 | between single or double quotes).
20 | }
21 |
--------------------------------------------------------------------------------
/man/fail_on_missing_package.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{fail_on_missing_package}
4 | \alias{fail_on_missing_package}
5 | \title{Check if R package can be loaded and fails loudly otherwise}
6 | \usage{
7 | fail_on_missing_package(pkg, min_version, call = NULL)
8 | }
9 | \arguments{
10 | \item{pkg}{string}
11 |
12 | \item{min_version}{optional minimum version needed}
13 |
14 | \item{call}{Call to include in error message.}
15 | }
16 | \description{
17 | Check if R package can be loaded and fails loudly otherwise
18 | }
19 | \examples{
20 | f <- function() fail_on_missing_package("foobar")
21 | try(f())
22 | g <- function() fail_on_missing_package("stats")
23 | g()
24 | }
25 |
--------------------------------------------------------------------------------
/man/figures/colors.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/daroczig/logger/9adf2c4e697140e12dab86c78b53f85b8fb79b95/man/figures/colors.png
--------------------------------------------------------------------------------
/man/figures/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/daroczig/logger/9adf2c4e697140e12dab86c78b53f85b8fb79b95/man/figures/logo.png
--------------------------------------------------------------------------------
/man/formatter_cli.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/formatters.R
3 | \name{formatter_cli}
4 | \alias{formatter_cli}
5 | \title{Apply \code{\link[cli:cli_text]{cli::cli_text()}} to format string with cli syntax}
6 | \usage{
7 | formatter_cli(
8 | ...,
9 | .logcall = sys.call(),
10 | .topcall = sys.call(-1),
11 | .topenv = parent.frame()
12 | )
13 | }
14 | \arguments{
15 | \item{...}{passed to \code{\link[cli:cli_text]{cli::cli_text()}} for the text interpolation}
16 |
17 | \item{.logcall}{the logging call being evaluated (useful in
18 | formatters and layouts when you want to have access to the raw,
19 | unevaluated R expression)}
20 |
21 | \item{.topcall}{R expression from which the logging function was
22 | called (useful in formatters and layouts to extract the calling
23 | function's name or arguments)}
24 |
25 | \item{.topenv}{original frame of the \code{.topcall} calling function
26 | where the formatter function will be evaluated and that is used
27 | to look up the \code{namespace} as well via \code{logger:::top_env_name}}
28 | }
29 | \value{
30 | character vector
31 | }
32 | \description{
33 | Apply \code{\link[cli:cli_text]{cli::cli_text()}} to format string with cli syntax
34 | }
35 | \seealso{
36 | Other log_formatters:
37 | \code{\link{formatter_glue}()},
38 | \code{\link{formatter_glue_or_sprintf}()},
39 | \code{\link{formatter_glue_safe}()},
40 | \code{\link{formatter_json}()},
41 | \code{\link{formatter_logging}()},
42 | \code{\link{formatter_pander}()},
43 | \code{\link{formatter_paste}()},
44 | \code{\link{formatter_sprintf}()}
45 | }
46 | \concept{log_formatters}
47 |
--------------------------------------------------------------------------------
/man/formatter_glue.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/formatters.R
3 | \name{formatter_glue}
4 | \alias{formatter_glue}
5 | \title{Apply \code{\link[glue:glue]{glue::glue()}} to convert R objects into a character vector}
6 | \usage{
7 | formatter_glue(
8 | ...,
9 | .logcall = sys.call(),
10 | .topcall = sys.call(-1),
11 | .topenv = parent.frame()
12 | )
13 | }
14 | \arguments{
15 | \item{...}{passed to \code{\link[glue:glue]{glue::glue()}} for the text interpolation}
16 |
17 | \item{.logcall}{the logging call being evaluated (useful in
18 | formatters and layouts when you want to have access to the raw,
19 | unevaluated R expression)}
20 |
21 | \item{.topcall}{R expression from which the logging function was
22 | called (useful in formatters and layouts to extract the calling
23 | function's name or arguments)}
24 |
25 | \item{.topenv}{original frame of the \code{.topcall} calling function
26 | where the formatter function will be evaluated and that is used
27 | to look up the \code{namespace} as well via \code{logger:::top_env_name}}
28 | }
29 | \value{
30 | character vector
31 | }
32 | \description{
33 | Apply \code{\link[glue:glue]{glue::glue()}} to convert R objects into a character vector
34 | }
35 | \note{
36 | Although this is the default log message formatter function,
37 | but when \pkg{glue} is not installed, \code{\link[=formatter_sprintf]{formatter_sprintf()}}
38 | will be used as a fallback.
39 | }
40 | \seealso{
41 | Other log_formatters:
42 | \code{\link{formatter_cli}()},
43 | \code{\link{formatter_glue_or_sprintf}()},
44 | \code{\link{formatter_glue_safe}()},
45 | \code{\link{formatter_json}()},
46 | \code{\link{formatter_logging}()},
47 | \code{\link{formatter_pander}()},
48 | \code{\link{formatter_paste}()},
49 | \code{\link{formatter_sprintf}()}
50 | }
51 | \concept{log_formatters}
52 |
--------------------------------------------------------------------------------
/man/formatter_glue_or_sprintf.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/formatters.R
3 | \name{formatter_glue_or_sprintf}
4 | \alias{formatter_glue_or_sprintf}
5 | \title{Apply \code{\link[glue:glue]{glue::glue()}} and \code{\link[=sprintf]{sprintf()}}}
6 | \usage{
7 | formatter_glue_or_sprintf(
8 | msg,
9 | ...,
10 | .logcall = sys.call(),
11 | .topcall = sys.call(-1),
12 | .topenv = parent.frame()
13 | )
14 | }
15 | \arguments{
16 | \item{msg}{passed to \code{\link[=sprintf]{sprintf()}} as \code{fmt} or handled as part of \code{...}
17 | in \code{\link[glue:glue]{glue::glue()}}}
18 |
19 | \item{...}{passed to \code{\link[glue:glue]{glue::glue()}} for the text interpolation}
20 |
21 | \item{.logcall}{the logging call being evaluated (useful in
22 | formatters and layouts when you want to have access to the raw,
23 | unevaluated R expression)}
24 |
25 | \item{.topcall}{R expression from which the logging function was
26 | called (useful in formatters and layouts to extract the calling
27 | function's name or arguments)}
28 |
29 | \item{.topenv}{original frame of the \code{.topcall} calling function
30 | where the formatter function will be evaluated and that is used
31 | to look up the \code{namespace} as well via \code{logger:::top_env_name}}
32 | }
33 | \value{
34 | character vector
35 | }
36 | \description{
37 | The best of both words: using both formatter functions in your log
38 | messages, which can be useful eg if you are migrating from
39 | \code{\link[=sprintf]{sprintf()}} formatted log messages to \code{\link[glue:glue]{glue::glue()}} or similar.
40 | }
41 | \details{
42 | Note that this function tries to be smart when passing arguments to
43 | \code{\link[glue:glue]{glue::glue()}} and \code{\link[=sprintf]{sprintf()}}, but might fail with some edge cases, and
44 | returns an unformatted string.
45 | }
46 | \examples{
47 | formatter_glue_or_sprintf("{a} + {b} = \%s", a = 2, b = 3, 5)
48 | formatter_glue_or_sprintf("{pi} * {2} = \%s", pi * 2)
49 | formatter_glue_or_sprintf("{pi} * {2} = {pi*2}")
50 |
51 | formatter_glue_or_sprintf("Hi ", "{c('foo', 'bar')}, did you know that 2*4={2*4}")
52 | formatter_glue_or_sprintf("Hi {c('foo', 'bar')}, did you know that 2*4={2*4}")
53 | formatter_glue_or_sprintf("Hi {c('foo', 'bar')}, did you know that 2*4=\%s", 2 * 4)
54 | formatter_glue_or_sprintf("Hi \%s, did you know that 2*4={2*4}", c("foo", "bar"))
55 | formatter_glue_or_sprintf("Hi \%s, did you know that 2*4=\%s", c("foo", "bar"), 2 * 4)
56 | }
57 | \seealso{
58 | Other log_formatters:
59 | \code{\link{formatter_cli}()},
60 | \code{\link{formatter_glue}()},
61 | \code{\link{formatter_glue_safe}()},
62 | \code{\link{formatter_json}()},
63 | \code{\link{formatter_logging}()},
64 | \code{\link{formatter_pander}()},
65 | \code{\link{formatter_paste}()},
66 | \code{\link{formatter_sprintf}()}
67 | }
68 | \concept{log_formatters}
69 |
--------------------------------------------------------------------------------
/man/formatter_glue_safe.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/formatters.R
3 | \name{formatter_glue_safe}
4 | \alias{formatter_glue_safe}
5 | \title{Apply \code{\link[glue:glue_safe]{glue::glue_safe()}} to convert R objects into a character vector}
6 | \usage{
7 | formatter_glue_safe(
8 | ...,
9 | .logcall = sys.call(),
10 | .topcall = sys.call(-1),
11 | .topenv = parent.frame()
12 | )
13 | }
14 | \arguments{
15 | \item{...}{passed to \code{\link[glue:glue_safe]{glue::glue_safe()}} for the text interpolation}
16 |
17 | \item{.logcall}{the logging call being evaluated (useful in
18 | formatters and layouts when you want to have access to the raw,
19 | unevaluated R expression)}
20 |
21 | \item{.topcall}{R expression from which the logging function was
22 | called (useful in formatters and layouts to extract the calling
23 | function's name or arguments)}
24 |
25 | \item{.topenv}{original frame of the \code{.topcall} calling function
26 | where the formatter function will be evaluated and that is used
27 | to look up the \code{namespace} as well via \code{logger:::top_env_name}}
28 | }
29 | \value{
30 | character vector
31 | }
32 | \description{
33 | Apply \code{\link[glue:glue_safe]{glue::glue_safe()}} to convert R objects into a character vector
34 | }
35 | \seealso{
36 | Other log_formatters:
37 | \code{\link{formatter_cli}()},
38 | \code{\link{formatter_glue}()},
39 | \code{\link{formatter_glue_or_sprintf}()},
40 | \code{\link{formatter_json}()},
41 | \code{\link{formatter_logging}()},
42 | \code{\link{formatter_pander}()},
43 | \code{\link{formatter_paste}()},
44 | \code{\link{formatter_sprintf}()}
45 | }
46 | \concept{log_formatters}
47 |
--------------------------------------------------------------------------------
/man/formatter_json.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/formatters.R
3 | \name{formatter_json}
4 | \alias{formatter_json}
5 | \title{Transforms all passed R objects into a JSON list}
6 | \usage{
7 | formatter_json(
8 | ...,
9 | .logcall = sys.call(),
10 | .topcall = sys.call(-1),
11 | .topenv = parent.frame()
12 | )
13 | }
14 | \arguments{
15 | \item{...}{passed to \code{toJSON} wrapped into a \code{list}}
16 |
17 | \item{.logcall}{the logging call being evaluated (useful in
18 | formatters and layouts when you want to have access to the raw,
19 | unevaluated R expression)}
20 |
21 | \item{.topcall}{R expression from which the logging function was
22 | called (useful in formatters and layouts to extract the calling
23 | function's name or arguments)}
24 |
25 | \item{.topenv}{original frame of the \code{.topcall} calling function
26 | where the formatter function will be evaluated and that is used
27 | to look up the \code{namespace} as well via \code{logger:::top_env_name}}
28 | }
29 | \value{
30 | character vector
31 | }
32 | \description{
33 | Transforms all passed R objects into a JSON list
34 | }
35 | \note{
36 | This functionality depends on the \pkg{jsonlite} package.
37 | }
38 | \examples{
39 | \dontshow{old <- logger:::namespaces_set()}
40 | log_formatter(formatter_json)
41 | log_layout(layout_json_parser())
42 | log_info(everything = 42)
43 | log_info(mtcars = mtcars, species = iris$Species)
44 | \dontshow{logger:::namespaces_set(old)}
45 | }
46 | \seealso{
47 | Other log_formatters:
48 | \code{\link{formatter_cli}()},
49 | \code{\link{formatter_glue}()},
50 | \code{\link{formatter_glue_or_sprintf}()},
51 | \code{\link{formatter_glue_safe}()},
52 | \code{\link{formatter_logging}()},
53 | \code{\link{formatter_pander}()},
54 | \code{\link{formatter_paste}()},
55 | \code{\link{formatter_sprintf}()}
56 | }
57 | \concept{log_formatters}
58 |
--------------------------------------------------------------------------------
/man/formatter_logging.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/formatters.R
3 | \name{formatter_logging}
4 | \alias{formatter_logging}
5 | \title{Mimic the default formatter used in the \pkg{logging} package}
6 | \usage{
7 | formatter_logging(
8 | ...,
9 | .logcall = sys.call(),
10 | .topcall = sys.call(-1),
11 | .topenv = parent.frame()
12 | )
13 | }
14 | \arguments{
15 | \item{...}{string and further params passed to \code{\link[=sprintf]{sprintf()}} or R
16 | expressions to be evaluated}
17 |
18 | \item{.logcall}{the logging call being evaluated (useful in
19 | formatters and layouts when you want to have access to the raw,
20 | unevaluated R expression)}
21 |
22 | \item{.topcall}{R expression from which the logging function was
23 | called (useful in formatters and layouts to extract the calling
24 | function's name or arguments)}
25 |
26 | \item{.topenv}{original frame of the \code{.topcall} calling function
27 | where the formatter function will be evaluated and that is used
28 | to look up the \code{namespace} as well via \code{logger:::top_env_name}}
29 | }
30 | \value{
31 | character vector
32 | }
33 | \description{
34 | The \pkg{logging} package uses a formatter that behaves differently
35 | when the input is a string or other R object. If the first argument
36 | is a string, then \code{\link[=sprintf]{sprintf()}} is being called -- otherwise it does
37 | something like \code{\link[=log_eval]{log_eval()}} and logs the R expression(s) and the
38 | result(s) as well.
39 | }
40 | \examples{
41 | \dontshow{old <- logger:::namespaces_set()}
42 | log_formatter(formatter_logging)
43 | log_info("42")
44 | log_info(42)
45 | log_info(4 + 2)
46 | log_info("foo \%s", "bar")
47 | log_info("vector \%s", 1:3)
48 | log_info(12, 1 + 1, 2 * 2)
49 | \dontshow{logger:::namespaces_set(old)}
50 | }
51 | \seealso{
52 | Other log_formatters:
53 | \code{\link{formatter_cli}()},
54 | \code{\link{formatter_glue}()},
55 | \code{\link{formatter_glue_or_sprintf}()},
56 | \code{\link{formatter_glue_safe}()},
57 | \code{\link{formatter_json}()},
58 | \code{\link{formatter_pander}()},
59 | \code{\link{formatter_paste}()},
60 | \code{\link{formatter_sprintf}()}
61 | }
62 | \concept{log_formatters}
63 |
--------------------------------------------------------------------------------
/man/formatter_pander.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/formatters.R
3 | \name{formatter_pander}
4 | \alias{formatter_pander}
5 | \title{Formats R objects with pander}
6 | \usage{
7 | formatter_pander(
8 | x,
9 | ...,
10 | .logcall = sys.call(),
11 | .topcall = sys.call(-1),
12 | .topenv = parent.frame()
13 | )
14 | }
15 | \arguments{
16 | \item{x}{object to be logged}
17 |
18 | \item{...}{optional parameters passed to \code{pander}}
19 |
20 | \item{.logcall}{the logging call being evaluated (useful in
21 | formatters and layouts when you want to have access to the raw,
22 | unevaluated R expression)}
23 |
24 | \item{.topcall}{R expression from which the logging function was
25 | called (useful in formatters and layouts to extract the calling
26 | function's name or arguments)}
27 |
28 | \item{.topenv}{original frame of the \code{.topcall} calling function
29 | where the formatter function will be evaluated and that is used
30 | to look up the \code{namespace} as well via \code{logger:::top_env_name}}
31 | }
32 | \value{
33 | character vector
34 | }
35 | \description{
36 | Formats R objects with pander
37 | }
38 | \note{
39 | This functionality depends on the \pkg{pander} package.
40 | }
41 | \examples{
42 | \dontshow{old <- logger:::namespaces_set()}
43 | log_formatter(formatter_pander)
44 | log_info("42")
45 | log_info(42)
46 | log_info(4 + 2)
47 | log_info(head(iris))
48 | log_info(head(iris), style = "simple")
49 | log_info(lm(hp ~ wt, mtcars))
50 | \dontshow{logger:::namespaces_set(old)}
51 | }
52 | \seealso{
53 | Other log_formatters:
54 | \code{\link{formatter_cli}()},
55 | \code{\link{formatter_glue}()},
56 | \code{\link{formatter_glue_or_sprintf}()},
57 | \code{\link{formatter_glue_safe}()},
58 | \code{\link{formatter_json}()},
59 | \code{\link{formatter_logging}()},
60 | \code{\link{formatter_paste}()},
61 | \code{\link{formatter_sprintf}()}
62 | }
63 | \concept{log_formatters}
64 |
--------------------------------------------------------------------------------
/man/formatter_paste.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/formatters.R
3 | \name{formatter_paste}
4 | \alias{formatter_paste}
5 | \title{Concatenate R objects into a character vector via \code{paste}}
6 | \usage{
7 | formatter_paste(
8 | ...,
9 | .logcall = sys.call(),
10 | .topcall = sys.call(-1),
11 | .topenv = parent.frame()
12 | )
13 | }
14 | \arguments{
15 | \item{...}{passed to \code{paste}}
16 |
17 | \item{.logcall}{the logging call being evaluated (useful in
18 | formatters and layouts when you want to have access to the raw,
19 | unevaluated R expression)}
20 |
21 | \item{.topcall}{R expression from which the logging function was
22 | called (useful in formatters and layouts to extract the calling
23 | function's name or arguments)}
24 |
25 | \item{.topenv}{original frame of the \code{.topcall} calling function
26 | where the formatter function will be evaluated and that is used
27 | to look up the \code{namespace} as well via \code{logger:::top_env_name}}
28 | }
29 | \value{
30 | character vector
31 | }
32 | \description{
33 | Concatenate R objects into a character vector via \code{paste}
34 | }
35 | \seealso{
36 | Other log_formatters:
37 | \code{\link{formatter_cli}()},
38 | \code{\link{formatter_glue}()},
39 | \code{\link{formatter_glue_or_sprintf}()},
40 | \code{\link{formatter_glue_safe}()},
41 | \code{\link{formatter_json}()},
42 | \code{\link{formatter_logging}()},
43 | \code{\link{formatter_pander}()},
44 | \code{\link{formatter_sprintf}()}
45 | }
46 | \concept{log_formatters}
47 |
--------------------------------------------------------------------------------
/man/formatter_sprintf.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/formatters.R
3 | \name{formatter_sprintf}
4 | \alias{formatter_sprintf}
5 | \title{Apply \code{\link[=sprintf]{sprintf()}} to convert R objects into a character vector}
6 | \usage{
7 | formatter_sprintf(
8 | fmt,
9 | ...,
10 | .logcall = sys.call(),
11 | .topcall = sys.call(-1),
12 | .topenv = parent.frame()
13 | )
14 | }
15 | \arguments{
16 | \item{fmt}{passed to \code{\link[=sprintf]{sprintf()}}}
17 |
18 | \item{...}{passed to \code{\link[=sprintf]{sprintf()}}}
19 |
20 | \item{.logcall}{the logging call being evaluated (useful in
21 | formatters and layouts when you want to have access to the raw,
22 | unevaluated R expression)}
23 |
24 | \item{.topcall}{R expression from which the logging function was
25 | called (useful in formatters and layouts to extract the calling
26 | function's name or arguments)}
27 |
28 | \item{.topenv}{original frame of the \code{.topcall} calling function
29 | where the formatter function will be evaluated and that is used
30 | to look up the \code{namespace} as well via \code{logger:::top_env_name}}
31 | }
32 | \value{
33 | character vector
34 | }
35 | \description{
36 | Apply \code{\link[=sprintf]{sprintf()}} to convert R objects into a character vector
37 | }
38 | \seealso{
39 | Other log_formatters:
40 | \code{\link{formatter_cli}()},
41 | \code{\link{formatter_glue}()},
42 | \code{\link{formatter_glue_or_sprintf}()},
43 | \code{\link{formatter_glue_safe}()},
44 | \code{\link{formatter_json}()},
45 | \code{\link{formatter_logging}()},
46 | \code{\link{formatter_pander}()},
47 | \code{\link{formatter_paste}()}
48 | }
49 | \concept{log_formatters}
50 |
--------------------------------------------------------------------------------
/man/get_logger_meta_variables.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/layouts.R
3 | \name{get_logger_meta_variables}
4 | \alias{get_logger_meta_variables}
5 | \title{Collect useful information about the logging environment to be used in log messages}
6 | \usage{
7 | get_logger_meta_variables(
8 | log_level = NULL,
9 | namespace = NA_character_,
10 | .logcall = sys.call(),
11 | .topcall = sys.call(-1),
12 | .topenv = parent.frame(),
13 | .timestamp = Sys.time()
14 | )
15 | }
16 | \arguments{
17 | \item{log_level}{log level as per \code{\link[=log_levels]{log_levels()}}}
18 |
19 | \item{namespace}{string referring to the \code{logger} environment /
20 | config to be used to override the target of the message record to
21 | be used instead of the default namespace, which is defined by the
22 | R package name from which the logger was called, and falls back
23 | to a common, global namespace.}
24 |
25 | \item{.logcall}{the logging call being evaluated (useful in
26 | formatters and layouts when you want to have access to the raw,
27 | unevaluated R expression)}
28 |
29 | \item{.topcall}{R expression from which the logging function was
30 | called (useful in formatters and layouts to extract the calling
31 | function's name or arguments)}
32 |
33 | \item{.topenv}{original frame of the \code{.topcall} calling function
34 | where the formatter function will be evaluated and that is used
35 | to look up the \code{namespace} as well via \code{logger:::top_env_name}}
36 |
37 | \item{.timestamp}{The time the logging occured. Defaults to the current time
38 | but may be overwritten if the logging is delayed from the time it happend}
39 | }
40 | \value{
41 | list
42 | }
43 | \description{
44 | Available variables to be used in the log formatter functions, eg in \code{\link[=layout_glue_generator]{layout_glue_generator()}}:
45 | }
46 | \details{
47 | \itemize{
48 | \item \code{levelr}: log level as an R object, eg \code{\link[=INFO]{INFO()}}
49 | \item \code{level}: log level as a string, eg \code{\link[=INFO]{INFO()}}
50 | \item \code{time}: current time as \code{POSIXct}
51 | \item \code{node}: name by which the machine is known on the network as reported by \code{Sys.info}
52 | \item \code{arch}: machine type, typically the CPU architecture
53 | \item \code{os_name}: Operating System's name
54 | \item \code{os_release}: Operating System's release
55 | \item \code{os_version}: Operating System's version
56 | \item \code{user}: name of the real user id as reported by \code{Sys.info}
57 | \item \code{pid}: the process identification number of the R session
58 | \item \code{node}: name by which the machine is known on the network as reported by \code{Sys.info}
59 | \item \code{r_version}: R's major and minor version as a string
60 | \item \code{ns}: namespace usually defaults to \code{global} or the name of the holding R package
61 | of the calling the logging function
62 | \item \code{ns_pkg_version}: the version of \code{ns} when it's a package
63 | \item \code{ans}: same as \code{ns} if there's a defined \code{\link[=logger]{logger()}} for the namespace,
64 | otherwise a fallback namespace (eg usually \code{global})
65 | \item \code{topenv}: the name of the top environment from which the parent call was called
66 | (eg R package name or \code{GlobalEnv})
67 | \item \code{call}: parent call (if any) calling the logging function
68 | \item \code{location}: A list with element \code{path} and \code{line} giving the location of the
69 | log call
70 | \item \code{fn}: function's (if any) name calling the logging function
71 | }
72 | }
73 | \seealso{
74 | \code{\link[=layout_glue_generator]{layout_glue_generator()}}
75 |
76 | Other log_layouts:
77 | \code{\link{layout_blank}()},
78 | \code{\link{layout_glue}()},
79 | \code{\link{layout_glue_colors}()},
80 | \code{\link{layout_glue_generator}()},
81 | \code{\link{layout_json}()},
82 | \code{\link{layout_json_parser}()},
83 | \code{\link{layout_logging}()},
84 | \code{\link{layout_simple}()}
85 | }
86 | \concept{log_layouts}
87 |
--------------------------------------------------------------------------------
/man/grapes-except-grapes.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/try.R
3 | \name{\%except\%}
4 | \alias{\%except\%}
5 | \title{Try to evaluate an expressions and evaluate another expression on
6 | exception}
7 | \usage{
8 | try \%except\% except
9 | }
10 | \arguments{
11 | \item{try}{R expression}
12 |
13 | \item{except}{fallback R expression to be evaluated if \code{try} fails}
14 | }
15 | \description{
16 | Try to evaluate an expressions and evaluate another expression on
17 | exception
18 | }
19 | \note{
20 | Suppress log messages in the \code{except} namespace if you don't
21 | want to throw a \code{WARN} log message on the exception branch.
22 | }
23 | \examples{
24 | everything \%except\% 42
25 | everything <- "640kb"
26 | everything \%except\% 42
27 |
28 | FunDoesNotExist(1:10) \%except\% sum(1:10) / length(1:10)
29 | FunDoesNotExist(1:10) \%except\% (sum(1:10) / length(1:10))
30 | FunDoesNotExist(1:10) \%except\% MEAN(1:10) \%except\% mean(1:10)
31 | FunDoesNotExist(1:10) \%except\% (MEAN(1:10) \%except\% mean(1:10))
32 | }
33 |
--------------------------------------------------------------------------------
/man/layout_blank.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/layouts.R
3 | \name{layout_blank}
4 | \alias{layout_blank}
5 | \title{Format a log record by including the raw message without anything
6 | added or modified}
7 | \usage{
8 | layout_blank(
9 | level,
10 | msg,
11 | namespace = NA_character_,
12 | .logcall = sys.call(),
13 | .topcall = sys.call(-1),
14 | .topenv = parent.frame(),
15 | .timestamp = Sys.time()
16 | )
17 | }
18 | \arguments{
19 | \item{level}{log level, see \code{\link[=log_levels]{log_levels()}} for more details}
20 |
21 | \item{msg}{string message}
22 |
23 | \item{namespace}{string referring to the \code{logger} environment /
24 | config to be used to override the target of the message record to
25 | be used instead of the default namespace, which is defined by the
26 | R package name from which the logger was called, and falls back
27 | to a common, global namespace.}
28 |
29 | \item{.logcall}{the logging call being evaluated (useful in
30 | formatters and layouts when you want to have access to the raw,
31 | unevaluated R expression)}
32 |
33 | \item{.topcall}{R expression from which the logging function was
34 | called (useful in formatters and layouts to extract the calling
35 | function's name or arguments)}
36 |
37 | \item{.topenv}{original frame of the \code{.topcall} calling function
38 | where the formatter function will be evaluated and that is used
39 | to look up the \code{namespace} as well via \code{logger:::top_env_name}}
40 |
41 | \item{.timestamp}{The time the logging occured. Defaults to the current time
42 | but may be overwritten if the logging is delayed from the time it happend}
43 | }
44 | \value{
45 | character vector
46 | }
47 | \description{
48 | Format a log record by including the raw message without anything
49 | added or modified
50 | }
51 | \seealso{
52 | Other log_layouts:
53 | \code{\link{get_logger_meta_variables}()},
54 | \code{\link{layout_glue}()},
55 | \code{\link{layout_glue_colors}()},
56 | \code{\link{layout_glue_generator}()},
57 | \code{\link{layout_json}()},
58 | \code{\link{layout_json_parser}()},
59 | \code{\link{layout_logging}()},
60 | \code{\link{layout_simple}()}
61 | }
62 | \concept{log_layouts}
63 |
--------------------------------------------------------------------------------
/man/layout_glue.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/layouts.R
3 | \name{layout_glue}
4 | \alias{layout_glue}
5 | \title{Format a log message with \code{\link[glue:glue]{glue::glue()}}}
6 | \usage{
7 | layout_glue(
8 | level,
9 | msg,
10 | namespace = NA_character_,
11 | .logcall = sys.call(),
12 | .topcall = sys.call(-1),
13 | .topenv = parent.frame(),
14 | .timestamp = Sys.time()
15 | )
16 | }
17 | \arguments{
18 | \item{level}{log level, see \code{\link[=log_levels]{log_levels()}} for more details}
19 |
20 | \item{msg}{string message}
21 |
22 | \item{namespace}{string referring to the \code{logger} environment /
23 | config to be used to override the target of the message record to
24 | be used instead of the default namespace, which is defined by the
25 | R package name from which the logger was called, and falls back
26 | to a common, global namespace.}
27 |
28 | \item{.logcall}{the logging call being evaluated (useful in
29 | formatters and layouts when you want to have access to the raw,
30 | unevaluated R expression)}
31 |
32 | \item{.topcall}{R expression from which the logging function was
33 | called (useful in formatters and layouts to extract the calling
34 | function's name or arguments)}
35 |
36 | \item{.topenv}{original frame of the \code{.topcall} calling function
37 | where the formatter function will be evaluated and that is used
38 | to look up the \code{namespace} as well via \code{logger:::top_env_name}}
39 |
40 | \item{.timestamp}{The time the logging occured. Defaults to the current time
41 | but may be overwritten if the logging is delayed from the time it happend}
42 | }
43 | \value{
44 | character vector
45 | }
46 | \description{
47 | By default, this layout includes the log level of the log record as
48 | per \code{\link[=log_levels]{log_levels()}}, the current timestamp and the actual log
49 | message -- that you can override via calling
50 | \code{\link[=layout_glue_generator]{layout_glue_generator()}} directly. For colorized output, see
51 | \code{\link[=layout_glue_colors]{layout_glue_colors()}}.
52 | }
53 | \seealso{
54 | Other log_layouts:
55 | \code{\link{get_logger_meta_variables}()},
56 | \code{\link{layout_blank}()},
57 | \code{\link{layout_glue_colors}()},
58 | \code{\link{layout_glue_generator}()},
59 | \code{\link{layout_json}()},
60 | \code{\link{layout_json_parser}()},
61 | \code{\link{layout_logging}()},
62 | \code{\link{layout_simple}()}
63 | }
64 | \concept{log_layouts}
65 |
--------------------------------------------------------------------------------
/man/layout_glue_colors.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/layouts.R
3 | \name{layout_glue_colors}
4 | \alias{layout_glue_colors}
5 | \title{Format a log message with \code{\link[glue:glue]{glue::glue()}} and ANSI escape codes to add colors}
6 | \usage{
7 | layout_glue_colors(
8 | level,
9 | msg,
10 | namespace = NA_character_,
11 | .logcall = sys.call(),
12 | .topcall = sys.call(-1),
13 | .topenv = parent.frame(),
14 | .timestamp = Sys.time()
15 | )
16 | }
17 | \arguments{
18 | \item{level}{log level, see \code{\link[=log_levels]{log_levels()}} for more details}
19 |
20 | \item{msg}{string message}
21 |
22 | \item{namespace}{string referring to the \code{logger} environment /
23 | config to be used to override the target of the message record to
24 | be used instead of the default namespace, which is defined by the
25 | R package name from which the logger was called, and falls back
26 | to a common, global namespace.}
27 |
28 | \item{.logcall}{the logging call being evaluated (useful in
29 | formatters and layouts when you want to have access to the raw,
30 | unevaluated R expression)}
31 |
32 | \item{.topcall}{R expression from which the logging function was
33 | called (useful in formatters and layouts to extract the calling
34 | function's name or arguments)}
35 |
36 | \item{.topenv}{original frame of the \code{.topcall} calling function
37 | where the formatter function will be evaluated and that is used
38 | to look up the \code{namespace} as well via \code{logger:::top_env_name}}
39 |
40 | \item{.timestamp}{The time the logging occured. Defaults to the current time
41 | but may be overwritten if the logging is delayed from the time it happend}
42 | }
43 | \value{
44 | character vector
45 | }
46 | \description{
47 | Colour log levels based on their severity. Log levels are coloured
48 | with \code{\link[=colorize_by_log_level]{colorize_by_log_level()}} and the messages are coloured with
49 | \code{\link[=grayscale_by_log_level]{grayscale_by_log_level()}}.
50 | }
51 | \note{
52 | This functionality depends on the \pkg{crayon} package.
53 | }
54 | \examples{
55 | \dontshow{if (requireNamespace("crayon")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
56 | log_layout(layout_glue_colors)
57 | log_threshold(TRACE)
58 | log_info("Starting the script...")
59 | log_debug("This is the second line")
60 | log_trace("That is being placed right after the first one.")
61 | log_warn("Some errors might come!")
62 | log_error("This is a problem")
63 | log_debug("Getting an error is usually bad")
64 | log_error("This is another problem")
65 | log_fatal("The last problem.")
66 | \dontshow{\}) # examplesIf}
67 | }
68 | \seealso{
69 | Other log_layouts:
70 | \code{\link{get_logger_meta_variables}()},
71 | \code{\link{layout_blank}()},
72 | \code{\link{layout_glue}()},
73 | \code{\link{layout_glue_generator}()},
74 | \code{\link{layout_json}()},
75 | \code{\link{layout_json_parser}()},
76 | \code{\link{layout_logging}()},
77 | \code{\link{layout_simple}()}
78 | }
79 | \concept{log_layouts}
80 |
--------------------------------------------------------------------------------
/man/layout_glue_generator.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/layouts.R
3 | \name{layout_glue_generator}
4 | \alias{layout_glue_generator}
5 | \title{Generate log layout function using common variables available via glue syntax}
6 | \usage{
7 | layout_glue_generator(
8 | format = "{level} [{format(time, \\"\%Y-\%m-\%d \%H:\%M:\%S\\")}] {msg}"
9 | )
10 | }
11 | \arguments{
12 | \item{format}{\code{\link[glue:glue]{glue::glue()}}-flavored layout of the message using the above
13 | variables}
14 | }
15 | \value{
16 | function taking \code{level} and \code{msg} arguments - keeping the
17 | original call creating the generator in the \code{generator} attribute
18 | that is returned when calling \code{\link[=log_layout]{log_layout()}} for the currently
19 | used layout
20 | }
21 | \description{
22 | \code{format} is passed to \code{\link[glue:glue]{glue::glue()}} with access to the below variables:
23 | \itemize{ \item msg: the actual log message \item further variables
24 | set by \code{\link[=get_logger_meta_variables]{get_logger_meta_variables()}} }
25 | }
26 | \examples{
27 | \dontshow{old <- logger:::namespaces_set()}
28 | example_layout <- layout_glue_generator(
29 | format = "{node}/{pid}/{ns}/{ans}/{topenv}/{fn} {time} {level}: {msg}"
30 | )
31 | example_layout(INFO, "try {runif(1)}")
32 |
33 | log_layout(example_layout)
34 | log_info("try {runif(1)}")
35 | \dontshow{logger:::namespaces_set(old)}
36 | }
37 | \seealso{
38 | See example calls from \code{\link[=layout_glue]{layout_glue()}} and \code{\link[=layout_glue_colors]{layout_glue_colors()}}.
39 |
40 | Other log_layouts:
41 | \code{\link{get_logger_meta_variables}()},
42 | \code{\link{layout_blank}()},
43 | \code{\link{layout_glue}()},
44 | \code{\link{layout_glue_colors}()},
45 | \code{\link{layout_json}()},
46 | \code{\link{layout_json_parser}()},
47 | \code{\link{layout_logging}()},
48 | \code{\link{layout_simple}()}
49 | }
50 | \concept{log_layouts}
51 |
--------------------------------------------------------------------------------
/man/layout_json.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/layouts.R
3 | \name{layout_json}
4 | \alias{layout_json}
5 | \title{Generate log layout function rendering JSON}
6 | \usage{
7 | layout_json(fields = default_fields())
8 | }
9 | \arguments{
10 | \item{fields}{character vector of field names to be included in the
11 | JSON}
12 | }
13 | \value{
14 | character vector
15 | }
16 | \description{
17 | Generate log layout function rendering JSON
18 | }
19 | \note{
20 | This functionality depends on the \pkg{jsonlite} package.
21 | }
22 | \examples{
23 | \dontshow{old <- logger:::namespaces_set()}
24 | log_layout(layout_json())
25 | log_info(42)
26 | log_info("ok {1:3} + {1:3} = {2*(1:3)}")
27 | \dontshow{logger:::namespaces_set(old)}
28 | }
29 | \seealso{
30 | Other log_layouts:
31 | \code{\link{get_logger_meta_variables}()},
32 | \code{\link{layout_blank}()},
33 | \code{\link{layout_glue}()},
34 | \code{\link{layout_glue_colors}()},
35 | \code{\link{layout_glue_generator}()},
36 | \code{\link{layout_json_parser}()},
37 | \code{\link{layout_logging}()},
38 | \code{\link{layout_simple}()}
39 | }
40 | \concept{log_layouts}
41 |
--------------------------------------------------------------------------------
/man/layout_json_parser.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/layouts.R
3 | \name{layout_json_parser}
4 | \alias{layout_json_parser}
5 | \title{Generate log layout function rendering JSON after merging meta
6 | fields with parsed list from JSON message}
7 | \usage{
8 | layout_json_parser(fields = default_fields())
9 | }
10 | \arguments{
11 | \item{fields}{character vector of field names to be included in the
12 | JSON. If named, the names will be used as field names in the JSON.}
13 | }
14 | \description{
15 | Generate log layout function rendering JSON after merging meta
16 | fields with parsed list from JSON message
17 | }
18 | \note{
19 | This functionality depends on the \pkg{jsonlite} package.
20 | }
21 | \examples{
22 | \dontshow{old <- logger:::namespaces_set()}
23 | log_formatter(formatter_json)
24 | log_info(everything = 42)
25 |
26 | log_layout(layout_json_parser())
27 | log_info(everything = 42)
28 |
29 | log_layout(layout_json_parser(fields = c("time", "node")))
30 | log_info(cars = row.names(mtcars), species = unique(iris$Species))
31 |
32 | log_layout(layout_json_parser(fields = c(timestamp = "time", "node")))
33 | log_info(
34 | message = paste(
35 | "Compared to the previous example,
36 | the 'time' field is renamed to 'timestamp'"
37 | )
38 | )
39 | \dontshow{logger:::namespaces_set(old)}
40 | }
41 | \seealso{
42 | Other log_layouts:
43 | \code{\link{get_logger_meta_variables}()},
44 | \code{\link{layout_blank}()},
45 | \code{\link{layout_glue}()},
46 | \code{\link{layout_glue_colors}()},
47 | \code{\link{layout_glue_generator}()},
48 | \code{\link{layout_json}()},
49 | \code{\link{layout_logging}()},
50 | \code{\link{layout_simple}()}
51 | }
52 | \concept{log_layouts}
53 |
--------------------------------------------------------------------------------
/man/layout_logging.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/layouts.R
3 | \name{layout_logging}
4 | \alias{layout_logging}
5 | \title{Format a log record as the logging package does by default}
6 | \usage{
7 | layout_logging(
8 | level,
9 | msg,
10 | namespace = NA_character_,
11 | .logcall = sys.call(),
12 | .topcall = sys.call(-1),
13 | .topenv = parent.frame(),
14 | .timestamp = Sys.time()
15 | )
16 | }
17 | \arguments{
18 | \item{level}{log level, see \code{\link[=log_levels]{log_levels()}} for more details}
19 |
20 | \item{msg}{string message}
21 |
22 | \item{namespace}{string referring to the \code{logger} environment /
23 | config to be used to override the target of the message record to
24 | be used instead of the default namespace, which is defined by the
25 | R package name from which the logger was called, and falls back
26 | to a common, global namespace.}
27 |
28 | \item{.logcall}{the logging call being evaluated (useful in
29 | formatters and layouts when you want to have access to the raw,
30 | unevaluated R expression)}
31 |
32 | \item{.topcall}{R expression from which the logging function was
33 | called (useful in formatters and layouts to extract the calling
34 | function's name or arguments)}
35 |
36 | \item{.topenv}{original frame of the \code{.topcall} calling function
37 | where the formatter function will be evaluated and that is used
38 | to look up the \code{namespace} as well via \code{logger:::top_env_name}}
39 |
40 | \item{.timestamp}{The time the logging occured. Defaults to the current time
41 | but may be overwritten if the logging is delayed from the time it happend}
42 | }
43 | \value{
44 | character vector
45 | }
46 | \description{
47 | Format a log record as the logging package does by default
48 | }
49 | \examples{
50 | \dontshow{old <- logger:::namespaces_set()}
51 | log_layout(layout_logging)
52 | log_info(42)
53 | log_info(42, namespace = "everything")
54 |
55 | \dontrun{
56 | devtools::load_all(system.file("demo-packages/logger-tester-package", package = "logger"))
57 | logger_tester_function(INFO, 42)
58 | }
59 | \dontshow{logger:::namespaces_set(old)}
60 | }
61 | \seealso{
62 | Other log_layouts:
63 | \code{\link{get_logger_meta_variables}()},
64 | \code{\link{layout_blank}()},
65 | \code{\link{layout_glue}()},
66 | \code{\link{layout_glue_colors}()},
67 | \code{\link{layout_glue_generator}()},
68 | \code{\link{layout_json}()},
69 | \code{\link{layout_json_parser}()},
70 | \code{\link{layout_simple}()}
71 | }
72 | \concept{log_layouts}
73 |
--------------------------------------------------------------------------------
/man/layout_simple.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/layouts.R
3 | \name{layout_simple}
4 | \alias{layout_simple}
5 | \title{Format a log record by concatenating the log level, timestamp and
6 | message}
7 | \usage{
8 | layout_simple(
9 | level,
10 | msg,
11 | namespace = NA_character_,
12 | .logcall = sys.call(),
13 | .topcall = sys.call(-1),
14 | .topenv = parent.frame(),
15 | .timestamp = Sys.time()
16 | )
17 | }
18 | \arguments{
19 | \item{level}{log level, see \code{\link[=log_levels]{log_levels()}} for more details}
20 |
21 | \item{msg}{string message}
22 |
23 | \item{namespace}{string referring to the \code{logger} environment /
24 | config to be used to override the target of the message record to
25 | be used instead of the default namespace, which is defined by the
26 | R package name from which the logger was called, and falls back
27 | to a common, global namespace.}
28 |
29 | \item{.logcall}{the logging call being evaluated (useful in
30 | formatters and layouts when you want to have access to the raw,
31 | unevaluated R expression)}
32 |
33 | \item{.topcall}{R expression from which the logging function was
34 | called (useful in formatters and layouts to extract the calling
35 | function's name or arguments)}
36 |
37 | \item{.topenv}{original frame of the \code{.topcall} calling function
38 | where the formatter function will be evaluated and that is used
39 | to look up the \code{namespace} as well via \code{logger:::top_env_name}}
40 |
41 | \item{.timestamp}{The time the logging occured. Defaults to the current time
42 | but may be overwritten if the logging is delayed from the time it happend}
43 | }
44 | \value{
45 | character vector
46 | }
47 | \description{
48 | Format a log record by concatenating the log level, timestamp and
49 | message
50 | }
51 | \seealso{
52 | Other log_layouts:
53 | \code{\link{get_logger_meta_variables}()},
54 | \code{\link{layout_blank}()},
55 | \code{\link{layout_glue}()},
56 | \code{\link{layout_glue_colors}()},
57 | \code{\link{layout_glue_generator}()},
58 | \code{\link{layout_json}()},
59 | \code{\link{layout_json_parser}()},
60 | \code{\link{layout_logging}()}
61 | }
62 | \concept{log_layouts}
63 |
--------------------------------------------------------------------------------
/man/layout_syslognet.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/layouts.R
3 | \name{layout_syslognet}
4 | \alias{layout_syslognet}
5 | \title{Format a log record for syslognet}
6 | \usage{
7 | layout_syslognet(
8 | level,
9 | msg,
10 | namespace = NA_character_,
11 | .logcall = sys.call(),
12 | .topcall = sys.call(-1),
13 | .topenv = parent.frame(),
14 | .timestamp = Sys.time()
15 | )
16 | }
17 | \arguments{
18 | \item{level}{log level, see \code{\link[=log_levels]{log_levels()}} for more details}
19 |
20 | \item{msg}{string message}
21 |
22 | \item{namespace}{string referring to the \code{logger} environment /
23 | config to be used to override the target of the message record to
24 | be used instead of the default namespace, which is defined by the
25 | R package name from which the logger was called, and falls back
26 | to a common, global namespace.}
27 |
28 | \item{.logcall}{the logging call being evaluated (useful in
29 | formatters and layouts when you want to have access to the raw,
30 | unevaluated R expression)}
31 |
32 | \item{.topcall}{R expression from which the logging function was
33 | called (useful in formatters and layouts to extract the calling
34 | function's name or arguments)}
35 |
36 | \item{.topenv}{original frame of the \code{.topcall} calling function
37 | where the formatter function will be evaluated and that is used
38 | to look up the \code{namespace} as well via \code{logger:::top_env_name}}
39 |
40 | \item{.timestamp}{The time the logging occured. Defaults to the current time
41 | but may be overwritten if the logging is delayed from the time it happend}
42 | }
43 | \value{
44 | A character vector with a severity attribute.
45 | }
46 | \description{
47 | Format a log record for syslognet.
48 | This function converts the logger log level to a
49 | log severity level according to RFC 5424 "The Syslog Protocol".
50 | }
51 |
--------------------------------------------------------------------------------
/man/log_appender.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/logger.R
3 | \name{log_appender}
4 | \alias{log_appender}
5 | \title{Get or set log record appender function}
6 | \usage{
7 | log_appender(appender = NULL, namespace = "global", index = 1)
8 | }
9 | \arguments{
10 | \item{appender}{function delivering a log record to the
11 | destination, eg \code{\link[=appender_console]{appender_console()}}, \code{\link[=appender_file]{appender_file()}} or
12 | \code{\link[=appender_tee]{appender_tee()}}, default NULL}
13 |
14 | \item{namespace}{logger namespace}
15 |
16 | \item{index}{index of the logger within the namespace}
17 | }
18 | \description{
19 | Get or set log record appender function
20 | }
21 | \examples{
22 | \dontshow{old <- logger:::namespaces_set()}
23 | ## change appender to "tee" that writes to the console and a file as well
24 | t <- tempfile()
25 | log_appender(appender_tee(t))
26 | log_info(42)
27 | log_info(43)
28 | log_info(44)
29 | readLines(t)
30 |
31 | ## poor man's tee by stacking loggers in the namespace
32 | t <- tempfile()
33 | log_appender(appender_stdout)
34 | log_appender(appender_file(t), index = 2)
35 | log_info(42)
36 | readLines(t)
37 | \dontshow{logger:::namespaces_set(old)}
38 | }
39 | \seealso{
40 | Other log configutation functions:
41 | \code{\link{log_formatter}()},
42 | \code{\link{log_layout}()},
43 | \code{\link{log_threshold}()}
44 | }
45 | \concept{log configutation functions}
46 |
--------------------------------------------------------------------------------
/man/log_chunk_time.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/helpers.R
3 | \name{log_chunk_time}
4 | \alias{log_chunk_time}
5 | \title{Automatically log execution time of knitr chunks}
6 | \usage{
7 | log_chunk_time(..., level = INFO, namespace = NA_character_)
8 | }
9 | \arguments{
10 | \item{...}{passed to \code{\link[=log_level]{log_level()}}}
11 |
12 | \item{level}{see \code{\link[=log_levels]{log_levels()}}}
13 |
14 | \item{namespace}{x}
15 | }
16 | \description{
17 | Calling this function in the first chunk of a document will instruct knitr
18 | to automatically log the execution time of each chunk. If using
19 | \code{\link[=formatter_glue]{formatter_glue()}} or \code{\link[=formatter_cli]{formatter_cli()}} then the \code{options} variable will be
20 | available, providing the chunk options such as chunk label etc.
21 | }
22 | \examples{
23 | # To be put in the first chunk of a document
24 | log_chunk_time("chunk {options$label}")
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/man/log_elapsed.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/helpers.R
3 | \name{log_elapsed}
4 | \alias{log_elapsed}
5 | \alias{log_elapsed_start}
6 | \title{Log cumulative running time}
7 | \usage{
8 | log_elapsed(..., level = INFO, namespace = NA_character_)
9 |
10 | log_elapsed_start(level = INFO, namespace = NA_character_, quiet = FALSE)
11 | }
12 | \arguments{
13 | \item{...}{passed to \code{\link[=log_level]{log_level()}}}
14 |
15 | \item{level}{see \code{\link[=log_levels]{log_levels()}}}
16 |
17 | \item{namespace}{x}
18 |
19 | \item{quiet}{Should starting the time emit a log message}
20 | }
21 | \description{
22 | This function is working like \code{\link[=log_tictoc]{log_tictoc()}} but differs in that it continues
23 | to count up rather than resetting the timer at every call. You can set the
24 | start time using \code{log_elapsed_start()}, but if that hasn't been called it
25 | will show the time since the R session started.
26 | }
27 | \examples{
28 | log_elapsed_start()
29 | Sys.sleep(0.4)
30 | log_elapsed("Tast 1")
31 | Sys.sleep(0.2)
32 | log_elapsed("Task 2")
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/man/log_errors.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/hooks.R
3 | \name{log_errors}
4 | \alias{log_errors}
5 | \title{Injects a logger call to standard errors}
6 | \usage{
7 | log_errors(
8 | muffle = getOption("logger_muffle_errors", FALSE),
9 | traceback = FALSE
10 | )
11 | }
12 | \arguments{
13 | \item{muffle}{if TRUE, the error is not thrown after being logged}
14 |
15 | \item{traceback}{if TRUE the error traceback is logged along with the error
16 | message}
17 | }
18 | \description{
19 | This function uses \code{\link[=trace]{trace()}} to add a \code{\link[=log_error]{log_error()}} function call when
20 | \code{\link[=stop]{stop()}} is called to log the error messages with the \code{logger} layout
21 | and appender.
22 | }
23 | \examples{
24 | \dontrun{
25 | log_errors()
26 | stop("foobar")
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/man/log_eval.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/helpers.R
3 | \name{log_eval}
4 | \alias{log_eval}
5 | \title{Evaluate an expression and log results}
6 | \usage{
7 | log_eval(expr, level = TRACE, multiline = FALSE)
8 | }
9 | \arguments{
10 | \item{expr}{R expression to be evaluated while logging the
11 | expression itself along with the result}
12 |
13 | \item{level}{\code{\link[=log_levels]{log_levels()}}}
14 |
15 | \item{multiline}{setting to \code{FALSE} will print both the expression
16 | (enforced to be on one line by removing line-breaks if any) and
17 | its result on a single line separated by \verb{=>}, while setting to
18 | \code{TRUE} will log the expression and the result in separate
19 | sections reserving line-breaks and rendering the printed results}
20 | }
21 | \description{
22 | Evaluate an expression and log results
23 | }
24 | \examples{
25 | \dontshow{old <- logger:::namespaces_set()}
26 | log_eval(pi * 2, level = INFO)
27 |
28 | ## lowering the log level threshold so that we don't have to set a higher level in log_eval
29 | log_threshold(TRACE)
30 | log_eval(x <- 4)
31 | log_eval(sqrt(x))
32 |
33 | ## log_eval can be called in-line as well as returning the return value of the expression
34 | x <- log_eval(mean(runif(1e3)))
35 | x
36 |
37 | ## https://twitter.com/krlmlr/status/1067864829547999232
38 | f <- sqrt
39 | g <- mean
40 | x <- 1:31
41 | log_eval(f(g(x)), level = INFO)
42 | log_eval(y <- f(g(x)), level = INFO)
43 |
44 | ## returning a function
45 | log_eval(f <- sqrt)
46 | log_eval(f)
47 |
48 | ## evaluating something returning a wall of "text"
49 | log_eval(f <- log_eval)
50 | log_eval(f <- log_eval, multiline = TRUE)
51 |
52 | ## doing something computationally intensive
53 | log_eval(system.time(for (i in 1:100) mad(runif(1000))), multiline = TRUE)
54 | \dontshow{logger:::namespaces_set(old)}
55 | }
56 |
--------------------------------------------------------------------------------
/man/log_failure.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/helpers.R
3 | \name{log_failure}
4 | \alias{log_failure}
5 | \title{Logs the error message to console before failing}
6 | \usage{
7 | log_failure(expression)
8 | }
9 | \arguments{
10 | \item{expression}{call}
11 | }
12 | \description{
13 | Logs the error message to console before failing
14 | }
15 | \examples{
16 | log_failure("foobar")
17 | try(log_failure(foobar))
18 | }
19 |
--------------------------------------------------------------------------------
/man/log_formatter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/logger.R
3 | \name{log_formatter}
4 | \alias{log_formatter}
5 | \title{Get or set log message formatter}
6 | \usage{
7 | log_formatter(formatter = NULL, namespace = "global", index = 1)
8 | }
9 | \arguments{
10 | \item{formatter}{function defining how R objects are converted into
11 | a single string, eg \code{\link[=formatter_paste]{formatter_paste()}}, \code{\link[=formatter_sprintf]{formatter_sprintf()}},
12 | \code{\link[=formatter_glue]{formatter_glue()}}, \code{\link[=formatter_glue_or_sprintf]{formatter_glue_or_sprintf()}},
13 | \code{\link[=formatter_logging]{formatter_logging()}}, default NULL}
14 |
15 | \item{namespace}{logger namespace}
16 |
17 | \item{index}{index of the logger within the namespace}
18 | }
19 | \description{
20 | Get or set log message formatter
21 | }
22 | \seealso{
23 | Other log configutation functions:
24 | \code{\link{log_appender}()},
25 | \code{\link{log_layout}()},
26 | \code{\link{log_threshold}()}
27 | }
28 | \concept{log configutation functions}
29 |
--------------------------------------------------------------------------------
/man/log_indices.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/logger.R
3 | \name{log_indices}
4 | \alias{log_indices}
5 | \title{Returns number of currently active indices}
6 | \usage{
7 | log_indices(namespace = "global")
8 | }
9 | \arguments{
10 | \item{namespace}{override the default / auto-picked namespace with
11 | a custom string}
12 | }
13 | \value{
14 | number of indices
15 | }
16 | \description{
17 | Returns number of currently active indices
18 | }
19 |
--------------------------------------------------------------------------------
/man/log_layout.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/logger.R
3 | \name{log_layout}
4 | \alias{log_layout}
5 | \title{Get or set log record layout}
6 | \usage{
7 | log_layout(layout = NULL, namespace = "global", index = 1)
8 | }
9 | \arguments{
10 | \item{layout}{function defining the structure of a log record, eg
11 | \code{\link[=layout_simple]{layout_simple()}}, \code{\link[=layout_glue]{layout_glue()}} or \code{\link[=layout_glue_colors]{layout_glue_colors()}},
12 | \code{\link[=layout_json]{layout_json()}}, or generator functions such as
13 | \code{\link[=layout_glue_generator]{layout_glue_generator()}}, default NULL}
14 |
15 | \item{namespace}{logger namespace}
16 |
17 | \item{index}{index of the logger within the namespace}
18 | }
19 | \description{
20 | Get or set log record layout
21 | }
22 | \examples{
23 | \dontshow{old <- logger:::namespaces_set()}
24 | log_layout(layout_json())
25 | log_info(42)
26 | \dontshow{logger:::namespaces_set(old)}
27 | }
28 | \seealso{
29 | Other log configutation functions:
30 | \code{\link{log_appender}()},
31 | \code{\link{log_formatter}()},
32 | \code{\link{log_threshold}()}
33 | }
34 | \concept{log configutation functions}
35 |
--------------------------------------------------------------------------------
/man/log_level.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/logger.R
3 | \name{log_level}
4 | \alias{log_level}
5 | \alias{log_fatal}
6 | \alias{log_error}
7 | \alias{log_warn}
8 | \alias{log_success}
9 | \alias{log_info}
10 | \alias{log_debug}
11 | \alias{log_trace}
12 | \title{Log a message with given log level}
13 | \usage{
14 | log_level(
15 | level,
16 | ...,
17 | namespace = NA_character_,
18 | .logcall = sys.call(),
19 | .topcall = sys.call(-1),
20 | .topenv = parent.frame(),
21 | .timestamp = Sys.time()
22 | )
23 |
24 | log_fatal(
25 | ...,
26 | namespace = NA_character_,
27 | .logcall = sys.call(),
28 | .topcall = sys.call(-1),
29 | .topenv = parent.frame(),
30 | .timestamp = Sys.time()
31 | )
32 |
33 | log_error(
34 | ...,
35 | namespace = NA_character_,
36 | .logcall = sys.call(),
37 | .topcall = sys.call(-1),
38 | .topenv = parent.frame(),
39 | .timestamp = Sys.time()
40 | )
41 |
42 | log_warn(
43 | ...,
44 | namespace = NA_character_,
45 | .logcall = sys.call(),
46 | .topcall = sys.call(-1),
47 | .topenv = parent.frame(),
48 | .timestamp = Sys.time()
49 | )
50 |
51 | log_success(
52 | ...,
53 | namespace = NA_character_,
54 | .logcall = sys.call(),
55 | .topcall = sys.call(-1),
56 | .topenv = parent.frame(),
57 | .timestamp = Sys.time()
58 | )
59 |
60 | log_info(
61 | ...,
62 | namespace = NA_character_,
63 | .logcall = sys.call(),
64 | .topcall = sys.call(-1),
65 | .topenv = parent.frame(),
66 | .timestamp = Sys.time()
67 | )
68 |
69 | log_debug(
70 | ...,
71 | namespace = NA_character_,
72 | .logcall = sys.call(),
73 | .topcall = sys.call(-1),
74 | .topenv = parent.frame(),
75 | .timestamp = Sys.time()
76 | )
77 |
78 | log_trace(
79 | ...,
80 | namespace = NA_character_,
81 | .logcall = sys.call(),
82 | .topcall = sys.call(-1),
83 | .topenv = parent.frame(),
84 | .timestamp = Sys.time()
85 | )
86 | }
87 | \arguments{
88 | \item{level}{log level, see \code{\link[=log_levels]{log_levels()}} for more details}
89 |
90 | \item{...}{R objects that can be converted to a character vector
91 | via the active message formatter function}
92 |
93 | \item{namespace}{string referring to the \code{logger} environment /
94 | config to be used to override the target of the message record to
95 | be used instead of the default namespace, which is defined by the
96 | R package name from which the logger was called, and falls back
97 | to a common, global namespace.}
98 |
99 | \item{.logcall}{the logging call being evaluated (useful in
100 | formatters and layouts when you want to have access to the raw,
101 | unevaluated R expression)}
102 |
103 | \item{.topcall}{R expression from which the logging function was
104 | called (useful in formatters and layouts to extract the calling
105 | function's name or arguments)}
106 |
107 | \item{.topenv}{original frame of the \code{.topcall} calling function
108 | where the formatter function will be evaluated and that is used
109 | to look up the \code{namespace} as well via \code{logger:::top_env_name}}
110 |
111 | \item{.timestamp}{The time the logging occured. Defaults to the current time
112 | but may be overwritten if the logging is delayed from the time it happend}
113 | }
114 | \value{
115 | Invisible \code{list} of \code{logger} objects. See \code{\link[=logger]{logger()}} for more details on the format.
116 | }
117 | \description{
118 | Log a message with given log level
119 | }
120 | \examples{
121 | \dontshow{old <- logger:::namespaces_set()}
122 | log_level(INFO, "hi there")
123 | log_info("hi there")
124 |
125 | ## output omitted
126 | log_debug("hi there")
127 |
128 | ## lower threshold and retry
129 | log_threshold(TRACE)
130 | log_debug("hi there")
131 |
132 | ## multiple lines
133 | log_info("ok {1:3} + {1:3} = {2*(1:3)}")
134 |
135 | ## use json layout
136 | log_layout(layout_json(c("time", "level")))
137 | log_info("ok {1:3} + {1:3} = {2*(1:3)}")
138 | \dontshow{logger:::namespaces_set(old)}
139 | }
140 |
--------------------------------------------------------------------------------
/man/log_levels.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/levels.R
3 | \docType{data}
4 | \name{log_levels}
5 | \alias{log_levels}
6 | \alias{OFF}
7 | \alias{FATAL}
8 | \alias{ERROR}
9 | \alias{WARN}
10 | \alias{SUCCESS}
11 | \alias{INFO}
12 | \alias{DEBUG}
13 | \alias{TRACE}
14 | \title{Log levels}
15 | \usage{
16 | OFF
17 |
18 | FATAL
19 |
20 | ERROR
21 |
22 | WARN
23 |
24 | SUCCESS
25 |
26 | INFO
27 |
28 | DEBUG
29 |
30 | TRACE
31 | }
32 | \description{
33 | The standard Apache logj4 log levels plus a custom level for
34 | \code{SUCCESS}. For the full list of these log levels and suggested
35 | usage, check the below Details.
36 | }
37 | \details{
38 | List of supported log levels:
39 | \itemize{
40 | \item \code{OFF} No events will be logged
41 | \item \code{FATAL} Severe error that will prevent the application from continuing
42 | \item \code{ERROR} An error in the application, possibly recoverable
43 | \item \code{WARN} An event that might possible lead to an error
44 | \item \code{SUCCESS} An explicit success event above the INFO level that you want to log
45 | \item \code{INFO} An event for informational purposes
46 | \item \code{DEBUG} A general debugging event
47 | \item \code{TRACE} A fine-grained debug message, typically capturing the flow through the application.
48 | }
49 | }
50 | \references{
51 | \url{https://logging.apache.org/log4j/2.x/javadoc/log4j-api/org/apache/logging/log4j/Level.html},
52 | \url{https://logging.apache.org/log4j/2.x/manual/customloglevels.html}
53 | }
54 | \keyword{datasets}
55 |
--------------------------------------------------------------------------------
/man/log_messages.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/hooks.R
3 | \name{log_messages}
4 | \alias{log_messages}
5 | \title{Injects a logger call to standard messages}
6 | \usage{
7 | log_messages()
8 | }
9 | \description{
10 | This function uses \code{\link[=trace]{trace()}} to add a \code{\link[=log_info]{log_info()}} function call when
11 | \code{message} is called to log the informative messages with the
12 | \code{logger} layout and appender.
13 | }
14 | \examples{
15 | \dontrun{
16 | log_messages()
17 | message("hi there")
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/man/log_namespaces.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/logger.R
3 | \name{log_namespaces}
4 | \alias{log_namespaces}
5 | \title{Looks up logger namespaces}
6 | \usage{
7 | log_namespaces()
8 | }
9 | \value{
10 | character vector of namespace names
11 | }
12 | \description{
13 | Looks up logger namespaces
14 | }
15 |
--------------------------------------------------------------------------------
/man/log_separator.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/helpers.R
3 | \name{log_separator}
4 | \alias{log_separator}
5 | \title{Logs a long line to stand out from the console}
6 | \usage{
7 | log_separator(
8 | level = INFO,
9 | namespace = NA_character_,
10 | separator = "=",
11 | width = 80,
12 | .logcall = sys.call(),
13 | .topcall = sys.call(-1),
14 | .topenv = parent.frame(),
15 | .timestamp = Sys.time()
16 | )
17 | }
18 | \arguments{
19 | \item{level}{log level, see \code{\link[=log_levels]{log_levels()}} for more details}
20 |
21 | \item{namespace}{string referring to the \code{logger} environment /
22 | config to be used to override the target of the message record to
23 | be used instead of the default namespace, which is defined by the
24 | R package name from which the logger was called, and falls back
25 | to a common, global namespace.}
26 |
27 | \item{separator}{character to be used as a separator}
28 |
29 | \item{width}{max width of message -- longer text will be wrapped into multiple lines}
30 |
31 | \item{.logcall}{the logging call being evaluated (useful in
32 | formatters and layouts when you want to have access to the raw,
33 | unevaluated R expression)}
34 |
35 | \item{.topcall}{R expression from which the logging function was
36 | called (useful in formatters and layouts to extract the calling
37 | function's name or arguments)}
38 |
39 | \item{.topenv}{original frame of the \code{.topcall} calling function
40 | where the formatter function will be evaluated and that is used
41 | to look up the \code{namespace} as well via \code{logger:::top_env_name}}
42 |
43 | \item{.timestamp}{The time the logging occured. Defaults to the current time
44 | but may be overwritten if the logging is delayed from the time it happend}
45 | }
46 | \description{
47 | Logs a long line to stand out from the console
48 | }
49 | \examples{
50 | \dontshow{old <- logger:::namespaces_set()}
51 | log_separator()
52 | log_separator(ERROR, separator = "!", width = 60)
53 | log_separator(ERROR, separator = "!", width = 100)
54 | logger <- layout_glue_generator(format = "{node}/{pid}/{namespace}/{fn} {time} {level}: {msg}")
55 | log_layout(logger)
56 | log_separator(ERROR, separator = "!", width = 100)
57 | log_layout(layout_blank)
58 | log_separator(ERROR, separator = "!", width = 80)
59 | \dontshow{logger:::namespaces_set(old)}
60 | }
61 | \seealso{
62 | \code{\link[=log_with_separator]{log_with_separator()}}
63 | }
64 |
--------------------------------------------------------------------------------
/man/log_shiny_input_changes.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/hooks.R
3 | \name{log_shiny_input_changes}
4 | \alias{log_shiny_input_changes}
5 | \title{Auto logging input changes in Shiny app}
6 | \usage{
7 | log_shiny_input_changes(
8 | input,
9 | level = INFO,
10 | namespace = NA_character_,
11 | excluded_inputs = character()
12 | )
13 | }
14 | \arguments{
15 | \item{input}{passed from Shiny's \code{server}}
16 |
17 | \item{level}{log level}
18 |
19 | \item{namespace}{the name of the namespace}
20 |
21 | \item{excluded_inputs}{character vector of input names to exclude from logging}
22 | }
23 | \description{
24 | This is to be called in the \code{server} section of the Shiny app.
25 | }
26 | \examples{
27 | \dontrun{
28 | library(shiny)
29 |
30 | ui <- bootstrapPage(
31 | numericInput("mean", "mean", 0),
32 | numericInput("sd", "sd", 1),
33 | textInput("title", "title", "title"),
34 | textInput("foo", "This is not used at all, still gets logged", "foo"),
35 | passwordInput("password", "Password not to be logged", "secret"),
36 | plotOutput("plot")
37 | )
38 |
39 | server <- function(input, output) {
40 | logger::log_shiny_input_changes(input, excluded_inputs = "password")
41 |
42 | output$plot <- renderPlot({
43 | hist(rnorm(1e3, input$mean, input$sd), main = input$title)
44 | })
45 | }
46 |
47 | shinyApp(ui = ui, server = server)
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/man/log_threshold.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/logger.R
3 | \name{log_threshold}
4 | \alias{log_threshold}
5 | \title{Get or set log level threshold}
6 | \usage{
7 | log_threshold(level = NULL, namespace = "global", index = 1)
8 | }
9 | \arguments{
10 | \item{level}{see \code{\link[=log_levels]{log_levels()}}}
11 |
12 | \item{namespace}{logger namespace}
13 |
14 | \item{index}{index of the logger within the namespace}
15 | }
16 | \value{
17 | currently set log level threshold
18 | }
19 | \description{
20 | Get or set log level threshold
21 | }
22 | \examples{
23 | \dontshow{old <- logger:::namespaces_set()}
24 | ## check the currently set log level threshold
25 | log_threshold()
26 |
27 | ## change the log level threshold to WARN
28 | log_threshold(WARN)
29 | log_info(1)
30 | log_warn(2)
31 |
32 | ## add another logger with a lower log level threshold and check the number of logged messages
33 | log_threshold(INFO, index = 2)
34 | log_info(1)
35 | log_warn(2)
36 |
37 | ## set the log level threshold in all namespaces to ERROR
38 | log_threshold(ERROR, namespace = log_namespaces())
39 | \dontshow{logger:::namespaces_set(old)}
40 | }
41 | \seealso{
42 | Other log configutation functions:
43 | \code{\link{log_appender}()},
44 | \code{\link{log_formatter}()},
45 | \code{\link{log_layout}()}
46 | }
47 | \concept{log configutation functions}
48 |
--------------------------------------------------------------------------------
/man/log_tictoc.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/helpers.R
3 | \name{log_tictoc}
4 | \alias{log_tictoc}
5 | \title{Tic-toc logging}
6 | \usage{
7 | log_tictoc(..., level = INFO, namespace = NA_character_)
8 | }
9 | \arguments{
10 | \item{...}{passed to \code{\link[=log_level]{log_level()}}}
11 |
12 | \item{level}{see \code{\link[=log_levels]{log_levels()}}}
13 |
14 | \item{namespace}{x}
15 | }
16 | \description{
17 | Tic-toc logging
18 | }
19 | \examples{
20 | log_tictoc("warming up")
21 | Sys.sleep(0.1)
22 | log_tictoc("running")
23 | Sys.sleep(0.1)
24 | log_tictoc("running")
25 | Sys.sleep(runif(1))
26 | log_tictoc("and running")
27 | }
28 | \author{
29 | Thanks to Neal Fultz for the idea and original implementation!
30 | }
31 |
--------------------------------------------------------------------------------
/man/log_warnings.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/hooks.R
3 | \name{log_warnings}
4 | \alias{log_warnings}
5 | \title{Injects a logger call to standard warnings}
6 | \usage{
7 | log_warnings(muffle = getOption("logger_muffle_warnings", FALSE))
8 | }
9 | \arguments{
10 | \item{muffle}{if TRUE, the warning is not shown after being logged}
11 | }
12 | \description{
13 | This function uses \code{trace} to add a \code{log_warn} function call when
14 | \code{warning} is called to log the warning messages with the \code{logger}
15 | layout and appender.
16 | }
17 | \examples{
18 | \dontrun{
19 | log_warnings()
20 | for (i in 1:5) {
21 | Sys.sleep(runif(1))
22 | warning(i)
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/man/log_with_separator.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/helpers.R
3 | \name{log_with_separator}
4 | \alias{log_with_separator}
5 | \title{Logs a message in a very visible way}
6 | \usage{
7 | log_with_separator(
8 | ...,
9 | level = INFO,
10 | namespace = NA_character_,
11 | separator = "=",
12 | width = 80
13 | )
14 | }
15 | \arguments{
16 | \item{...}{R objects that can be converted to a character vector
17 | via the active message formatter function}
18 |
19 | \item{level}{log level, see \code{\link[=log_levels]{log_levels()}} for more details}
20 |
21 | \item{namespace}{string referring to the \code{logger} environment /
22 | config to be used to override the target of the message record to
23 | be used instead of the default namespace, which is defined by the
24 | R package name from which the logger was called, and falls back
25 | to a common, global namespace.}
26 |
27 | \item{separator}{character to be used as a separator}
28 |
29 | \item{width}{max width of message -- longer text will be wrapped into multiple lines}
30 | }
31 | \description{
32 | Logs a message in a very visible way
33 | }
34 | \examples{
35 | \dontshow{old <- logger:::namespaces_set()}
36 | log_with_separator("An important message")
37 | log_with_separator("Some critical KPI down!!!", separator = "$")
38 | log_with_separator("This message is worth a {1e3} words")
39 | log_with_separator(paste(
40 | "A very important message with a bunch of extra words that will",
41 | "eventually wrap into a multi-line message for our quite nice demo :wow:"
42 | ))
43 | log_with_separator(
44 | paste(
45 | "A very important message with a bunch of extra words that will",
46 | "eventually wrap into a multi-line message for our quite nice demo :wow:"
47 | ),
48 | width = 60
49 | )
50 | log_with_separator("Boo!", level = FATAL)
51 | log_layout(layout_blank)
52 | log_with_separator("Boo!", level = FATAL)
53 | logger <- layout_glue_generator(format = "{node}/{pid}/{namespace}/{fn} {time} {level}: {msg}")
54 | log_layout(logger)
55 | log_with_separator("Boo!", level = FATAL, width = 120)
56 | \dontshow{logger:::namespaces_set(old)}
57 | }
58 | \seealso{
59 | \code{\link[=log_separator]{log_separator()}}
60 | }
61 |
--------------------------------------------------------------------------------
/man/logger-package.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/logger-package.R
3 | \docType{package}
4 | \name{logger-package}
5 | \alias{logger-package}
6 | \title{logger: A Lightweight, Modern and Flexible Logging Utility}
7 | \description{
8 | \if{html}{\figure{logo.png}{options: style='float: right' alt='logo' width='120'}}
9 |
10 | Inspired by the the 'futile.logger' R package and 'logging' Python module, this utility provides a flexible and extensible way of formatting and delivering log messages with low overhead.
11 | }
12 | \seealso{
13 | Useful links:
14 | \itemize{
15 | \item \url{https://daroczig.github.io/logger/}
16 | \item Report bugs at \url{https://github.com/daroczig/logger/issues}
17 | }
18 |
19 | }
20 | \author{
21 | \strong{Maintainer}: Gergely Daróczi \email{daroczig@rapporter.net} (\href{https://orcid.org/0000-0003-3149-8537}{ORCID})
22 |
23 | Authors:
24 | \itemize{
25 | \item Hadley Wickham \email{hadley@posit.co} (\href{https://orcid.org/0000-0003-4757-117X}{ORCID})
26 | }
27 |
28 | Other contributors:
29 | \itemize{
30 | \item System1 [funder]
31 | }
32 |
33 | }
34 | \keyword{internal}
35 |
--------------------------------------------------------------------------------
/man/logger.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/logger.R
3 | \name{logger}
4 | \alias{logger}
5 | \title{Generate logging utility}
6 | \usage{
7 | logger(threshold, formatter, layout, appender)
8 | }
9 | \arguments{
10 | \item{threshold}{omit log messages below this \code{\link[=log_levels]{log_levels()}}}
11 |
12 | \item{formatter}{function pre-processing the message of the log
13 | record when it's not wrapped in a \code{\link[=skip_formatter]{skip_formatter()}} call}
14 |
15 | \item{layout}{function rendering the layout of the actual log
16 | record}
17 |
18 | \item{appender}{function writing the log record}
19 | }
20 | \value{
21 | A function taking the log \code{level} to compare with the set
22 | threshold, all the \code{...} arguments passed to the formatter
23 | function, besides the standard \code{namespace}, \code{.logcall},
24 | \code{.topcall} and \code{.topenv} arguments (see \code{\link[=log_level]{log_level()}} for more
25 | details). The function invisibly returns a list including the
26 | original \code{level}, \code{namespace}, all \code{...} transformed to a list as
27 | \code{params}, the log \code{message} (after calling the \code{formatter}
28 | function) and the log \code{record} (after calling the \code{layout}
29 | function), and a list of \code{handlers} with the \code{formatter},
30 | \code{layout} and \code{appender} functions.
31 | }
32 | \description{
33 | A logger consists of a log level \code{threshold}, a log message
34 | \code{formatter} function, a log record \code{layout} formatting function and
35 | the \code{appender} function deciding on the destination of the log
36 | record. For more details, see the package \code{README.md}.
37 | }
38 | \details{
39 | By default, a general logger definition is created when loading the \code{logger} package, that uses
40 | \itemize{
41 | \item \code{\link[=INFO]{INFO()}} (or as per the \code{LOGGER_LOG_LEVEL} environment variable override) as the log level threshold
42 | \item \code{\link[=layout_simple]{layout_simple()}} as the layout function showing the log level, timestamp and log message
43 | \item \code{\link[=formatter_glue]{formatter_glue()}} (or \code{\link[=formatter_sprintf]{formatter_sprintf()}} if \pkg{glue} is not installed) as the
44 | default formatter function transforming the R objects to be logged to a character vector
45 | \item \code{\link[=appender_console]{appender_console()}} as the default log record destination
46 | }
47 | }
48 | \note{
49 | It's quite unlikely that you need to call this function
50 | directly, but instead set the logger parameters and functions at
51 | \code{\link[=log_threshold]{log_threshold()}}, \code{\link[=log_formatter]{log_formatter()}}, \code{\link[=log_layout]{log_layout()}} and
52 | \code{\link[=log_appender]{log_appender()}} and then call \code{\link[=log_levels]{log_levels()}} and its
53 | derivatives, such as \code{\link[=log_info]{log_info()}} directly.
54 | }
55 | \examples{
56 | \dontrun{
57 | do.call(logger, logger:::namespaces$global[[1]])(INFO, 42)
58 | do.call(logger, logger:::namespaces$global[[1]])(INFO, "{pi}")
59 | x <- 42
60 | do.call(logger, logger:::namespaces$global[[1]])(INFO, "{x}^2 = {x^2}")
61 | }
62 | }
63 | \references{
64 | For more details, see the Anatomy of a Log Request
65 | vignette at
66 | \url{https://daroczig.github.io/logger/articles/anatomy.html}.
67 | }
68 |
--------------------------------------------------------------------------------
/man/skip_formatter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/formatters.R
3 | \name{skip_formatter}
4 | \alias{skip_formatter}
5 | \title{Skip the formatter function}
6 | \usage{
7 | skip_formatter(message, ...)
8 | }
9 | \arguments{
10 | \item{message}{character vector directly passed to the appender
11 | function in \code{\link[=logger]{logger()}}}
12 |
13 | \item{...}{should be never set}
14 | }
15 | \value{
16 | character vector with \code{skip_formatter} attribute set to
17 | \code{TRUE}
18 | }
19 | \description{
20 | Adds the \code{skip_formatter} attribute to an object so that logger
21 | will skip calling the formatter function(s). This is useful if you
22 | want to preprocess the log message with a custom function instead
23 | of the active formatter function(s). Note that the \code{message} should
24 | be a string, and \code{skip_formatter} should be the only input for the
25 | logging function to make this work.
26 | }
27 |
--------------------------------------------------------------------------------
/man/with_log_threshold.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/logger.R
3 | \name{with_log_threshold}
4 | \alias{with_log_threshold}
5 | \title{Evaluate R expression with a temporarily updated log level threshold}
6 | \usage{
7 | with_log_threshold(
8 | expression,
9 | threshold = ERROR,
10 | namespace = "global",
11 | index = 1
12 | )
13 | }
14 | \arguments{
15 | \item{expression}{R command}
16 |
17 | \item{threshold}{\code{\link[=log_levels]{log_levels()}}}
18 |
19 | \item{namespace}{logger namespace}
20 |
21 | \item{index}{index of the logger within the namespace}
22 | }
23 | \description{
24 | Evaluate R expression with a temporarily updated log level threshold
25 | }
26 | \examples{
27 | \dontshow{old <- logger:::namespaces_set()}
28 | log_threshold(TRACE)
29 | log_trace("Logging everything!")
30 | x <- with_log_threshold(
31 | {
32 | log_info("Now we are temporarily suppressing eg INFO messages")
33 | log_warn("WARN")
34 | log_debug("Debug messages are suppressed as well")
35 | log_error("ERROR")
36 | invisible(42)
37 | },
38 | threshold = WARN
39 | )
40 | x
41 | log_trace("DONE")
42 | \dontshow{logger:::namespaces_set(old)}
43 | }
44 |
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/daroczig/logger/9adf2c4e697140e12dab86c78b53f85b8fb79b95/pkgdown/favicon/apple-touch-icon-120x120.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/daroczig/logger/9adf2c4e697140e12dab86c78b53f85b8fb79b95/pkgdown/favicon/apple-touch-icon-152x152.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/daroczig/logger/9adf2c4e697140e12dab86c78b53f85b8fb79b95/pkgdown/favicon/apple-touch-icon-180x180.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/daroczig/logger/9adf2c4e697140e12dab86c78b53f85b8fb79b95/pkgdown/favicon/apple-touch-icon-60x60.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/daroczig/logger/9adf2c4e697140e12dab86c78b53f85b8fb79b95/pkgdown/favicon/apple-touch-icon-76x76.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/daroczig/logger/9adf2c4e697140e12dab86c78b53f85b8fb79b95/pkgdown/favicon/apple-touch-icon.png
--------------------------------------------------------------------------------
/pkgdown/favicon/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/daroczig/logger/9adf2c4e697140e12dab86c78b53f85b8fb79b95/pkgdown/favicon/favicon-16x16.png
--------------------------------------------------------------------------------
/pkgdown/favicon/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/daroczig/logger/9adf2c4e697140e12dab86c78b53f85b8fb79b95/pkgdown/favicon/favicon-32x32.png
--------------------------------------------------------------------------------
/pkgdown/favicon/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/daroczig/logger/9adf2c4e697140e12dab86c78b53f85b8fb79b95/pkgdown/favicon/favicon.ico
--------------------------------------------------------------------------------
/tests/testthat.R:
--------------------------------------------------------------------------------
1 | # This file is part of the standard setup for testthat.
2 | # It is recommended that you do not modify it.
3 | #
4 | # Where should you do additional test configuration?
5 | # Learn more about the roles of various files in:
6 | # * https://r-pkgs.org/testing-design.html#sec-tests-files-overview
7 | # * https://testthat.r-lib.org/articles/special-files.html
8 |
9 | library(testthat)
10 | library(logger)
11 |
12 | test_check("logger")
13 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/formatters.md:
--------------------------------------------------------------------------------
1 | # glue works
2 |
3 | Code
4 | formatter_glue("malformed {")
5 | Condition
6 | Error in `h()`:
7 | ! `glue` failed in `formatter_glue` on:
8 |
9 | chr "malformed {"
10 |
11 | Raw error message:
12 |
13 | Expecting '}'
14 |
15 | Please consider using another `log_formatter` or `skip_formatter` on strings with curly braces.
16 |
17 | # glue gives informative error if message contains curlies
18 |
19 | Code
20 | log_info("hi{")
21 | Condition
22 | Error in `h()`:
23 | ! `glue` failed in `formatter_glue` on:
24 |
25 | chr "hi{"
26 |
27 | Raw error message:
28 |
29 | Expecting '}'
30 |
31 | Please consider using another `log_formatter` or `skip_formatter` on strings with curly braces.
32 |
33 | # glue_safe works
34 |
35 | Code
36 | formatter_glue_safe("Hi {42}")
37 | Condition
38 | Error in `value[[3L]]()`:
39 | ! `glue_safe` failed in `formatter_glue_safe` on:
40 |
41 | chr "Hi {42}"
42 |
43 | Raw error message:
44 |
45 | object '42' not found
46 |
47 | Please consider using another `log_formatter` or `skip_formatter` on strings with curly braces.
48 | Code
49 | formatter_glue_safe("malformed {")
50 | Condition
51 | Error in `value[[3L]]()`:
52 | ! `glue_safe` failed in `formatter_glue_safe` on:
53 |
54 | chr "malformed {"
55 |
56 | Raw error message:
57 |
58 | Expecting '}'
59 |
60 | Please consider using another `log_formatter` or `skip_formatter` on strings with curly braces.
61 |
62 | # sprintf works
63 |
64 | Code
65 | formatter_sprintf("%s and %i", 1)
66 | Condition
67 | Error in `sprintf()`:
68 | ! too few arguments
69 |
70 | # skip formatter
71 |
72 | Code
73 | log_info(skip_formatter("hi {x}", x = 4))
74 | Condition
75 | Error in `skip_formatter()`:
76 | ! Cannot skip the formatter function if further arguments are passed besides the actual log message(s)
77 |
78 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/helpers.md:
--------------------------------------------------------------------------------
1 | # log failure
2 |
3 | Code
4 | capture.output(log_failure(foobar))
5 | Condition
6 | Error:
7 | ! object 'foobar' not found
8 |
9 | # log with separator
10 |
11 | Code
12 | log_with_separator(42)
13 | Output
14 | INFO ===========================================================================
15 | INFO = 42 =
16 | INFO ===========================================================================
17 | Code
18 | log_with_separator(42, separator = "|")
19 | Output
20 | INFO |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
21 | INFO | 42 |
22 | INFO |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
23 |
24 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/hooks.md:
--------------------------------------------------------------------------------
1 | # log_messages
2 |
3 | Code
4 | writeLines(eval_outside("log_messages()", "message(42)"))
5 | Output
6 | INFO 42
7 |
8 | # log_warnings
9 |
10 | Code
11 | writeLines(eval_outside("log_warnings(TRUE)", "warning(42)", "log(-1)"))
12 | Output
13 | WARN 42
14 | WARN NaNs produced
15 |
16 | # log_errors
17 |
18 | Code
19 | writeLines(eval_outside("log_errors()", "stop(42)"))
20 | Output
21 | ERROR 42
22 | Code
23 | writeLines(eval_outside("log_errors()", "foobar"))
24 | Output
25 | ERROR object 'foobar' not found
26 | Code
27 | writeLines(eval_outside("log_errors()", "f<-function(x) {42 * \"foobar\"}; f()"))
28 | Output
29 | ERROR non-numeric argument to binary operator
30 | Code
31 | writeLines(eval_outside("log_errors(traceback = TRUE)",
32 | "source(\"helper.R\", keep.source = TRUE)", "function_that_fails()"))
33 | Output
34 | ERROR I'm failing
35 | ERROR Traceback:
36 | ERROR 2: stop("I'm failing") at helper.R #46
37 | ERROR 1: function_that_fails()
38 |
39 | # shiny input initialization is detected
40 |
41 | Code
42 | writeLines(obs)
43 | Output
44 | INFO mock-session Default Shiny inputs initialized: {}
45 |
46 | # shiny input initialization is detected with different log-level
47 |
48 | Code
49 | writeLines(obs)
50 | Output
51 | ERROR mock-session Default Shiny inputs initialized: {}
52 |
53 | # shiny input change is detected
54 |
55 | Code
56 | writeLines(obs)
57 | Output
58 | INFO mock-session Default Shiny inputs initialized: {}
59 | INFO mock-session Shiny input change detected in a: NULL -> 2
60 |
61 | # shiny input change is logged with different level
62 |
63 | Code
64 | writeLines(obs)
65 | Output
66 | ERROR mock-session Default Shiny inputs initialized: {}
67 | ERROR mock-session Shiny input change detected in a: NULL -> 2
68 |
69 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/layouts.md:
--------------------------------------------------------------------------------
1 | # JSON layout warns if you include msg
2 |
3 | Code
4 | layout <- layout_json(fields = "msg")
5 | Condition
6 | Warning in `layout_json()`:
7 | 'msg' is always automatically included
8 |
9 | # must throw errors
10 |
11 | Code
12 | layout_simple(FOOBAR)
13 | Condition
14 | Error:
15 | ! object 'FOOBAR' not found
16 | Code
17 | layout_simple(42)
18 | Condition
19 | Error in `layout_simple()`:
20 | ! argument "msg" is missing, with no default
21 | Code
22 | layout_simple(msg = "foobar")
23 | Condition
24 | Error in `layout_simple()`:
25 | ! argument "level" is missing, with no default
26 |
27 | ---
28 |
29 | Code
30 | layout_glue(FOOBAR)
31 | Condition
32 | Error:
33 | ! object 'FOOBAR' not found
34 | Code
35 | layout_glue(42)
36 | Condition
37 | Error in `layout_glue()`:
38 | ! Invalid log level, see ?log_levels
39 | Code
40 | layout_glue(msg = "foobar")
41 | Condition
42 | Error in `layout_glue()`:
43 | ! argument "level" is missing, with no default
44 | Code
45 | layout_glue(level = 53, msg = "foobar")
46 | Condition
47 | Error in `layout_glue()`:
48 | ! Invalid log level, see ?log_levels
49 |
50 | # log_info() captures local info
51 |
52 | Code
53 | log_info("foobar")
54 | Output
55 | logger / global / logger / eval / eval(expr, envir)
56 | Code
57 | f()
58 | Output
59 | logger / global / logger / f / f()
60 | Code
61 | g()
62 | Output
63 | logger / global / logger / f / f()
64 |
65 | # log_info() captures package info
66 |
67 | Code
68 | logger_tester_function(INFO, "x = ")
69 | Output
70 | logger.tester INFO x = 0.0807501375675201
71 | Code
72 | logger_info_tester_function("everything = ")
73 | Output
74 | logger.tester INFO everything = 42
75 |
76 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/logger.md:
--------------------------------------------------------------------------------
1 | # setters check inputs
2 |
3 | Code
4 | log_appender(1)
5 | Condition
6 | Error in `log_appender()`:
7 | ! `appender` must be a function
8 | Code
9 | log_formatter(1)
10 | Condition
11 | Error in `log_formatter()`:
12 | ! `formatter` must be a function
13 | Code
14 | log_layout(1)
15 | Condition
16 | Error in `log_layout()`:
17 | ! `layout` must be a function
18 | Code
19 | log_threshold("x")
20 | Condition
21 | Error in `validate_log_level()`:
22 | ! Invalid log level
23 |
24 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/try.md:
--------------------------------------------------------------------------------
1 | # %except% logs errors and returns default value
2 |
3 | Code
4 | out <- f()
5 | Output
6 | except / global / logger / f / f()
7 | WARN Running '1' as 'FunDoesNotExist(1:10)' failed: 'could not find function "FunDoesNotExist"'
8 |
9 |
--------------------------------------------------------------------------------
/tests/testthat/_snaps/utils.md:
--------------------------------------------------------------------------------
1 | # fail_on_missing_package
2 |
3 | Code
4 | fail_on_missing_package("logger", "9.9.9", call = quote(f()))
5 | Condition
6 | Error:
7 | ! Please install min. 9.9.9 version of logger to use f
8 | Code
9 | fail_on_missing_package("an.R.package-that-doesNotExists", call = quote(f()))
10 | Condition
11 | Error:
12 | ! Please install the 'an.R.package-that-doesNotExists' package to use f
13 |
14 | # validate_log_level
15 |
16 | Code
17 | validate_log_level("FOOBAR")
18 | Condition
19 | Error in `validate_log_level()`:
20 | ! Invalid log level
21 |
22 |
--------------------------------------------------------------------------------
/tests/testthat/helper.R:
--------------------------------------------------------------------------------
1 | # Do not move this to another line as the location of this piece of code is tested for
2 | test_info <- function() {
3 | log_info("TEST")
4 | }
5 |
6 | local_test_logger <- function(threshold = INFO,
7 | formatter = formatter_glue,
8 | layout = layout_simple,
9 | appender = appender_stdout,
10 | namespace = "global",
11 | frame = parent.frame()) {
12 | old <- namespaces[[namespace]]
13 |
14 | namespaces[[namespace]] <- list(
15 | default = list(
16 | threshold = as.loglevel(threshold),
17 | layout = layout,
18 | formatter = formatter,
19 | appender = appender
20 | )
21 | )
22 |
23 | withr::defer(namespaces[[namespace]] <- old, frame)
24 | invisible()
25 | }
26 |
27 | eval_outside <- function(...) {
28 | input <- normalizePath(withr::local_tempfile(lines = character()), winslash = "/")
29 | output <- normalizePath(withr::local_tempfile(lines = character()), winslash = "/")
30 | writeLines(con = input, c(
31 | "library(logger)",
32 | "log_layout(layout_glue_generator('{level} {msg}'))",
33 | paste0("log_appender(appender_file('", output, "'))"),
34 | ...
35 | ))
36 | path <- file.path(R.home("bin"), "Rscript")
37 | if (Sys.info()[["sysname"]] == "Windows") {
38 | path <- paste0(path, ".exe")
39 | }
40 | suppressWarnings(system2(path, input, stdout = TRUE, stderr = TRUE))
41 | readLines(output)
42 | }
43 |
44 | # This function is needed to test traceback logging
45 | function_that_fails <- function() {
46 | stop("I'm failing")
47 | }
48 |
--------------------------------------------------------------------------------
/tests/testthat/test-appenders.R:
--------------------------------------------------------------------------------
1 | test_that("append to file", {
2 | t <- withr::local_tempfile()
3 | local_test_logger(
4 | appender = appender_file(t),
5 | layout = layout_glue_generator("{level} {msg}"),
6 | threshold = TRACE
7 | )
8 | log_info("foobar")
9 | log_info("{1:2}")
10 | expect_equal(length(readLines(t)), 3)
11 | expect_equal(readLines(t)[1], "INFO foobar")
12 | expect_equal(readLines(t)[3], "INFO 2")
13 | })
14 |
15 | test_that("overwrite file", {
16 | t <- withr::local_tempfile()
17 | local_test_logger(
18 | appender = appender_file(t, append = FALSE),
19 | layout = layout_glue_generator("{level} {msg}"),
20 | threshold = TRACE
21 | )
22 |
23 | log_info("foobar")
24 | log_info("{1:2}")
25 | expect_equal(length(readLines(t)), 2)
26 | expect_equal(readLines(t), c("INFO 1", "INFO 2"))
27 |
28 | log_info("42")
29 | expect_equal(length(readLines(t)), 1)
30 | expect_equal(readLines(t), "INFO 42")
31 | })
32 |
33 | test_that("append to file + print to console", {
34 | t <- withr::local_tempfile()
35 | local_test_logger(
36 | appender = appender_tee(t),
37 | layout = layout_glue_generator("{level} {msg}"),
38 | )
39 |
40 | expect_output(log_info("foobar"), "INFO foobar")
41 | capture.output(log_info("{1:2}"))
42 | expect_equal(length(readLines(t)), 3)
43 | expect_equal(readLines(t)[1], "INFO foobar")
44 | })
45 |
46 | test_that("logrotate", {
47 | t <- withr::local_tempdir()
48 | f <- file.path(t, "log")
49 | local_test_logger(
50 | appender = appender_file(f, max_lines = 2, max_files = 5L),
51 | layout = layout_glue_generator("{msg}"),
52 | threshold = TRACE
53 | )
54 |
55 | for (i in 1:24) log_info(i)
56 | expect_equal(length(readLines(f)), 2)
57 | expect_equal(length(list.files(t)), 5)
58 | expect_equal(readLines(f), c("23", "24"))
59 | log_info("42")
60 | expect_equal(length(readLines(f)), 1)
61 | expect_equal(readLines(f), "42")
62 | })
63 |
64 | test_that("async logging", {
65 | skip_on_cran()
66 |
67 | t <- withr::local_tempfile()
68 | local_test_logger(
69 | layout = layout_blank,
70 | appender = appender_async(appender_file(file = t))
71 | )
72 |
73 | for (i in 1:5) log_info(i)
74 | Sys.sleep(1)
75 |
76 | expect_equal(readLines(t)[1], "1")
77 | expect_equal(length(readLines(t)), 5)
78 | })
79 |
--------------------------------------------------------------------------------
/tests/testthat/test-eval.R:
--------------------------------------------------------------------------------
1 | test_that("single line", {
2 | local_test_logger(layout = layout_glue_generator("{level} {msg}"))
3 |
4 | expect_output(log_eval(4, INFO), sprintf("INFO %s => %s", shQuote(4), shQuote(4)))
5 | })
6 |
7 | test_that("multi line", {
8 | local_test_logger(layout = layout_glue_generator("{level} {msg}"))
9 |
10 | expect_output(log_eval(4, INFO, multiline = TRUE), "Running expression")
11 | expect_output(log_eval(4, INFO, multiline = TRUE), "Results:")
12 | expect_output(log_eval(4, INFO, multiline = TRUE), "INFO 4")
13 | })
14 |
15 | test_that("invisible return", {
16 | local_test_logger(layout = layout_glue_generator("{level} {msg}"))
17 | expect_output(log_eval(require(logger), INFO), sprintf(
18 | "INFO %s => %s",
19 | shQuote("require\\(logger\\)"),
20 | shQuote(TRUE)
21 | ))
22 | })
23 |
24 | test_that("lower log level", {
25 | local_test_logger(TRACE, layout = layout_glue_generator("{level} {msg}"))
26 | expect_output(log_eval(4), sprintf("TRACE %s => %s", shQuote(4), shQuote(4)))
27 | })
28 |
--------------------------------------------------------------------------------
/tests/testthat/test-formatters.R:
--------------------------------------------------------------------------------
1 | test_that("glue works", {
2 | local_test_logger(formatter = formatter_glue)
3 | a <- 43
4 |
5 | expect_equal(formatter_glue("Hi"), "Hi")
6 | expect_equal(formatter_glue(" Hi"), " Hi")
7 | expect_equal(formatter_glue("1 + {1}"), "1 + 1")
8 | expect_equal(formatter_glue("{1:2}"), as.character(1:2))
9 | expect_equal(formatter_glue("pi is {round(pi, 2)}"), "pi is 3.14")
10 | expect_equal(formatter_glue("Hi {42}"), "Hi 42")
11 | expect_equal(formatter_glue("Hi {a}", a = 42), "Hi 42")
12 | expect_equal(formatter_glue("Hi {1:2}"), paste("Hi", 1:2))
13 |
14 | expect_output(do.call(logger, namespaces$global[[1]])(INFO, 42), "42")
15 | expect_output(do.call(logger, namespaces$global[[1]])(INFO, "Hi {a}"), "43")
16 |
17 | expect_equal(formatter_glue("Hi {a}"), "Hi 43")
18 | expect_output(log_info("Hi {a}"), "43")
19 | expect_output(log_warn("Hi {a}"), "43")
20 | f <- function() log_info("Hi {a}")
21 | expect_output(f(), "43")
22 |
23 | local_test_logger(
24 | formatter = formatter_glue,
25 | appender = appender_void,
26 | )
27 | expect_snapshot(formatter_glue("malformed {"), error = TRUE)
28 | expect_no_error(formatter_glue("malformed {{"))
29 |
30 | ## nolint start
31 | ## disabled for https://github.com/atalv/azlogr/issues/35
32 | ## expect_warning(formatter_glue(NULL))
33 | ## expect_warning(log_info(NULL))
34 | ## expect_warning(log_info(a = 42, b = "foobar"))
35 | ## nolint end
36 | })
37 |
38 | test_that("glue gives informative error if message contains curlies", {
39 | local_test_logger(formatter = formatter_glue)
40 | expect_snapshot(log_info("hi{"), error = TRUE)
41 | })
42 |
43 | test_that("glue_safe works", {
44 | local_test_logger(formatter = formatter_glue_safe)
45 |
46 | expect_equal(formatter_glue_safe("Hi"), "Hi")
47 | expect_equal(formatter_glue_safe(" Hi"), " Hi")
48 | expect_equal(formatter_glue_safe("Hi {a}", a = 42), "Hi 42")
49 |
50 | a <- 43
51 | expect_equal(formatter_glue_safe("Hi {a}"), "Hi 43")
52 | expect_output(log_info("Hi {a}"), "43")
53 | expect_output(log_warn("Hi {a}"), "43")
54 | f <- function() log_info("Hi {a}")
55 | expect_output(f(), "43")
56 |
57 | expect_snapshot(error = TRUE, {
58 | formatter_glue_safe("Hi {42}")
59 | formatter_glue_safe("malformed {")
60 | })
61 | expect_no_error(formatter_glue_safe("malformed {{"))
62 | })
63 |
64 | test_that("sprintf works", {
65 | local_test_logger(formatter = formatter_sprintf)
66 |
67 | expect_equal(formatter_sprintf("Hi"), "Hi")
68 | expect_equal(formatter_sprintf("Hi %s", 42), "Hi 42")
69 | expect_equal(formatter_sprintf("Hi %s", 1:2), paste("Hi", 1:2))
70 | expect_equal(formatter_sprintf("1 + %s", 1), "1 + 1")
71 | expect_equal(formatter_sprintf("=>%2i", 2), "=> 2")
72 | expect_equal(formatter_sprintf("%s", 1:2), as.character(1:2))
73 | expect_equal(formatter_sprintf("pi is %s", round(pi, 2)), "pi is 3.14")
74 | expect_equal(formatter_sprintf("pi is %1.2f", pi), "pi is 3.14")
75 |
76 | expect_snapshot(formatter_sprintf("%s and %i", 1), error = TRUE)
77 | expect_equal(formatter_sprintf("%s and %i", 1, 2), "1 and 2")
78 |
79 | a <- 43
80 | expect_output(log_info("Hi %s", a), "43")
81 | expect_equal(formatter_sprintf("Hi %s", a), "Hi 43")
82 | f <- function() log_info("Hi %s", a)
83 | expect_output(f(), "43")
84 | })
85 |
86 |
87 | test_that("glue+sprintf works", {
88 | result <- c(
89 | "Hi foo, did you know that 2*4=8?",
90 | "Hi bar, did you know that 2*4=8?"
91 | )
92 |
93 | expect_equal(formatter_glue_or_sprintf("Hi ", "{c('foo', 'bar')}, did you know that 2*4={2*4}?"), result)
94 | expect_equal(formatter_glue_or_sprintf("Hi {c('foo', 'bar')}, did you know that 2*4={2*4}?"), result)
95 | expect_equal(formatter_glue_or_sprintf("Hi {c('foo', 'bar')}, did you know that 2*4=%s?", 2 * 4), result)
96 | expect_equal(formatter_glue_or_sprintf("Hi %s, did you know that 2*4={2*4}?", c("foo", "bar")), result)
97 | expect_equal(formatter_glue_or_sprintf("Hi %s, did you know that 2*4=%s?", c("foo", "bar"), 2 * 4), result)
98 |
99 | expect_equal(formatter_glue_or_sprintf("%s and %i"), "%s and %i")
100 | expect_equal(formatter_glue_or_sprintf("%s and %i", 1), "%s and %i")
101 | expect_equal(formatter_glue_or_sprintf("fun{fun}"), "fun{fun}")
102 |
103 | for (fn in c(formatter_sprintf, formatter_glue_or_sprintf)) {
104 | local_test_logger(formatter = fn, appender = appender_void)
105 | expect_no_error(log_info(character(0)))
106 |
107 | local_test_logger(formatter = fn)
108 | expect_output(log_info(character(0)), "INFO")
109 | }
110 | })
111 |
112 | test_that("cli works", {
113 | local_test_logger(formatter = formatter_cli)
114 |
115 | a <- 43
116 |
117 | expect_equal(formatter_cli("Hi"), "Hi")
118 | expect_equal(formatter_cli("{.arg Hi}"), "`Hi`")
119 | expect_equal(formatter_cli("1 + {1}"), "1 + 1")
120 | expect_equal(formatter_cli("{1:2}"), "1 and 2")
121 | expect_equal(formatter_cli("pi is {round(pi, 2)}"), "pi is 3.14")
122 | expect_equal(formatter_cli("Hi {42}"), "Hi 42")
123 | expect_equal(formatter_cli("Hi {1:2}"), paste("Hi 1 and 2"))
124 |
125 | expect_output(do.call(logger, namespaces$global[[1]])(INFO, 42), "42")
126 | expect_output(do.call(logger, namespaces$global[[1]])(INFO, "Hi {a}"), "43")
127 |
128 | expect_equal(formatter_cli("Hi {a}"), "Hi 43")
129 | expect_output(log_info("Hi {a}"), "43")
130 | expect_output(log_warn("Hi {a}"), "43")
131 | f <- function() log_info("Hi {a}")
132 | expect_output(f(), "43")
133 | })
134 |
135 | test_that("formatter_logging works", {
136 | local_test_logger(formatter = formatter_logging)
137 |
138 | expect_output(log_info("42"), "42")
139 | expect_output(log_info(42), "42")
140 | expect_output(log_info(4 + 2), "4 \\+ 2")
141 | expect_output(log_info(4 + 2), "6")
142 | expect_output(log_info("foo %s", "bar"), "foo bar")
143 | expect_output(log_info(12, 100 + 100, 2 * 2), "12")
144 | expect_output(log_info(12, 100 + 100, 2 * 2), "100 \\+ 100")
145 | expect_output(log_info(12, 100 + 100, 2 * 2), "200")
146 | expect_output(log_info(12, 100 + 100, 2 * 2), "2 \\* 2")
147 | expect_output(log_info(12, 100 + 100, 2 * 2), "4")
148 | })
149 |
150 | test_that("special chars in the text work", {
151 | array <- "[1, 2, 3, 4]"
152 | object <- '{"x": 1, "y": 2}'
153 | expect_equal(formatter_glue("JSON: {array}"), paste0("JSON: ", array))
154 | expect_equal(formatter_glue("JSON: {object}"), paste0("JSON: ", object))
155 |
156 | local_test_logger()
157 | expect_output(log_info("JSON: {array}"), paste0("JSON: ", array), fixed = TRUE)
158 | expect_output(log_info("JSON: {object}"), paste0("JSON: ", object), fixed = TRUE)
159 | })
160 |
161 | test_that("pander formatter", {
162 | local_test_logger(formatter = formatter_pander)
163 | # pander partially matches coef to coefficient
164 | withr::local_options(warnPartialMatchDollar = FALSE)
165 |
166 | expect_output(log_info(42), "_42_")
167 | expect_output(log_info("42"), "42")
168 | expect_output(log_info(head(iris)), "Sepal.Length")
169 | expect_output(log_info(lm(hp ~ wt, mtcars)), "Fitting linear model")
170 | })
171 |
172 | test_that("paste formatter in actual logs", {
173 | local_test_logger(formatter = formatter_paste)
174 | expect_output(log_info("hi", 5), "hi 5")
175 | })
176 |
177 | test_that("skip formatter", {
178 | local_test_logger(formatter = formatter_glue)
179 | expect_output(log_info(skip_formatter("hi {pi}")), "hi \\{pi\\}")
180 | expect_snapshot(log_info(skip_formatter("hi {x}", x = 4)), error = TRUE)
181 | })
182 |
183 | test_that("skip formatter", {
184 | local_test_logger(formatter = formatter_json)
185 | expect_output(log_info(skip_formatter("hi {pi}")), "hi \\{pi\\}")
186 | expect_output(log_info(x = 1), '\\{"x":1\\}')
187 | })
188 |
--------------------------------------------------------------------------------
/tests/testthat/test-helpers.R:
--------------------------------------------------------------------------------
1 | test_that("separator", {
2 | local_test_logger(layout = layout_blank)
3 | expect_output(log_separator(), "={80,80}")
4 |
5 | local_test_logger()
6 | expect_output(log_separator(separator = "-"), "---")
7 | expect_output(log_separator(), "INFO")
8 | expect_output(log_separator(WARN), "WARN")
9 | })
10 |
11 | test_that("tictoc", {
12 | local_test_logger()
13 | local_mocked_bindings(Sys.time = function() as.POSIXct("2024-01-01 00:00:00"))
14 | expect_output(log_tictoc(), "timer tic 0 secs")
15 | ## simulate time passing
16 | local_mocked_bindings(Sys.time = function() as.POSIXct("2024-01-01 00:01:00"))
17 | expect_output(log_tictoc(), "timer toc 1 mins")
18 | })
19 |
20 | test_that("elapsed", {
21 | local_test_logger()
22 |
23 | proc_null <- proc.time() * 0
24 |
25 | with_mocked_bindings(
26 | expect_output(log_elapsed(), "timer 2 secs elapsed"),
27 | proc.time = function() proc_null + 2
28 | )
29 |
30 | with_mocked_bindings(
31 | expect_output(log_elapsed_start(), "starting global timer"),
32 | proc.time = function() proc_null + 1
33 | )
34 |
35 | with_mocked_bindings(
36 | expect_output(log_elapsed(), "timer 1 secs elapsed"),
37 | proc.time = function() proc_null + 2
38 | )
39 |
40 | with_mocked_bindings(
41 | expect_silent(log_elapsed_start(quiet = TRUE)),
42 | proc.time = function() proc_null
43 | )
44 | })
45 |
46 | test_that("log with separator", {
47 | local_test_logger()
48 | expect_output(log_with_separator(42), "===")
49 | expect_output(log_with_separator("Boo!", level = FATAL, width = 120), width = 120)
50 | })
51 |
52 | test_that("log failure", {
53 | skip_if_not(getRversion() >= "4.3") # error call changed
54 |
55 | local_test_logger()
56 | expect_output(log_failure("foobar"), NA)
57 | expect_output(try(log_failure(foobar), silent = TRUE), "ERROR.*foobar")
58 | expect_no_error(log_failure("foobar"))
59 | expect_snapshot(capture.output(log_failure(foobar)), error = TRUE)
60 | })
61 |
62 | test_that("log with separator", {
63 | local_test_logger(layout = layout_glue_generator("{level} {msg}"))
64 |
65 | expect_snapshot({
66 | log_with_separator(42)
67 | log_with_separator(42, separator = "|")
68 | })
69 | })
70 |
71 |
72 | test_that("single line", {
73 | local_test_logger(layout = layout_glue_generator("{level} {msg}"))
74 |
75 | expect_output(log_eval(4, INFO), sprintf("INFO %s => %s", shQuote(4), shQuote(4)))
76 | })
77 |
78 | test_that("multi line", {
79 | local_test_logger(layout = layout_glue_generator("{level} {msg}"))
80 |
81 | expect_output(log_eval(4, INFO, multiline = TRUE), "Running expression")
82 | expect_output(log_eval(4, INFO, multiline = TRUE), "Results:")
83 | expect_output(log_eval(4, INFO, multiline = TRUE), "INFO 4")
84 | })
85 |
86 | test_that("invisible return", {
87 | local_test_logger(layout = layout_glue_generator("{level} {msg}"))
88 | expect_output(log_eval(require(logger), INFO), sprintf(
89 | "INFO %s => %s",
90 | shQuote("require\\(logger\\)"),
91 | shQuote(TRUE)
92 | ))
93 | })
94 |
95 | test_that("lower log level", {
96 | local_test_logger(TRACE, layout = layout_glue_generator("{level} {msg}"))
97 | expect_output(log_eval(4), sprintf("TRACE %s => %s", shQuote(4), shQuote(4)))
98 | })
99 |
100 | test_that("knitr hook gets applied", {
101 | local_test_logger()
102 | mock_doc <- c(
103 | "```{r, include = FALSE}",
104 | "library(logger)",
105 | "log_chunk_time()",
106 | "```",
107 | "```{r}",
108 | "Sys.sleep(0.2)",
109 | "```"
110 | )
111 | expect_output(knitr::knit(text = mock_doc, quiet = TRUE), "global timer 0.2")
112 | })
113 |
--------------------------------------------------------------------------------
/tests/testthat/test-hooks.R:
--------------------------------------------------------------------------------
1 | test_that("log_messages", {
2 | expect_snapshot({
3 | writeLines(eval_outside("log_messages()", "message(42)"))
4 | })
5 | })
6 |
7 | test_that("log_warnings", {
8 | expect_snapshot({
9 | writeLines(eval_outside("log_warnings(TRUE)", "warning(42)", "log(-1)"))
10 | })
11 | })
12 |
13 | test_that("log_errors", {
14 | expect_snapshot({
15 | writeLines(eval_outside("log_errors()", "stop(42)"))
16 | writeLines(eval_outside("log_errors()", "foobar"))
17 | writeLines(eval_outside("log_errors()", 'f<-function(x) {42 * "foobar"}; f()'))
18 | writeLines(eval_outside("log_errors(traceback = TRUE)",
19 | 'source("helper.R", keep.source = TRUE)',
20 | "function_that_fails()"))
21 | })
22 | })
23 |
24 | test_that("shiny input initialization is detected", {
25 | obs <- eval_outside("
26 | .globals <- shiny:::.globals
27 | .globals$appState <- new.env(parent = emptyenv())
28 | server <- function(input, output, session) {
29 | logger::log_shiny_input_changes(input)
30 | }
31 | shiny::testServer(server, {})
32 | ")
33 | expect_snapshot(writeLines(obs))
34 | })
35 |
36 | test_that("shiny input initialization is detected with different log-level", {
37 | obs <- eval_outside("
38 | .globals <- shiny:::.globals
39 | .globals$appState <- new.env(parent = emptyenv())
40 | server <- function(input, output, session) {
41 | logger::log_shiny_input_changes(input, level = logger::ERROR)
42 | }
43 | shiny::testServer(server, {})
44 | ")
45 | expect_snapshot(writeLines(obs))
46 | })
47 |
48 | test_that("shiny input change is detected", {
49 | obs <- eval_outside("
50 | .globals <- shiny:::.globals
51 | .globals$appState <- new.env(parent = emptyenv())
52 | server <- function(input, output, session) {
53 | logger::log_shiny_input_changes(input)
54 | x <- shiny::reactive(input$a)
55 | }
56 | shiny::testServer(server, {
57 | session$setInputs(a = 2)
58 | })
59 | ")
60 | expect_snapshot(writeLines(obs))
61 | })
62 |
63 | test_that("shiny input change is logged with different level", {
64 | obs <- eval_outside("
65 | .globals <- shiny:::.globals
66 | .globals$appState <- new.env(parent = emptyenv())
67 | server <- function(input, output, session) {
68 | logger::log_shiny_input_changes(input, level = logger::ERROR)
69 | x <- shiny::reactive(input$a)
70 | }
71 | shiny::testServer(server, {
72 | session$setInputs(a = 2)
73 | })
74 | ")
75 | expect_snapshot(writeLines(obs))
76 | })
77 |
--------------------------------------------------------------------------------
/tests/testthat/test-layouts.R:
--------------------------------------------------------------------------------
1 | test_that("blank layout", {
2 | local_test_logger(layout = layout_blank)
3 | expect_output(log_info("foobar"), "foobar")
4 | expect_equal(capture.output(log_info("foobar")), "foobar")
5 | })
6 |
7 | test_that("colorized layout", {
8 | local_test_logger(layout = layout_glue_colors)
9 | expect_output(log_info("foobar"), "INFO")
10 | expect_output(log_info("foobar"), "foobar")
11 | expect_output(log_error("foobar"), "ERROR")
12 | expect_output(log_error("foobar"), "foobar")
13 | })
14 |
15 | test_that("metavars", {
16 | local_test_logger(layout = layout_glue_generator("{level} {ans} {fn}"))
17 |
18 | f_info <- function() log_info()
19 | expect_output(f_info(), "INFO global f_info()")
20 |
21 | f_warn <- function() log_warn()
22 | expect_output(f_warn(), "WARN global f_warn()")
23 |
24 | local_test_logger(layout = layout_glue_generator("{time}"))
25 | time <- as.POSIXct("2025-04-04 10:31:57 CEST")
26 | expect_output(log_info("test", .timestamp = time), "2025-04-04 10:31:57")
27 | })
28 |
29 | test_that("JSON layout", {
30 | local_test_logger(layout = layout_json(fields = "level"))
31 |
32 | out <- jsonlite::fromJSON(capture.output(log_info("foobar")))
33 | expect_equal(out, list(level = "INFO", msg = "foobar"))
34 | })
35 |
36 | test_that("JSON layout warns if you include msg", {
37 | expect_snapshot(layout <- layout_json(fields = "msg"))
38 | local_test_logger(layout = layout)
39 | out <- jsonlite::fromJSON(capture.output(log_info("foobar")))
40 | expect_equal(out, list(msg = "foobar"))
41 | })
42 |
43 | test_that("JSON parser layout", {
44 | local_test_logger(layout = layout_json_parser(fields = character()))
45 | expect_output(log_info(skip_formatter('{"x": 4}')), '{"x":4}', fixed = TRUE)
46 | })
47 |
48 | test_that("JSON parser layout can be renamed", {
49 | local_test_logger(layout = layout_json_parser(c(LEVEL = "level")))
50 | expect_output(log_info(skip_formatter('{"x": 4}')), '{"LEVEL":"INFO","x":4}', fixed = TRUE)
51 | })
52 |
53 | test_that("must throw errors", {
54 | skip_if_not(getRversion() >= "4.3") # error call changed
55 |
56 | expect_snapshot(error = TRUE, {
57 | layout_simple(FOOBAR)
58 | layout_simple(42)
59 | layout_simple(msg = "foobar")
60 | })
61 |
62 | expect_snapshot(error = TRUE, {
63 | layout_glue(FOOBAR)
64 | layout_glue(42)
65 | layout_glue(msg = "foobar")
66 | layout_glue(level = 53, msg = "foobar")
67 | })
68 | })
69 |
70 | test_that("logging layout", {
71 | local_test_logger(layout = layout_logging)
72 | expect_output(log_level(INFO, "foo", namespace = "bar"), "INFO:bar:foo")
73 | expect_output(log_info("foobar"), "INFO")
74 | expect_output(log_info("foo", namespace = "bar"), "foo")
75 | expect_output(log_info("foo", namespace = "bar"), "bar")
76 | expect_output(log_info("foo", namespace = "bar"), "INFO:bar:foo")
77 | })
78 |
79 | test_that("log_info() captures local info", {
80 | local_test_logger(
81 | layout = layout_glue_generator("{ns} / {ans} / {topenv} / {fn} / {call}")
82 | )
83 | f <- function() log_info("foobar")
84 | g <- function() f()
85 |
86 | expect_snapshot({
87 | log_info("foobar")
88 | f()
89 | g()
90 | })
91 | })
92 |
93 | test_that("log_info() captures package info", {
94 | devtools::load_all(
95 | system.file("demo-packages/logger-tester-package", package = "logger"),
96 | quiet = TRUE
97 | )
98 | withr::defer(devtools::unload("logger.tester"))
99 |
100 | local_test_logger(layout = layout_glue_generator("{ns} {level} {msg}"))
101 | expect_snapshot({
102 | logger_tester_function(INFO, "x = ")
103 | logger_info_tester_function("everything = ")
104 | })
105 | })
106 |
--------------------------------------------------------------------------------
/tests/testthat/test-logger-meta.R:
--------------------------------------------------------------------------------
1 | test_that("captures call/environment info", {
2 | f <- function(...) logger_meta_env()
3 | env <- f(x = 1)
4 |
5 | # values are computed lazily
6 | expect_type(substitute(fn, env), "language")
7 | expect_type(substitute(call, env), "language")
8 |
9 | # and give correct values
10 | expect_equal(env$fn, "f")
11 | expect_equal(env$call, "f(x = 1)")
12 | expect_equal(env$topenv, "logger")
13 | })
14 |
15 | test_that("captures namespace info", {
16 | env <- logger_meta_env(namespace = "testthat")
17 | expect_equal(env$ns, "testthat")
18 | expect_equal(env$ans, "global")
19 | expect_equal(env$ns_pkg_version, as.character(packageVersion("testthat")))
20 | })
21 |
22 | test_that("captures other environmental metadata", {
23 | env <- logger_meta_env()
24 | expect_equal(env$pid, Sys.getpid())
25 | expect_equal(env$r_version, as.character(getRversion()))
26 |
27 | sysinfo <- as.list(Sys.info())
28 | expect_equal(env$node, sysinfo$nodename)
29 | expect_equal(env$arch, sysinfo$machine)
30 | expect_equal(env$os_name, sysinfo$sysname)
31 | expect_equal(env$os_release, sysinfo$release)
32 | expect_equal(env$os_version, sysinfo$version)
33 | expect_equal(env$user, sysinfo$user)
34 |
35 | local_test_logger(layout = layout_glue_generator("{location$path}#{location$line}: {msg}"))
36 | expect_output(test_info(), file.path(getwd(), "helper.R#3: TEST"))
37 | })
38 |
--------------------------------------------------------------------------------
/tests/testthat/test-logger.R:
--------------------------------------------------------------------------------
1 | test_that("log levels", {
2 | local_test_logger(WARN)
3 |
4 | expect_output(log_fatal("foo"), "FATAL.*foo")
5 | expect_output(log_error("foo"), "ERROR.*foo")
6 | expect_output(log_warn("foo"), "WARN.*foo")
7 | expect_output(log_success("foo"), NA)
8 | expect_output(log_info("foo"), NA)
9 | expect_output(log_debug("foo"), NA)
10 | expect_output(log_trace("foo"), NA)
11 | expect_output(log_level("ERROR", "foo"), "ERROR.*foo")
12 | expect_output(log_level(ERROR, "foo"), "ERROR.*foo")
13 | expect_output(log_level(as.loglevel(ERROR), "foo"), "ERROR.*foo")
14 | expect_output(log_level(as.loglevel("ERROR"), "foo"), "ERROR.*foo")
15 | expect_output(log_level(as.loglevel(200L), "foo"), "ERROR.*foo")
16 | expect_output(log_level("TRACE", "foo"), NA)
17 | expect_output(log_level(TRACE, "foo"), NA)
18 | expect_output(log_level(as.loglevel(TRACE), "foo"), NA)
19 | expect_output(log_level(as.loglevel("TRACE"), "foo"), NA)
20 | expect_output(log_level(as.loglevel(600L), "foo"), NA)
21 | })
22 |
23 | test_that("log levels - OFF", {
24 | local_test_logger(OFF)
25 | expect_output(log_fatal("foo"), NA)
26 | expect_output(log_error("foo"), NA)
27 | expect_output(log_warn("foo"), NA)
28 | expect_output(log_success("foo"), NA)
29 | expect_output(log_info("foo"), NA)
30 | expect_output(log_debug("foo"), NA)
31 | expect_output(log_trace("foo"), NA)
32 | })
33 |
34 | test_that("log thresholds", {
35 | local_test_logger(TRACE)
36 | expect_output(log_fatal("foo"), "FATAL.*foo")
37 | expect_output(log_error("foo"), "ERROR.*foo")
38 | expect_output(log_warn("foo"), "WARN.*foo")
39 | expect_output(log_success("foo"), "SUCCESS.*foo")
40 | expect_output(log_info("foo"), "INFO.*foo")
41 | expect_output(log_debug("foo"), "DEBUG.*foo")
42 | expect_output(log_trace("foo"), "TRACE.*foo")
43 | })
44 |
45 | test_that("with log thresholds", {
46 | local_test_logger(WARN)
47 | expect_output(with_log_threshold(log_fatal("foo"), threshold = TRACE), "FATAL.*foo")
48 | expect_output(with_log_threshold(log_error("foo"), threshold = TRACE), "ERROR.*foo")
49 | expect_output(with_log_threshold(log_error("foo"), threshold = FATAL), NA)
50 | expect_output(with_log_threshold(log_error("foo"), threshold = INFO), "ERROR.*foo")
51 | expect_output(with_log_threshold(log_debug("foo"), threshold = INFO), NA)
52 | })
53 |
54 | test_that("simple glue layout with no threshold", {
55 | local_test_logger(TRACE, layout = layout_glue_generator("{level} {msg}"))
56 |
57 | expect_equal(capture.output(log_fatal("foobar")), "FATAL foobar")
58 | expect_equal(capture.output(log_error("foobar")), "ERROR foobar")
59 | expect_equal(capture.output(log_warn("foobar")), "WARN foobar")
60 | expect_equal(capture.output(log_info("foobar")), "INFO foobar")
61 | expect_equal(capture.output(log_debug("foobar")), "DEBUG foobar")
62 | expect_equal(capture.output(log_trace("foobar")), "TRACE foobar")
63 | })
64 |
65 | test_that("simple glue layout with threshold", {
66 | local_test_logger(INFO, layout = layout_glue_generator("{level} {msg}"))
67 | expect_equal(capture.output(log_fatal("foobar")), "FATAL foobar")
68 | expect_equal(capture.output(log_error("foobar")), "ERROR foobar")
69 | expect_equal(capture.output(log_warn("foobar")), "WARN foobar")
70 | expect_equal(capture.output(log_info("foobar")), "INFO foobar")
71 | expect_equal(capture.output(log_debug("foobar")), character())
72 | expect_equal(capture.output(log_trace("foobar")), character())
73 | })
74 |
75 | test_that("namespaces", {
76 | local_test_logger(ERROR, namespace = "custom", layout = layout_glue_generator("{level} {msg}"))
77 | expect_output(log_fatal("foobar", namespace = "custom"), "FATAL foobar")
78 | expect_output(log_error("foobar", namespace = "custom"), "ERROR foobar")
79 | expect_output(log_info("foobar", namespace = "custom"), NA)
80 | expect_output(log_debug("foobar", namespace = "custom"), NA)
81 |
82 | local_test_logger(INFO, namespace = "custom", layout = layout_glue_generator("{level} {msg}"))
83 | expect_output(log_info("foobar", namespace = "custom"), "INFO foobar")
84 | expect_output(log_debug("foobar", namespace = "custom"), NA)
85 |
86 | log_threshold(TRACE, namespace = log_namespaces())
87 | expect_output(log_debug("foobar", namespace = "custom"), "DEBUG foobar")
88 | })
89 |
90 | test_that("simple glue layout with threshold directly calling log", {
91 | local_test_logger(layout = layout_glue_generator("{level} {msg}"))
92 | expect_equal(capture.output(log_level(FATAL, "foobar")), "FATAL foobar")
93 | expect_equal(capture.output(log_level(ERROR, "foobar")), "ERROR foobar")
94 | expect_equal(capture.output(log_level(WARN, "foobar")), "WARN foobar")
95 | expect_equal(capture.output(log_level(INFO, "foobar")), "INFO foobar")
96 | expect_equal(capture.output(log_level(DEBUG, "foobar")), character())
97 | expect_equal(capture.output(log_level(TRACE, "foobar")), character())
98 | })
99 |
100 | test_that("built in variables: pid", {
101 | local_test_logger(layout = layout_glue_generator("{pid}"))
102 | expect_equal(capture.output(log_info("foobar")), as.character(Sys.getpid()))
103 | })
104 |
105 | test_that("built in variables: fn and call", {
106 | local_test_logger(layout = layout_glue_generator("{fn} / {call}"))
107 | f <- function() log_info("foobar")
108 | expect_output(f(), "f / f()")
109 | g <- function() f()
110 | expect_output(g(), "f / f()")
111 | g <- f
112 | expect_output(g(), "g / g()")
113 | })
114 |
115 | test_that("built in variables: namespace", {
116 | local_test_logger(layout = layout_glue_generator("{ns}"))
117 | expect_output(log_info("bar", namespace = "foo"), "foo")
118 |
119 | local_test_logger(layout = layout_glue_generator("{ans}"))
120 | expect_output(log_info("bar", namespace = "foo"), "global")
121 | })
122 |
123 | test_that("print.level", {
124 | expect_equal(capture.output(print(INFO)), "Log level: INFO")
125 | })
126 |
127 | test_that("config setter called from do.call", {
128 | local_test_logger()
129 |
130 | t <- withr::local_tempfile()
131 | expect_no_error(do.call(log_appender, list(appender_file(t))))
132 | log_info(42)
133 | expect_length(readLines(t), 1)
134 | expect_no_error(do.call(log_threshold, list(ERROR)))
135 | log_info(42)
136 | expect_length(readLines(t), 1)
137 | expect_no_error(do.call(log_threshold, list(INFO)))
138 | log_info(42)
139 | expect_length(readLines(t), 2)
140 | expect_no_error(do.call(log_layout, list(formatter_paste)))
141 | log_info(42)
142 | expect_length(readLines(t), 3)
143 | })
144 |
145 | test_that("providing log_level() args to wrappers diretly is OK", {
146 | local_test_logger(WARN)
147 | expect_silent(log_info("{Sepal.Length}", .topenv = list2env(iris)))
148 | })
149 |
150 | test_that("setters check inputs", {
151 | expect_snapshot(error = TRUE, {
152 | log_appender(1)
153 | log_formatter(1)
154 | log_layout(1)
155 | log_threshold("x")
156 | })
157 | })
158 |
--------------------------------------------------------------------------------
/tests/testthat/test-return.R:
--------------------------------------------------------------------------------
1 | glue_or_sprintf_result <- c(
2 | "Hi foo, did you know that 2*4=8?",
3 | "Hi bar, did you know that 2*4=8?"
4 | )
5 |
6 | test_that("return value is formatted string", {
7 | local_test_logger(appender = appender_file(withr::local_tempfile()))
8 |
9 | log_formatter(formatter_glue)
10 | expect_equal(log_info("pi is {round(pi, 2)}")[[1]]$message, "pi is 3.14")
11 | expect_match(log_info("pi is {round(pi, 2)}")[[1]]$record, "INFO [[0-9: -]*] pi is 3.14")
12 | log_formatter(formatter_paste, index = 2)
13 | expect_equal(log_info("pi is {round(pi, 2)}")[[1]]$message, "pi is 3.14")
14 | expect_equal(log_info("pi is {round(pi, 2)}")[[2]]$message, "pi is {round(pi, 2)}")
15 | })
16 |
--------------------------------------------------------------------------------
/tests/testthat/test-try.R:
--------------------------------------------------------------------------------
1 | test_that("%except% logs errors and returns default value", {
2 | local_test_logger(layout = layout_glue_generator("{ns} / {ans} / {topenv} / {fn} / {call}\n{level} {msg}"))
3 |
4 | f <- function() {
5 | FunDoesNotExist(1:10) %except% 1
6 | }
7 |
8 | expect_snapshot(out <- f())
9 | expect_equal(out, 1)
10 | })
11 |
12 | test_that("%except% returns value when no error", {
13 | expect_equal(5 %except% 1, 5)
14 | })
15 |
--------------------------------------------------------------------------------
/tests/testthat/test-utils.R:
--------------------------------------------------------------------------------
1 | test_that("fail_on_missing_package", {
2 | expect_no_error(fail_on_missing_package("logger"))
3 |
4 | expect_snapshot(error = TRUE, {
5 | fail_on_missing_package("logger", "9.9.9", call = quote(f()))
6 | fail_on_missing_package("an.R.package-that-doesNotExists", call = quote(f()))
7 | })
8 | })
9 |
10 | test_that("validate_log_level", {
11 | expect_equal(validate_log_level(ERROR), ERROR)
12 | expect_equal(validate_log_level("ERROR"), ERROR)
13 | expect_snapshot(validate_log_level("FOOBAR"), error = TRUE)
14 | })
15 |
16 | test_that("catch_base_log", {
17 | expect_true(nchar(catch_base_log(ERROR, NA_character_)) == 28)
18 | expect_true(nchar(catch_base_log(INFO, NA_character_)) == 27)
19 | local_test_logger(layout = layout_blank)
20 | expect_true(nchar(catch_base_log(INFO, NA_character_)) == 0)
21 |
22 | local_test_logger(layout = layout_glue_generator(format = "{namespace}/{fn}"))
23 | expect_equal(catch_base_log(INFO, "TEMP", .topcall = NA), "global/NA")
24 | expect_equal(catch_base_log(INFO, "TEMP", .topcall = quote(f())), "global/f")
25 | })
26 |
--------------------------------------------------------------------------------
/vignettes/Intro.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Introduction to logger"
3 | output: rmarkdown::html_vignette
4 | vignette: >
5 | %\VignetteIndexEntry{Introduction to logger}
6 | %\VignetteEngine{knitr::rmarkdown}
7 | %\VignetteEncoding{UTF-8}
8 | ---
9 |
10 | ```{r setup, include = FALSE}
11 | knitr::opts_chunk$set(
12 | collapse = TRUE,
13 | comment = "#>"
14 | )
15 |
16 | library(logger)
17 | log_appender(appender_stdout)
18 | ```
19 |
20 | If you are not only using R in the interactive console for ad-hoc data analysis, but running eg batch jobs (for ETL, reporting, modeling, forecasting etc) as well, then logging the status(changes) of your script is a must so that later on you can review / debug what have happened.
21 |
22 | For most cases, it's enough to load the package and use the functions with the `log` prefix to log important and not so important messages, for example:
23 |
24 | ```{r}
25 | library(logger)
26 | log_info("Loading data")
27 | data(mtcars)
28 | log_info("The dataset includes {nrow(mtcars)} rows")
29 | if (max(mtcars$hp) < 1000) {
30 | log_warn("Oh, no! There are no cars with more than 1K horsepower in the dataset :/")
31 | log_debug("The most powerful car is {rownames(mtcars)[which.max(mtcars$hp)]} with {max(mtcars$hp)} hp")
32 | }
33 | ```
34 |
35 | Interestingly, the most powerful car was not being logged -- because by default the `logger` prints messages with at least the `INFO` log level:
36 |
37 | ```{r}
38 | log_threshold()
39 | ```
40 |
41 | To change that, specify the new log level threshold, eg `TRACE` to log everything:
42 |
43 | ```{r}
44 | log_threshold(TRACE)
45 | ```
46 |
47 | The rerunning the above code chunk:
48 |
49 | ```{r}
50 | log_info("Loading data")
51 | data(mtcars)
52 | log_info("The dataset includes {nrow(mtcars)} rows")
53 | if (max(mtcars$hp) < 1000) {
54 | log_warn("Oh, no! There are no cars with more than 1K horsepower in the dataset :/")
55 | log_debug("The most powerful car is {rownames(mtcars)[which.max(mtcars$hp)]} with {max(mtcars$hp)} hp")
56 | }
57 | ```
58 |
59 | You may also find the `?log_eval` function useful to log both an R expression and its result in the same log record:
60 |
61 | ```{r}
62 | f <- sqrt
63 | g <- mean
64 | x <- 1:31
65 | log_eval(y <- f(g(x)), level = INFO)
66 | str(y)
67 | ```
68 |
69 |
70 | Sometimes, it may be reasonable to log R objects as markdown, e.g. a smallish `data.frame` or `data.table`, e.g. `mtcars` or `iris`. Calling the formatter using `pander` instead of `glue` can help:
71 |
72 | ```{r knitr-pander-setup, include = FALSE}
73 | ppo1 <- pander::panderOptions("knitr.auto.asis")
74 | ppo2 <- pander::panderOptions("table.style")
75 | pander::panderOptions("knitr.auto.asis", FALSE)
76 | pander::panderOptions("table.style", "simple")
77 | ```
78 |
79 | ```{r}
80 | log_formatter(formatter_pander)
81 | log_info(head(iris))
82 | ```
83 |
84 | ```{r knitr-pander-revert, include = FALSE}
85 | pander::panderOptions("knitr.auto.asis", ppo1)
86 | pander::panderOptions("table.style", ppo2)
87 | ```
88 |
89 | For more details, check the [function reference in the manual](https://daroczig.github.io/logger/reference/index.html), or start with the [The Anatomy of a Log Request](https://daroczig.github.io/logger/articles/anatomy.html) and [Customizing the Format and the Destination of a Log Record](https://daroczig.github.io/logger/articles/customize_logger.html) vignettes.
90 |
91 | ```{r cleanup, include = FALSE}
92 | logger:::namespaces_reset()
93 | ```
94 |
--------------------------------------------------------------------------------
/vignettes/anatomy.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "The Anatomy of a Log Request"
3 | output: rmarkdown::html_vignette
4 | vignette: >
5 | %\VignetteIndexEntry{The Anatomy of a Log Request}
6 | %\VignetteEngine{knitr::rmarkdown}
7 | %\VignetteEncoding{UTF-8}
8 | resource_files:
9 | - logger_structure.svg
10 | ---
11 |
12 | ```{r setup, include = FALSE}
13 | knitr::opts_chunk$set(
14 | collapse = TRUE,
15 | comment = "#>"
16 | )
17 | library(logger)
18 | log_appender(appender_stdout)
19 | ```
20 |
21 | ```{r loggerStructureImage, echo=FALSE, out.extra='style="width: 100%;" title="The structure of a logger and the flow of a log record request" alt="The structure of a logger and the flow of a log record request"'}
22 | knitr::include_graphics("logger_structure.svg")
23 | ```
24 |
25 | To make a successful log record, `logger` requires the below components:
26 |
27 | - a **log request**, eg
28 |
29 | ```r
30 | log_error('Oops')
31 | ```
32 |
33 | - including the log level (importance) of the record, which will be later used to decide if the log record is to be delivered or not: `ERROR` in this case
34 | - R objects to be logged: a simple string in this case, although it could be a character vector or any R object(s) that can be converted into a character vector by the `formatter` function
35 |
36 | - the **environment** and meta-information of the log request, eg actual timestamp, hostname of the computer, the name of the user running the R script, the pid of the R process, calling function and the actual call etc.
37 |
38 |
39 | ```{r}
40 | f <- function() get_logger_meta_variables(log_level = INFO)
41 | f()
42 | ```
43 |
44 | - a **logger definition** to process the log request, including
45 |
46 | - log level `threshold`, eg `INFO`, which defines the minimum log level required for actual logging -- all log requests with lower log level will be thrown away
47 |
48 | ```{r}
49 | log_threshold()
50 | ERROR <= INFO
51 | log_error("Oops")
52 | ```
53 |
54 | - `formatter` function, which takes R objects and converts those into actual log message(s) to be then passed to the `layout` function for the log record rendering -- such as `paste`, `sprintf`, `glue` or eg the below custom example:
55 |
56 |
57 | ```{r}
58 | formatter <- function(...) paste(..., collapse = " ", sep = " ")
59 | formatter(1:3, c("foo", "bar"))
60 | ```
61 |
62 | - `layout` function, which takes log message(s) and further information on the log request (such as timestamp, hostname, username, calling function etc) to render the actual log records eg human-readable text, JSON etc
63 |
64 |
65 | ```r
66 | library(jsonlite)
67 | layout <- function(level, msg) toJSON(level = level, timestamp = time, hostname = node, message = msg)
68 | layout(INFO, 'Happy Thursday!')
69 | #> {'level': 'INFO', 'timestamp': '1970-01-01 00:00:00', 'hostname': 'foobar', 'message': 'Happy Thursday!'}
70 | ```
71 |
72 | - `appender` function, which takes fully-rendered log record(s) and delivers to somewhere, eg `stdout`, a file or a streaming service, eg
73 |
74 | ```{r}
75 | appender <- function(line) cat(line, "\n")
76 | appender("INFO [now] I am a log message")
77 | ```
78 |
79 | Putting all these together (by explicitly setting the default config in the `global` namespace):
80 |
81 | ```{r}
82 | log_threshold(INFO)
83 | log_formatter(formatter_glue)
84 | log_layout(layout_simple)
85 | log_appender(appender_stdout)
86 | log_debug("I am a low level log message that will not be printed with a high log level threshold")
87 | log_warn("I am a higher level log message that is very likely to be printed")
88 | ```
89 |
90 | Note, that all `logger` definitions and requests are tied to a logging namespace, and one log request might trigger multiple `logger` definitions as well (stacking). Find more information on these in the [Customizing the format and destination of log records](https://daroczig.github.io/logger/articles/customize_logger.html) vignette.
91 |
92 | ```{r cleanup, include = FALSE}
93 | logger:::namespaces_reset()
94 | ```
95 |
--------------------------------------------------------------------------------
/vignettes/performance.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Simple Benchmarks on logger Performance"
3 | output: rmarkdown::html_vignette
4 | vignette: >
5 | %\VignetteIndexEntry{Simple Benchmarks on logger Performance}
6 | %\VignetteEngine{knitr::rmarkdown}
7 | %\VignetteEncoding{UTF-8}
8 | ---
9 |
10 | Although this has not been an important feature in the early development and overall design of this `logger` implementation, but with the default `?layout_simple` and `?formatter_glue`, it seems to perform pretty well when comparing with `futile.logger` and `logging` packages:
11 |
12 | ```r
13 | library(microbenchmark)
14 |
15 | ## fl
16 | library(futile.logger)
17 | t1 <- tempfile()
18 | flog.appender(appender.file(t1))
19 | #> NULL
20 |
21 | ## lg
22 | library(logging)
23 | t2 <- tempfile()
24 | addHandler(writeToFile, file = t2)
25 |
26 | ## lr
27 | library(logger)
28 | #> The following objects are masked from ‘package:futile.logger’: DEBUG, ERROR, FATAL, INFO, TRACE, WARN
29 | t3 <- tempfile()
30 | log_appender(appender_file(t3))
31 |
32 | string_fl <- function() flog.info('hi')
33 | string_lg <- function() loginfo('hi')
34 | string_lr <- function() log_info('hi')
35 | dynamic_fl <- function() flog.info('hi %s', 42)
36 | dynamic_lg <- function() loginfo('hi %s', 42)
37 | dynamic_lr <- function() log_info('hi {42}')
38 | vector_fl <- function() flog.info(paste('hi', 1:5))
39 | vector_lg <- function() loginfo(paste('hi', 1:5))
40 | vector_lr <- function() log_info('hi {1:5}')
41 |
42 | microbenchmark(
43 | string_fl(), string_lg(), string_lr(),
44 | vector_fl(), vector_lg(), vector_lr(),
45 | dynamic_fl(), dynamic_lg(), dynamic_lr(),
46 | times = 1e3)
47 | #> Unit: microseconds
48 | #> expr min lq mean median uq max neval
49 | #> string_fl() 1533.379 1650.7915 2510.5517 1759.9345 2885.4465 20835.425 1000
50 | #> string_lg() 172.963 206.7615 315.6177 237.3150 335.3010 12738.735 1000
51 | #> string_lr() 227.981 263.4715 390.7139 301.9045 409.0400 11926.974 1000
52 | #> vector_fl() 1552.706 1661.7030 2434.0460 1766.7485 2819.5525 40892.197 1000
53 | #> vector_lg() 198.338 234.2355 330.3268 266.7695 358.2510 9969.333 1000
54 | #> vector_lr() 290.169 337.4730 592.0041 382.4335 537.5485 101946.435 1000
55 | #> dynamic_fl() 1538.985 1663.7890 2564.6668 1782.1160 2932.7555 46039.686 1000
56 | #> dynamic_lg() 188.213 226.5370 387.2470 255.1745 350.2015 60737.562 1000
57 | #> dynamic_lr() 271.478 317.3350 486.1123 360.5815 483.5830 12070.936 1000
58 |
59 | paste(t1, length(readLines(t1)))
60 | #> [1] "/tmp/Rtmp3Fp6qa/file7a8919485a36 7000"
61 | paste(t2, length(readLines(t2)))
62 | #> [1] "/tmp/Rtmp3Fp6qa/file7a89b17929f 7000"
63 | paste(t3, length(readLines(t3)))
64 | #> [1] "/tmp/Rtmp3Fp6qa/file289f24c88c41 7000"
65 | ```
66 |
67 | So based on the above, non-comprehensive benchmark, it seems that when it comes to using the very base functionality of a logging engine, `logging` comes first, then `logger` performs with a bit of overhead due to using `glue` by default, then comes a bit slower `futile.logger`.
68 |
69 | On the other hand, there are some low-hanging fruits to improve performance, eg caching the `logger` function in the namespace, or using much faster message formatters (eg `paste0` or `sprintf` instead of `glue`) if needed -- like what `futile.logger` and `logging` are using instead of `glue`, so a quick `logger` comparison:
70 |
71 | ```r
72 | log_formatter(formatter_sprintf)
73 | string <- function() log_info('hi')
74 | dynamic <- function() log_info('hi %s', 42)
75 | vector <- function() log_info(paste('hi', 1:5))
76 |
77 | microbenchmark(string(), vector(), dynamic(), times = 1e3)
78 | #> Unit: microseconds
79 | #> expr min lq mean median uq max neval cld
80 | #> string() 110.192 118.4850 148.5848 137.1825 152.7275 1312.903 1000 a
81 | #> vector() 129.111 136.8245 168.9274 155.5840 172.6795 3230.528 1000 b
82 | #> dynamic() 116.347 124.7620 159.1570 143.2140 160.5040 4397.640 1000 ab
83 | ```
84 |
85 | Which suggests that `logger` is a pretty well-performing log framework.
86 |
87 | If you need even more performance with slower appenders, then asynchronous logging is your friend: passing the log messages to a reliable message queue, and a background process delivering those to the actual log destination in the background -- without blocking the main R process. This can be easily achieved in `logger` by wrapping any appender function in the `appender_async` function, such as:
88 |
89 | ```r
90 | ## demo log appender that's pretty slow
91 | appender_file_slow <- function(file) {
92 | force(file)
93 | function(lines) {
94 | Sys.sleep(1)
95 | cat(lines, sep = '\n', file = file, append = TRUE)
96 | }
97 | }
98 |
99 | ## create an async appender and start using it right away
100 | log_appender(appender_async(appender_file_slow(file = tempfile())))
101 |
102 | async <- function() log_info('Was this slow?')
103 | microbenchmark(async(), times = 1e3)
104 | # Unit: microseconds
105 | # expr min lq mean median uq max neval
106 | # async() 298.275 315.5565 329.6235 322.219 333.371 894.579 1000
107 | ```
108 |
109 | Please note that although this ~0.3 ms is higher than the ~0.15 ms we achieved above with the `sprintf` formatter, but this time we are calling an appender that would take 1 full second to deliver the log message (and not just printing to the console), so bringing that down to less than 1 millisecond is not too bad.
110 |
111 | ```{r cleanup, include = FALSE}
112 | logger:::namespaces_reset()
113 | ```
114 |
--------------------------------------------------------------------------------
/vignettes/r_packages.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Logging from R Packages"
3 | output: rmarkdown::html_vignette
4 | vignette: >
5 | %\VignetteIndexEntry{Logging from R Packages}
6 | %\VignetteEngine{knitr::rmarkdown}
7 | %\VignetteEncoding{UTF-8}
8 | ---
9 |
10 | ```{r pkgchecks, echo = FALSE}
11 | ## check if other logger packages are available and exit if not
12 | for (pkg in c("devtools")) {
13 | if (!requireNamespace(pkg, quietly = TRUE)) {
14 | warning(paste(pkg, "package not available, so cannot build this vignette"))
15 | knitr::knit_exit()
16 | }
17 | }
18 | ```
19 |
20 | ```{r setup, include = FALSE}
21 | knitr::opts_chunk$set(
22 | collapse = TRUE,
23 | comment = "#>"
24 | )
25 | ```
26 |
27 | 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, especially with the [Log namespaces](https://daroczig.github.io/logger/articles/customize_logger.html#log-namespaces) section.
28 |
29 | So that your R package's users can suppress (or render with custom layout) the log messages triggered by your R package, it's wise to record all those log messages in a custom namespace. By default, if you are calling the `?log_level` function family from an R package after importing from the `logger` package, then `logger` will try to auto-guess the calling R package name and use that as the default namespace, see eg:
30 |
31 | ```{r}
32 | library(logger)
33 | devtools::load_all(system.file("demo-packages/logger-tester-package", package = "logger"))
34 | logger_tester_function(INFO, "hi from tester package")
35 | ```
36 |
37 | But if auto-guessing is not your style, then feel free to set your custom namespace (eg the name of your package) in all `?log_info` etc function calls and let your users know about how to suppress / reformat / redirect your log messages via `?log_threshold`, `?log_layout`, `?log_appender`.
38 |
39 | Please note that setting the formatter function via `?log_formatter` should not be left to the R package end-users, as the log message formatter is specific to your logging calls, so that should be decided by the R package author. Feel free to pick any formatter function (eg `glue`, `sprintf`, `paste` or something else), and set that via `?log_formatter` when your R package is loaded. All other parameters of your `logger` will inherit from the `global` namespace -- set by your R package's end user.
40 |
41 | ```{r cleanup, include = FALSE}
42 | logger:::namespaces_reset()
43 | ```
44 |
--------------------------------------------------------------------------------
/vignettes/write_custom_extensions.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Writing Custom Logger Extensions"
3 | vignette: >
4 | %\VignetteIndexEntry{Writing Custom Logger Extensions}
5 | %\VignetteEngine{knitr::rmarkdown}
6 | %\VignetteEncoding{UTF-8}
7 | ---
8 |
9 | ```{r setup, include = FALSE}
10 | knitr::opts_chunk$set(
11 | collapse = TRUE,
12 | comment = "#>"
13 | )
14 | ```
15 |
16 | 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.
17 |
18 | ## Custom log message formatter functions
19 |
20 | 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.
21 |
22 | 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.
23 |
24 | 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.
25 |
26 | 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.
27 |
28 | ## Custom log layout rendering functions
29 |
30 | The layout functions return the log record and take at least two arguments:
31 |
32 | - the log level and
33 | - a message already formatted as a string by a log message formatter function
34 | - 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`
35 |
36 | Such layout rendering function can be as simple as the default `?layout_simple`:
37 |
38 | ```r
39 | layout_simple <- function(level, msg, ...) {
40 | paste0(attr(level, 'level'), ' [', format(Sys.time(), "%Y-%m-%d %H:%M:%S"), '] ', msg)
41 | }
42 | ```
43 |
44 | 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.
45 |
46 | 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.
47 |
48 | 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.
49 |
50 | ## Custom log record appenders
51 |
52 | 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`).
53 |
54 | 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.
55 |
56 | An example for a custom appender delivering log messages to a database table:
57 |
58 | ```r
59 | ## the dbr package provides and easy and secure way of connecting to databased from R
60 | ## although if you want to minimize the dependencies, feel free to stick with DBI etc.
61 | library(dbr)
62 | ## init a persistent connection to the database using a yaml config in the background thanks to dbr
63 | ## NOTE that this is optional and a temporarily connection could be also used
64 | ## for higher reliability but lower throughput
65 | con <- db_connect('mydb')
66 | ## define custom function writing the log message to a table
67 | log_appender(function(lines) {
68 | db_append(
69 | df = data.frame(timestamp = Sys.time(), message = lines),
70 | table = 'logs', db = con)
71 | })
72 | ```
73 |
74 | ```{r cleanup, include = FALSE}
75 | logger:::namespaces_reset()
76 | ```
77 |
--------------------------------------------------------------------------------