77# ' [jsonlines](https://jsonlines.org/) log files. This provides a
88# ' nice balance between human- an machine-readability.
99# '
10- # '
11- # ' @section Event transformation:
12- # ' This Layout provides 4 ways to transform the event before serialization:
13- # '
14- # ' 1. `transform_event`: a generic function to transform the event
15- # ' 2. `timestamp_fmt`: a format string or function to apply to the timestamp field
16- # ' 3. `transform_event_names`: a named `character` vector or a second
17- # ' function to rename fields
18- # ' 4. `excluded_fields`: a `character` vector to include fields.
19- # '
20- # ' In theory supplying a custom `transform_event` function is enough to
21- # ' perform all these actions, but the other three parameters are provided for
22- # ' convenience. Please note that they are applied in order (e.g. if you
23- # ' rename a field you have to exclude the *renamed* field to really exclude it).
24- # '
2510# ' @family Layouts
2611# ' @seealso [read_json_lines()], [https://jsonlines.org/](https://jsonlines.org/)
2712# ' @export
3621# ' custom_field = "LayoutJson can handle arbitrary fields"
3722# ' )
3823# '
39- # ' # Default settings show all event fals
4024# ' lo <- LayoutJson$new()
4125# ' lo$format_event(event)
26+ # '
27+ # ' lo <- LayoutJson$new(
28+ # ' transform_event_names = toupper,
29+ # ' excluded_fields = c("RAWMSG", "CALLER"))
30+ # '
31+ # ' lo$format_event(event)
32+ # '
33+ # ' lo <- LayoutJson$new(
34+ # ' transform_event = function(e) {
35+ # ' values <- e$values
36+ # ' values$msg <- toupper(values$msg)
37+ # ' values
38+ # ' },
39+ # ' timestamp_fmt = "%a %b %d %H:%M:%S %Y",
40+ # ' excluded_fields = c("RAWMSG", "CALLER"))
41+ # '
42+ # ' lo$format_event(event)
4243LayoutJson <- R6 :: R6Class(
4344 " LayoutJson" ,
4445 inherit = Layout ,
@@ -49,30 +50,31 @@ LayoutJson <- R6::R6Class(
4950 # '
5051 # ' @param toJSON_args a list of arguments passed to [jsonlite::toJSON()],
5152 # '
52- # ' @param timestamp_fmt Format to be applied to the timestamp. This is
53- # ' applied after `transform_event()` but `before transform_event_names()`
54- # ' * `NULL` (the default): formatting of the timestamp is left to
55- # ' [jsonlite::toJSON()],
56- # ' * a `character` scalar as for [format.POSIXct()], or
57- # ' * a `function` that returns a vector of the same length as its
58- # ' ([POSIXct]) input. The returned vector can be of any type
59- # ' supported by [jsonlite::toJSON()], but should usually be `character`.
53+ # ' @param transform_event a `function` with a single argument that
54+ # ' takes a [LogEvent] object and returns a `list` of values.
6055 # '
61- # ' @param transform_event a `function` with a single argument `event` that
62- # ' takes a [LogEvent] object and returns a list of values.
56+ # ' @param timestamp_fmt Format to be applied to the timestamp. This is
57+ # ' applied after `transform_event` but `before transform_event_names`
58+ # ' * `NULL`: formatting of the timestamp is left to [jsonlite::toJSON()],
59+ # ' * a `character` scalar as for [format.POSIXct()], or
60+ # ' * a `function` that returns a vector of the same length as its
61+ # ' ([POSIXct]) input. The returned vector can be of any type
62+ # ' supported by [jsonlite::toJSON()].
6363 # '
64- # ' @param transform_event_names A named `character` vector mapping original
65- # ' field names to Dynatrace-compatible ones, or a function with a single
66- # ' mandatory argument that accepts a character vector of field names.
67- # ' Applied after to `transform_event()`.
64+ # ' @param transform_event_names
65+ # ' * `NULL`: don't process names
66+ # ' * a named `character` vector where the names are the original field names
67+ # ' and the values the desired new field names,
68+ # ' * or a `function` with a single mandatory argument that accepts a
69+ # ' `character` vector of field names. Applied after `transform_event`.
6870 # '
6971 # ' @param excluded_fields A `character` vector of field names to exclude
7072 # ' from the final output. Applied after `transform_event_names`.
7173 initialize = function (
7274 toJSON_args = list (auto_unbox = TRUE ),
7375 timestamp_fmt = NULL ,
7476 transform_event = function (event ) event [[" values" ]],
75- transform_event_names = identity ,
77+ transform_event_names = NULL ,
7678 excluded_fields = " rawMsg"
7779 ){
7880 self $ set_toJSON_args(toJSON_args )
@@ -83,115 +85,125 @@ LayoutJson <- R6::R6Class(
8385 },
8486
8587 format_event = function (event ) {
86-
8788 values <- get(" .transform_event" , private )(event )
88-
89- if (! is.null(self [[" timestamp_fmt" ]])){
90- values [[" timestamp" ]] <- fmt_timestamp(values [[" timestamp" ]], self [[" timestamp_fmt" ]])
91- }
92-
93- if (is.character(self $ transform_event_names )){
94- original_names <- names(values )
95- rename_idx <- match(original_names , names(self $ transform_event_names ), nomatch = 0L )
96- names(values )[rename_idx > 0L ] <- self $ transform_event_names [rename_idx [rename_idx > 0L ]]
97-
98- } else if (is.function(self $ transform_event_names )){
99- names(values ) <- self $ transform_event_names(names(values ))
100-
101- } else {
102- warning(" `transform_event_names` must be a character vector or a function" )
103- }
104-
105- if (! is.null(self $ excluded_fields )) {
106- values <- values [! names(values ) %in% self $ excluded_fields ]
107- }
89+ values [[" timestamp" ]] <- apply_timestamp_formatter(values [[" timestamp" ]], get(" .timestamp_fmt" , private ))
90+ names(values ) <- apply_event_name_transformer(names(values ), get(" .transform_event_names" , private ))
91+ values <- apply_field_exclusion(values , self $ excluded_fields )
10892
10993 do.call(
11094 jsonlite :: toJSON ,
11195 args = c(list (x = values ), get(" .toJSON_args" , private ))
11296 )
11397 },
11498
99+ # . . setters -------------------------------------------------------------
100+
101+ # ' @param x a `list`
115102 set_toJSON_args = function (x ){
116103 assert(is.list(x ))
117104 assert(identical(length(names(x )), length(x )))
118105 private $ .toJSON_args <- x
119106 invisible (self )
120107 },
121108
122-
123- # . . setters -------------------------------------------------------------
124-
109+ # ' @param x a `character` scalar or a `function` that accepts a `POSIXct`
110+ # ' as its single argument
125111 set_timestamp_fmt = function (x ){
126112 assert(is.null(x ) || is_scalar_character(x ) || is.function(x ))
127113 private [[" .timestamp_fmt" ]] <- x
128114 invisible (self )
129115 },
130116
117+ # ' @param x a `function` that accepts a `LogEvent` as its single argument
131118 set_transform_event = function (x ){
132119 assert(
133- is.function(x ) &&
134- identical(names(formals(x )), " event" ),
135- " `transform_event` must be a function a single argument `event`" )
120+ is.function(x ) && length(formals(x )) > = 1L ,
121+ " `transform_event` must be a function a single argument (optional arguments are OK)" )
136122
137123 private [[" .transform_event" ]] <- x
138124 invisible (self )
139125 },
140126
127+ # ' @param x a named `character` vector or a function that accepts a
128+ # ' `character` vector of field names as its single argument.
141129 set_transform_event_names = function (x ){
142- assert(is.function(x ) || is_field_name_map(x ),
143- " `transform_event_names` must be a named character vector or function with a single mandatory argument (optional arguments are OK)" )
130+ assert(
131+ is.null(x ) || is_field_name_map(x ) || (is.function(x ) && length(formals(x )) > = 1L ),
132+ " `transform_event_names` must be a named character vector or function with a single mandatory argument (optional arguments are OK)" )
144133
145134 private [[" .transform_event_names" ]] <- x
146135 },
147136
148137 # . . methods ----------------------------------------------------------------
149138
139+ # ' @description Represent the `LayoutJson` class as a string
150140 toString = function () {
151141 fmt_class(class(self )[[1 ]])
152142 },
153143
154- parse = function (
155- file
156- ){
157- read_json_lines(file )
158- },
144+ # ' @description Read and parse file written using this Layout
145+ # '
146+ # ' This can be used by the `$data` active binding of an [Appender]
147+ # '
148+ # ' @param file `character` scalar: path to a file
149+ parse = function (file ){
150+ read_json_lines(file )
151+ },
159152
153+ # ' @description Read a file written using this Layout (without parsing)
154+ # '
155+ # ' This can be used by the `$show()` method of an [Appender]
156+ # '
157+ # ' @param file `character` scalar: path to a file
158+ # ' @param threshold `character` Minimum log level to show. Requires parsing
159+ # ' of the log file (but will still display unparsed output)
160+ # ' @param n `integer` number of lines to show
160161 read = function (
161162 file ,
162163 threshold = NA_integer_ ,
163164 n = 20L
164- ){
165- assert(is_scalar_integerish(n ))
166- threshold <- standardize_threshold(threshold )
167-
168- dd <- readLines(file )
169- if (! is.na(threshold )){
170- sel <- self $ parse(file )$ level < = threshold
171- } else {
172- sel <- TRUE
173- }
174-
175- dd <- tail(dd [sel ], n )
176- dd
165+ ){
166+ assert(is_scalar_integerish(n ))
167+ threshold <- standardize_threshold(threshold )
168+
169+ dd <- readLines(file )
170+ if (! is.na(threshold )){
171+ sel <- self $ parse(file )$ level < = threshold
172+ } else {
173+ sel <- TRUE
177174 }
175+
176+ dd <- tail(dd [sel ], n )
177+ dd
178+ }
178179 ),
179180
180181
181182 # . . active fields ------------------------------------------------------
182183
183184 active = list (
185+
186+ # ' @field toJSON_args a `list`
184187 toJSON_args = function () {
185188 get(" .toJSON_args" , private )
186189 },
187190
191+ # ' @field timestamp_fmt a `character` scalar or a `function` that accepts a `POSIXct`
192+ # ' as its single argument
188193 timestamp_fmt = function () {
189194 get(" .timestamp_fmt" , private )
190195 },
191196
192- transform_event = function () get(" .transform_event" , private ),
197+ # ' @field transform_event a `function` that accepts a `LogEvent` as its single argument
198+ transform_event = function (){
199+ get(" .transform_event" , private )
200+ },
193201
194- transform_event_names = function () get(" .transform_event_names" , private )
202+ # ' @field transform_event_names a named `character` vector or a function that accepts a
203+ # ' `character` vector of field names as its single argument.
204+ transform_event_names = function () {
205+ get(" .transform_event_names" , private )
206+ }
195207 ),
196208
197209 # . . private --------------------------------------------------------------
@@ -204,6 +216,54 @@ LayoutJson <- R6::R6Class(
204216)
205217
206218
219+ # utils -------------------------------------------------------------------
220+
221+ apply_timestamp_formatter = function (x , f ){
222+ if (is.null(f )){
223+ return (x )
224+ }
225+
226+ if (is.character(f )){
227+ return (format(x , f ))
228+ }
229+
230+ if (is.function(f )){
231+ return (f(x ))
232+ }
233+
234+ warning(" `f` must be a character scalar or a function" )
235+ }
236+
237+
238+ apply_event_name_transformer = function (x , f ){
239+ if (is.null(f )){
240+ return (x )
241+ }
242+
243+ if (is.character(f )){
244+ rename_idx <- match(x , names(f ), nomatch = 0L )
245+ x [rename_idx > 0L ] <- f [rename_idx [rename_idx > 0L ]]
246+ return (x )
247+ }
248+
249+ if (is.function(f )){
250+ return (f(x ))
251+ }
252+
253+ warning(" `f` must be a named character vector or a function" )
254+ x
255+ }
256+
257+
258+ apply_field_exclusion <- function (x , f ){
259+ if (is.null(f )){
260+ return (x )
261+ }
262+
263+ x [! names(x ) %in% f ]
264+ }
265+
266+
207267is_field_name_map <- function (x ){
208268 is.character(x ) && ! is.null(names(x )) && all(nzchar(names(x )))
209269}
0 commit comments