The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.
The stenographer
package provides a flexible logging
framework with hierarchical logging levels, database integration, and
contextual logging capabilities. It includes support for SQLite storage,
colour-coded output, and parallel processing support.
The latest version includes support for SQLite
database
logging and context management.
You can install the released version of stenographer from CRAN:
install.packages("stenographer")
You can install stenographer from www.github.com/dereckmezquita/stenographer with:
# install.packages("remotes")
::install_github("dereckmezquita/stenographer") remotes
Here’s a quick example of how to use stenographer:
::use(stenographer[Stenographer, LogLevel])
box
# Create a basic logger
<- Stenographer$new()
steno
# Log some messages
$info("This is an informational message")
steno#> 2025-01-13T13:55:15.524Z INFO This is an informational message
$warn("This is a warning")
steno#> 2025-01-13T13:55:15.526Z WARNING This is a warning
$error("This is an error")
steno#> 2025-01-13T13:55:15.549Z ERROR This is an error
You can customise the stenographer by specifying the minimum log level, output file, and custom print function:
<- tempfile("app_log")
log_file
<- Stenographer$new(
custom_steno level = LogLevel$WARNING,
file_path = log_file,
print_fn = message
)
$info("This won't be logged")
custom_steno$warn("This will be logged to console and file")
custom_steno#> 2025-01-13T13:55:15.650Z WARNING This will be logged to console and file
$error("This is an error message", error = "Some error")
custom_steno#> 2025-01-13T13:55:15.651Z ERROR This is an error message
#> Error:
#> "Some error"
Logs are written to the specified file as JSON objects:
cat(readLines(log_file), sep = "\n")
#> {"datetime":"2025-01-13T13:55:15.650Z","level":"WARNING","msg":"This will be logged to console and file","data":{},"error":{},"context":{}}
#> {"datetime":"2025-01-13T13:55:15.651Z","level":"ERROR","msg":"This is an error message","data":{},"error":"[\"Some error\"]","context":{}}
stenographer now supports logging to a SQLite database and context
management so you can easily track application events. The context is
useful for filtering and querying logs based on specific criteria from
SQLite
:
::use(RSQLite[ SQLite ])
box::use(DBI[ dbConnect, dbDisconnect, dbGetQuery ])
box
# Create a database connection
<- dbConnect(SQLite(), "log.sqlite")
db
# Create a stenographer that logs to the database
<- Stenographer$new(
db_steno context = list(app_name = "MyApp", fun = "main"),
db_conn = db,
table_name = "app_logs"
)
# Log some messages
$info("This is logged to the database")
db_steno#> 2025-01-13T13:55:15.772Z INFO This is logged to the database
#> Context:
#> {
#> "app_name": "MyApp",
#> "fun": "main"
#> }
$warn("This is a warning", data = list(code = 101))
db_steno#> 2025-01-13T13:55:15.778Z WARNING This is a warning
#> Data:
#> {
#> "code": 101
#> }
#> Context:
#> {
#> "app_name": "MyApp",
#> "fun": "main"
#> }
$error("An error occurred", error = "Division by zero")
db_steno#> 2025-01-13T13:55:15.844Z ERROR An error occurred
#> Error:
#> "Division by zero"
#> Context:
#> {
#> "app_name": "MyApp",
#> "fun": "main"
#> }
# Example of querying the logs
<- "SELECT * FROM app_logs WHERE level = 'ERROR'"
query <- dbGetQuery(db, query)
result print(result)
#> id datetime level context
#> 1 3 2025-01-12T20:03:40.117Z ERROR {"app_name":["MyApp"],"fun":["main"]}
#> 2 6 2025-01-13T13:54:50.711Z ERROR {"app_name":["MyApp"],"fun":["main"]}
#> 3 9 2025-01-13T13:55:15.844Z ERROR {"app_name":["MyApp"],"fun":["main"]}
#> msg data error
#> 1 An error occurred <NA> ["[\\"Division by zero\\"]"]
#> 2 An error occurred <NA> ["[\\"Division by zero\\"]"]
#> 3 An error occurred <NA> ["[\\"Division by zero\\"]"]
# Don't forget to close the database connection when you're done
dbDisconnect(db)
Stenographer includes helper functions like
valueCoordinates
and tableToString
to provide
detailed context in log messages:
::use(stenographer[valueCoordinates, tableToString])
box
# Create a sample dataset with some issues
<- data.frame(
df a = c(1, NA, 3, 4, 5),
b = c(2, 4, NA, 8, 10),
c = c(3, 6, 9, NA, 15)
)
# Find coordinates of NA values
<- valueCoordinates(df)
na_coords
if (nrow(na_coords) > 0) {
$warn(
steno"NA values found in the dataset",
data = list(
na_locations = na_coords,
dataset_preview = tableToString(df)
)
)
}#> 2025-01-13T13:55:15.858Z WARNING NA values found in the dataset
#> Data:
#> {
#> "na_locations": [
#> {
#> "column": 1,
#> "row": 2
#> },
#> {
#> "column": 2,
#> "row": 3
#> },
#> {
#> "column": 3,
#> "row": 4
#> }
#> ],
#> "dataset_preview": " a b c\n1 1 2 3\n2 NA 4 6\n3 3 NA 9\n4 4 8 NA\n5 5 10 15"
#> }
stenographer makes it easy to log errors with context:
<- function(df) {
process_data tryCatch({
<- df$a / df$b
result if (any(is.infinite(result))) {
<- valueCoordinates(data.frame(result), Inf)
inf_coords $error(
steno"Division by zero occurred",
data = list(
infinite_values = inf_coords,
dataset_preview = tableToString(df)
)
)stop("Division by zero error")
}return(result)
error = function(e) {
}, $error(
stenopaste("An error occurred while processing data:", e$message),
data = list(dataset_preview = tableToString(df)),
error = e
)stop(e)
})
}
# Test the function with problematic data
<- data.frame(a = c(1, 2, 3), b = c(0, 2, 0))
df process_data(df)
#> 2025-01-13T13:55:15.883Z ERROR Division by zero occurred
#> Data:
#> {
#> "infinite_values": [
#> {
#> "column": 1,
#> "row": 1
#> },
#> {
#> "column": 1,
#> "row": 3
#> }
#> ],
#> "dataset_preview": " a b\n1 1 0\n2 2 2\n3 3 0"
#> }
#> 2025-01-13T13:55:15.884Z ERROR An error occurred while processing data: Division by zero error
#> Data:
#> {
#> "dataset_preview": " a b\n1 1 0\n2 2 2\n3 3 0"
#> }
#> Error:
#> {
#> "name": "simpleError",
#> "message": "Division by zero error",
#> "call": "doTryCatch(return(expr), name, parentenv, handler)"
#> }
#> Error in doTryCatch(return(expr), name, parentenv, handler): Division by zero error
stenographer provides support for logging in parallel environments:
::use(future)
box::use(future.apply[future_lapply])
box::use(stenographer[messageParallel])
box
<- Stenographer$new(print_fn = messageParallel)
steno
::plan(future$multisession, workers = 2)
future
<- future_lapply(1:5, function(i) {
result messageParallel(sprintf("Processing item %d", i))
if (i == 3) {
$warn(sprintf("Warning for item %d", i))
steno
}return(i * 2)
})
::plan(future::sequential) future
#> Processing item 1
#> Processing item 2
#> Processing item 3
#> 2024-08-03T11:18:03.091Z WARNING Warning for item 3
#> Processing item 4
#> Processing item 5
Contributions to stenographer are welcome! Please refer to the CONTRIBUTING.md file for guidelines.
This package is licensed under the MIT License.
If you use this package in your research or work, please cite it as:
Mezquita, D. (2025). stenographer: Flexible and Customisable Logging System. R package version 1.0.0. https://github.com/dereckmezquita/stenographer
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.