tty-logger 0.0.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc0a525df2a56832f927f224ed3d9d25b0e200e89745b743af4f35b1013f10ed
4
- data.tar.gz: 27598db1b9976f9e12a76a67225cc52c489a5e963008a50309bd70649983ff1e
3
+ metadata.gz: '0594c7ef471a07571e5621e8535469fcb50edeeb10d7ffa58b194ef8fe4f8350'
4
+ data.tar.gz: 42a3b6c20b756754723c42c5b0ce85490b403978ffe566cb267dc6f18811f1c9
5
5
  SHA512:
6
- metadata.gz: fa29e2ff2aaccc1ea89e8ef907cbbf740e9001066f0c4926c8383df31610813e5b84adfd1d9e102cfd68a9ea53373c42a4f5d2ac50394f7f839c2787b68b51a8
7
- data.tar.gz: 7713b63753afad9b07eabdae55526a0130c5d36334bfd10c15f2284b412cce26f60fe1ee74531a3a74b2c18a4fca3396372112f8f3cbaaa459ebf3a1b0a0d26a
6
+ metadata.gz: 5fb46bcc0515d4f4762dc9c601b65131a493153ed85e48e83ac64ae70b0b2060d0780d867560bfc8e50ed4c461ea62e1aeba5b4e1b6b55572656196e9958840b
7
+ data.tar.gz: 235421d689aa362a858a813fd369cef67673b0c6b13ccf50f6733a603746c3dfc00bde16fdd73fb1dd4358e9457c70d61c033c60d409e3f721eb0b34cc5c40bd
@@ -1,7 +1,57 @@
1
1
  # Change log
2
2
 
3
- ## [v0.1.0] - 2019-07-x
3
+ ## [v0.5.0] - 2020-09-27
4
+
5
+ ### Added
6
+ * Add :message_format option to customize how messages are displayed in the console
7
+ by Josh Greenwood (@JoshTGreenwood)
8
+
9
+ ### Fixed
10
+ * Fix to select event name from valid log types or current level
11
+ by Ryan Schlesinger (@ryansch)
12
+ * Fix duplicate filters attribute definition in TTY::Logger::Config
13
+
14
+ ## [v0.4.0] - 2020-07-29
15
+
16
+ ### Added
17
+ * Allow editing logger configuration at runtime ([#10](https://github.com/piotrmurach/tty-logger/pull/10))
18
+ * Support for the `<<` streaming operator ([#9](https://github.com/piotrmurach/tty-logger/pull/9)))
19
+
20
+ ### Changed
21
+ * Change gemspec to update pastel version and restrict only to minor version
22
+
23
+ ### Fixed
24
+ * Fix to filter sensitive information from exceptions
25
+
26
+ ## [v0.3.0] - 2020-01-01
27
+
28
+ ### Added
29
+ * Add ability to filter sensitive information out of structured data
30
+
31
+ ### Changed
32
+ * Remove the test and task files from the gemspec
33
+
34
+ ### Fixed
35
+ * Fix console handler highlighting of nested hash keys
36
+
37
+ ## [v0.2.0] - 2019-09-30
38
+
39
+ ### Added
40
+ * Add ability to add structured data inside logging block
41
+ * Add ability to filter sensitive data
42
+ * Add ability to define custom log types
43
+ * Add ability to temporarily log at different level
44
+ * Add performance tests
45
+
46
+ ### Changed
47
+ * Change to dynamically define log types
48
+
49
+ ## [v0.1.0] - 2019-07-21
4
50
 
5
51
  * Initial implementation and release
6
52
 
53
+ [v0.5.0]: https://github.com/piotrmurach/tty-logger/compare/v0.4.0..v0.5.0
54
+ [v0.4.0]: https://github.com/piotrmurach/tty-logger/compare/v0.3.0..v0.4.0
55
+ [v0.3.0]: https://github.com/piotrmurach/tty-logger/compare/v0.2.0..v0.3.0
56
+ [v0.2.0]: https://github.com/piotrmurach/tty-logger/compare/v0.1.0..v0.2.0
7
57
  [v0.1.0]: https://github.com/piotrmurach/tty-logger/compare/v0.1.0
data/README.md CHANGED
@@ -1,17 +1,48 @@
1
1
  <div align="center">
2
- <a href="https://piotrmurach.github.io/tty" target="_blank"><img width="130" src="https://cdn.rawgit.com/piotrmurach/tty/master/images/tty.png" alt="tty logo" /></a>
2
+ <a href="https://ttytoolkit.org" target="_blank"><img width="130" src="https://github.com/piotrmurach/tty/raw/master/images/tty.png" alt="tty logo" /></a>
3
3
  </div>
4
4
 
5
- # TTY::Logger
5
+ # TTY::Logger [![Gitter](https://badges.gitter.im/Join%20Chat.svg)][gitter]
6
+
7
+ [![Gem Version](https://badge.fury.io/rb/tty-logger.svg)][gem]
8
+ [![Build Status](https://secure.travis-ci.org/piotrmurach/tty-logger.svg?branch=master)][travis]
9
+ [![Build status](https://ci.appveyor.com/api/projects/status/vtrkdk0naknnxoog?svg=true)][appveyor]
10
+ [![Code Climate](https://codeclimate.com/github/piotrmurach/tty-logger/badges/gpa.svg)][codeclimate]
11
+ [![Coverage Status](https://coveralls.io/repos/github/piotrmurach/tty-logger/badge.svg)][coverage]
12
+ [![Inline docs](http://inch-ci.org/github/piotrmurach/tty-logger.svg?branch=master)][inchpages]
13
+
14
+ [gitter]: https://gitter.im/piotrmurach/tty
15
+ [gem]: http://badge.fury.io/rb/tty-logger
16
+ [travis]: http://travis-ci.org/piotrmurach/tty-logger
17
+ [appveyor]: https://ci.appveyor.com/project/piotrmurach/tty-logger
18
+ [codeclimate]: https://codeclimate.com/github/piotrmurach/tty-logger
19
+ [coverage]: https://coveralls.io/github/piotrmurach/tty-logger
20
+ [inchpages]: http://inch-ci.org/github/piotrmurach/tty-logger
6
21
 
7
22
  > A readable, structured and beautiful logging for the terminal
8
23
 
24
+ **TTY::Logger** provides independent logging component for [TTY toolkit](https://github.com/piotrmurach/tty).
25
+
26
+ ![](assets/tty-logger-levels.png)
27
+
28
+ ## Features
29
+
30
+ * Intuitive console output for an increased readability
31
+ * Ability to stream data to any IO object
32
+ * Supports structured data logging
33
+ * Filters sensitive data
34
+ * Allows to define custom log types
35
+ * Formats and truncates messages to avoid clogging logging output
36
+ * Customizable styling of labels and symbols for console output
37
+ * Includes metadata information: time, location, scope
38
+ * Handles multiple logging outputs
39
+
9
40
  ## Installation
10
41
 
11
42
  Add this line to your application's Gemfile:
12
43
 
13
44
  ```ruby
14
- gem 'tty-logger'
45
+ gem "tty-logger"
15
46
  ```
16
47
 
17
48
  And then execute:
@@ -22,12 +53,746 @@ Or install it yourself as:
22
53
 
23
54
  $ gem install tty-logger
24
55
 
25
- ## Usage
56
+
57
+ ## Contents
58
+
59
+ * [1. Usage](#1-usage)
60
+ * [2. Synopsis](#2-synopsis)
61
+ * [2.1 Logging](#21-logging)
62
+ * [2.1.1 Exceptions](#211-exceptions)
63
+ * [2.1.2 Types](#212-types)
64
+ * [2.2 Levels](#22-levels)
65
+ * [2.2.1 Scoped Level](#22-scoped-level)
66
+ * [2.3 Structured Data](#23-structured-data)
67
+ * [2.4 Configuration](#24-configuration)
68
+ * [2.4.1 Metadata](#241-metadata)
69
+ * [2.4.2 Filters](#242-filters)
70
+ * [2.5 Cloning](#25-cloning)
71
+ * [2.6 Handlers](#26-handlers)
72
+ * [2.6.1 Console Handler](#261-console-handler)
73
+ * [2.6.2 Stream Handler](#262-stream-handler)
74
+ * [2.6.3 Custom Handler](#263-custom-handler)
75
+ * [2.6.4 Multiple Handlers](#264-multiple-handlers)
76
+ * [2.7 Formatters](#27-formatters)
77
+ * [2.8 Output streams](#28-output-streams)
78
+ * [3. Community Extensions](#3-community-extensions)
79
+ * [3.1 Sentry Handler](#31-sentry-handler)
80
+
81
+ ## 1. Usage
82
+
83
+ Create logger:
84
+
85
+ ```ruby
86
+ logger = TTY::Logger.new
87
+ ```
88
+
89
+ And log information using any of the logger [built-in types](#212-types):
90
+
91
+ ```ruby
92
+ logger.info "Deployed successfully"
93
+ logger.info "Deployed", "successfully"
94
+ logger.info { "Dynamically generated info" }
95
+ ```
96
+
97
+ Include structured data:
98
+
99
+ ```ruby
100
+ logger.success "Deployed successfully", myapp: "myapp", env: "prod"
101
+ # =>
102
+ # ✔ success Deployed successfully app=myapp env=prod
103
+ ```
104
+
105
+ Add [metadata](#241-metadata) information:
106
+
107
+ ```ruby
108
+ logger = TTY::Logger.new do |config|
109
+ config.metadata = [:date, :time]
110
+ end
111
+ logger.info "Deployed successfully", myapp: "myapp", env: "prod"
112
+ # =>
113
+ # [2019-07-17] [23:21:55.287] › ℹ info Info about the deploy app=myapp env=prod
114
+ ```
115
+
116
+ Or change structured data [formatting](#27-formatters) display to `JSON`:
117
+
118
+ ```ruby
119
+ logger = TTY::Logger.new do |config|
120
+ config.formatter = :json
121
+ end
122
+ logger.info "Deployed successfully"
123
+ # =>
124
+ # [2019-07-17] [23:21:55.287] › ℹ info Info about the deploy {"app":"myapp","env":"prod"}
125
+ ```
126
+
127
+ ## 2. Synopsis
128
+
129
+ ## 2.1 Logging
130
+
131
+ There are many logger types to choose from:
132
+
133
+ * `debug` - logs message at `:debug` level
134
+ * `info` - logs message at `:info` level
135
+ * `success` - logs message at `:info` level
136
+ * `wait` - logs message at `:info` level
137
+ * `warn` - logs message at `:warn` level
138
+ * `error` - logs message at `:error` level
139
+ * `fatal` - logs message at `:fatal` level
140
+
141
+ To log a message, simply choose one of the above types and pass in the actual message. For example, to log successfully deployment at info level do:
142
+
143
+ ```ruby
144
+ logger.success "Deployed successfully"
145
+ # =>
146
+ # ✔ success Deployed successfully
147
+ ```
148
+
149
+ Or pass in multiple messages:
150
+
151
+ ```ruby
152
+ logger.success "Deployed", "successfully"
153
+ # =>
154
+ # ✔ success Deployed successfully
155
+ ```
156
+
157
+ You can delay message evaluation by passing it inside a block:
158
+
159
+ ```ruby
160
+ logger.success { "Dynamically generated info" }
161
+ # =>
162
+ # ✔ success Dynamically generated info
163
+ ```
164
+
165
+ Similar to regular logging, you cal split your message into chunks inside a block:
166
+
167
+ ```ruby
168
+ logger.success { ["Dynamically", "generated", "info"] }
169
+ # =>
170
+ # ✔ success Dynamically generated info
171
+ ```
172
+
173
+ The above comes handy when paired with [structured data](#23-structured-data).
174
+
175
+ #### 2.1.1 Exceptions
176
+
177
+ You can also report on exceptions.
178
+
179
+ For example, let's say you caught an exception about incorrect data format and use `fatal` level to log it:
180
+
181
+ ```ruby
182
+ begin
183
+ raise ArgumentError, "Wrong data"
184
+ rescue => ex
185
+ logger.fatal("Error:", ex)
186
+ end
187
+ ```
188
+
189
+ This will result in a message followed by a full backtrace:
190
+
191
+ ```ruby
192
+ # =>
193
+ # ! fatal Error: Wrong data
194
+ # tty-logger/spec/unit/exception_spec.rb:12:in `block (2 levels) in <top (required)>'
195
+ # rspec-core-3.8.2/lib/rspec/core/example.rb:257:in `instance_exec'
196
+ # rspec-core-3.8.2/lib/rspec/core/example.rb:257:in `block in run'
197
+ ```
198
+
199
+ #### 2.1.2 Types
200
+
201
+ You can define custom log types via the `types` configuration option:
202
+
203
+ For example, if you want to add `thanks` and `done` log types, you need to provide their names along with logging levels. You can further customise the `:console` output with your desired styling:
204
+
205
+ ```ruby
206
+ logger = TTY::Logger.new do |config|
207
+ config.types = {
208
+ thanks: {level: :info},
209
+ done: {level: :info}
210
+ }
211
+ config.handlers = [
212
+ [:console, {
213
+ styles: {
214
+ thanks: {
215
+ symbol: "❤️ ",
216
+ label: "thanks",
217
+ color: :magenta,
218
+ levelpad: 0
219
+ },
220
+ done: {
221
+ symbol: "!!",
222
+ label: "done",
223
+ color: :green,
224
+ levelpad: 2
225
+ }
226
+ }
227
+ }]
228
+ ]
229
+ end
230
+ ```
231
+
232
+ Once defined, you can call new log types:
233
+
234
+ ```ruby
235
+ logger.thanks("Great work!")
236
+ logger.done("Work done!")
237
+ # =>
238
+ # ❤️ thanks Great work!
239
+ # !! done Work done!
240
+ ```
241
+
242
+ ![](assets/tty-logger-custom-log-types.png)
243
+
244
+ ### 2.2 Levels
245
+
246
+ The supported levels, ordered by precedence, are:
247
+
248
+ * `:debug` - for debug-related messages
249
+ * `:info` - for information of any kind
250
+ * `:warn` - for warnings
251
+ * `:error` - for errors
252
+ * `:fatal` - for fatal conditions
253
+
254
+ So the order is: `:debug` < `:info` < `:warn` < `:error` < `:fatal`
255
+
256
+ For example, `:info` takes precedence over `:debug`. If your log level is set to `:info`, `:info`, `:warn`, `:error` and `:fatal` will be printed to the console. If your log level is set to `:warn`, only `:warn`, `:error` and `:fatal` will be printed.
257
+
258
+ You can set level using the `level` configuration option. The value can be a symbol, a string or level constant. For example, `:info`, `INFO` or `TTY::Logger::INFO_LEVEL` will qualify as valid level value.
259
+
260
+ ```ruby
261
+ TTY::Logger.new do |config|
262
+ config.level = :info # or "INFO" or TTY::Logger::INFO_LEVEL
263
+ end
264
+ ```
265
+
266
+ Or you can specific level for each log events handler.
267
+
268
+ For example, to log messages above `:info` level to a stream and only `:error` level events to the console do:
269
+
270
+ ```ruby
271
+ logger = TTY::Logger.new do |config|
272
+ config.handlers = [
273
+ [:console, level: :error],
274
+ [:stream, level: :info]
275
+ ]
276
+ end
277
+ ```
278
+
279
+ You can also change the [output streams](#28-output-streams) for each handler.
280
+
281
+ #### 2.2.1 Scoped Level
282
+
283
+ You can temporarily change level, raise it or lower it by using the `log_at` call. By default messages are logged at `:info` level, but you can change this for the duration of a block:
26
284
 
27
285
  ```ruby
28
286
  logger = TTY::Logger.new
287
+
288
+ logger.info("not logged")
289
+
290
+ logger.log_at :debug do
291
+ logger.debug("logged")
292
+ end
293
+ # =>
294
+ # • debug logged
295
+ ```
296
+
297
+ Or elevate a level to an error with a constant `ERROR_LEVEL`:
298
+
299
+ ```ruby
300
+ logger.log_at TTY::Logger::ERROR_LEVEL do
301
+ logger.debug("not logged")
302
+ logger.error("logged")
303
+ end
304
+ # =>
305
+ # ⨯ error logged
306
+ ```
307
+
308
+ ### 2.3 Structured data
309
+
310
+ To add global data available for all logger calls:
311
+
312
+ ```ruby
313
+ logger = TTY::Logger.new(fields: {app: "myapp", env: "prod"})
314
+
315
+ logger.info("Deploying...")
316
+ # =>
317
+ # ℹ info Deploying... app=myapp env=prod
318
+ ```
319
+
320
+ To only add data for a single log event:
321
+
322
+ ```ruby
323
+ logger = TTY::Logger.new
324
+ logger.wait "Ready to deploy", app: "myapp", env: "prod"
325
+ # =>
326
+ # … waiting Ready to deploy app=myapp env=prod
327
+ ```
328
+
329
+ You can delay data evaluation until it's evaluated by passing it inside a block:
330
+
331
+ ```ruby
332
+ logger.wait { ["Ready to deploy", {app: "myapp", env: "prod"}] }
333
+ # =>
334
+ # … waiting Ready to deploy app=myapp env=prod
335
+ ```
336
+
337
+ ### 2.4 Configuration
338
+
339
+ All the configuration options can be changed globally via `configure` or per logger instance.
340
+
341
+ * `:filters` - the storage of placeholders to filter sensitive data out from the logs. Defaults to `{}`.
342
+ * `:formatter` - the formatter used to display structured data. Defaults to `:text`. See [Formatters](#27-formatters) for more details.
343
+ * `:handlers` - the handlers used to log messages. Defaults to `[:console]`. See [Handlers](#26-handlers) for more details.
344
+ * `:level` - the logging level. Any message logged below this level will be simply ignored. Each handler may have it's own default level. Defaults to `:info`
345
+ * `:max_bytes` - the maximum message size to be logged in bytes. Defaults to `8192` bytes. The truncated message will have `...` at the end.
346
+ * `:max_depth` - the maximum depth for nested structured data. Defaults to `3`.
347
+ * `:metadata` - the meta info to display before the message, can be `:pid`, `:date`, `:time` or `:file`. Defaults to empty array `[]`, no metadata. Setting this to `:all` will print all the metadata.
348
+ * `:types` - the new custom log types. Defaults to `{}`.
349
+ * `:date_format` - uses `strftime` format to display dates. Defaults to `"%F"`.
350
+ * `:time_format` - uses `strftime` format to display times. Defaults to `"%T.%3N"`.
351
+
352
+ For example, to configure `:max_bytes`, `:level` and `:metadata` for all logger instances do:
353
+
354
+ ```ruby
355
+ TTY::Logger.configure do |config|
356
+ config.max_bytes = 2**10
357
+ config.level = :error
358
+ config.metadata = [:time, :date]
359
+ end
360
+ ```
361
+
362
+ Or if you wish to setup configuration per logger instance use block:
363
+
364
+ ```ruby
365
+ logger = TTY::Logger.new do |config|
366
+ config.max_bytes = 2**20
367
+ config.metadata = [:all]
368
+ end
369
+ ```
370
+
371
+ You can also change the logger's configuration at runtime:
372
+
373
+ ```ruby
374
+ logger.configure do |config|
375
+ config.level = :debug
376
+ end
377
+ ```
378
+
379
+ ### 2.4.1 Metadata
380
+
381
+ The `:metdata` configuration option can include the following symbols:
382
+
383
+ * `:pid` - the log event process identifier
384
+ * `:date` - the log event date
385
+ * `:time` - the log event time
386
+ * `:file` - the file with a line number the log event is triggered from
387
+
388
+ ### 2.4.2 Filters
389
+
390
+ You can filter sensitive data out of log output with `filters` configuration option. The `filters` can be further configured to remove info from log message with `message` or structured data with `data`. Both methods, as a value accept a list of sensitive items to search for.
391
+
392
+ If you want to filter sensitive information from log messages use `message`:
393
+
394
+ ```ruby
395
+ logger = TTY::Logger.new(output: output) do |config|
396
+ config.filters.message = %w[secret password]
397
+ end
398
+ ```
399
+
400
+ Which by default will replace each matching string with `[FILTERED]` placeholder:
401
+
402
+ ```ruby
403
+ logger.info("Super secret info with password")
404
+ # =>
405
+ # ℹ info Super [FILTERED] info with [FILTERED]
406
+ ```
407
+
408
+ You can also replace each data item with a custom placeholder. To do so use a `:mask` keyword with a replacement placeholder.
409
+
410
+ For example, to replace "secret" content with placeholder `"<SECRET>"` do:
411
+
412
+ ```ruby
413
+ logger = TTY::Logger.new do |config|
414
+ config.filters.message = %w[secret]
415
+ config.filters.mask = "<SECRET>"
416
+ end
417
+ ```
418
+
419
+ When logged, it will produce:
420
+
421
+ ```ruby
422
+ logger.info("Super secret info")
423
+ # =>
424
+ # ℹ info Super <SECRET> info
425
+ ```
426
+
427
+ To filter out sensitive information out of structured data use `data` method. By default any value matching a parameter name will be filtered regardless of the level of nesting. If you wish to filter only a specific deeply nested key use a dot notation like `params.card.password` to only filter `{params: {card: {password: "Secret123"}}}`.
428
+
429
+ For example to filter out a `:password` from data do:
430
+
431
+ ```ruby
432
+ logger = TTY::Logger.new do |config|
433
+ config.filters.data = %i[password]
434
+ end
435
+ ```
436
+
437
+ This will filter out any key matching password:
438
+
439
+ ```ruby
440
+ logger.info("Secret info", password: "Secret123", email: "")
441
+ # =>
442
+ # ℹ info Secret info password="[FILTERED]" email="secret@example.com"
29
443
  ```
30
444
 
445
+ But also any nested data item:
446
+
447
+ ```ruby
448
+ logger.info("Secret info", params: {password: "Secret123", email: ""})
449
+ # =>
450
+ # ℹ info Secret info params={password="[FILTERED]" email="secret@example.com"}
451
+ ```
452
+
453
+ You're not limited to using only direct string comparison. You can also match based on regular expressions. For example, to match keys starting with `ba` we can add a following filter:
454
+
455
+ ```ruby
456
+ logger = TTY::Logger.new do |config|
457
+ config.filters.data = [/ba/]
458
+ end
459
+ ```
460
+
461
+ Then appropriate values will be masked:
462
+
463
+ ```ruby
464
+ logger.info("Filtering data", {"foo" => {"bar" => "val", "baz" => "val"}})
465
+ # =>
466
+ # ℹ info Filtering data foo={bar="[FILTERED]" baz="[FILTERED]"}
467
+ ```
468
+
469
+ You can mix and match. To filter keys based on pattern inside a deeply nested hash use dot notation with regular expression. For example, to find keys for the `:foo` parent key that starts with `:b` character, we could do:
470
+
471
+ ```ruby
472
+ logger = TTY::Logger.new do |config|
473
+ config.filters.data = [/^foo\.b/]
474
+ end
475
+ ```
476
+
477
+ Then only keys under the `:foo` key will be filtered:
478
+
479
+ ```ruby
480
+ logger.info("Filtering data", {"foo" => {"bar" => "val"}, "baz" => {"bar" => val"}})
481
+ # =>
482
+ # ℹ info Filtering data foo={bar="[FILTERED]"} baz={bar=val}
483
+ ```
484
+
485
+ ### 2.5 Cloning
486
+
487
+ You can create a copy of a logger with the current configuration using the `copy` method.
488
+
489
+ For example, given the following logger with `:app` and `:env` data:
490
+
491
+ ```ruby
492
+ logger = TTY::Logger.new(fields: {app: "parent", env: "prod"})
493
+ ```
494
+
495
+ We can create a copy with a custom configuration that changes filtered message content and `:app` data:
496
+
497
+ ```ruby
498
+ child_logger = logger.copy(app: "child") do |config|
499
+ config.filters = ["logging"]
500
+ end
501
+ ```
502
+
503
+ ```ruby
504
+ logger.info("Parent logging")
505
+ child_logger.warn("Child logging")
506
+ # =>
507
+ # ℹ info Parent logging app=parent env=prod
508
+ # ⚠ warning Child [FILTERED] app=child env=prod
509
+ ```
510
+
511
+ ### 2.6 Handlers
512
+
513
+ `TTY::Logger` supports many ways to handle log messages.
514
+
515
+ The available handlers by default are:
516
+
517
+ * `:console` - log messages to the console, enabled by default
518
+ * `:null` - discards any log messages
519
+ * `:stream` - log messages to an `IO` stream, a file, a socket or a console.
520
+
521
+ You can also implement your own [custom handler](#263-custom-handler).
522
+
523
+ The handlers can be configured via global or instance configuration with `handlers`. The handler can be a name or a class name:
524
+
525
+ ```ruby
526
+ TTY::Logger.new do |config|
527
+ config.handlers = [:console]
528
+ end
529
+ ```
530
+
531
+ Or using class name:
532
+
533
+ ```ruby
534
+ TTY::Logger.new do |config|
535
+ config.handlers = [TTY::Logger::Handlers::Console]
536
+ end
537
+ ```
538
+
539
+ Handlers can also be added/removed dynamically through `add_handler` or `remove_handler`.
540
+
541
+ ```ruby
542
+ logger = TTY::Logger.new
543
+ logger.add_handler(:console)
544
+ logger.remove_handler(:console)
545
+ ```
546
+
547
+ #### 2.6.1 Console Handler
548
+
549
+ The console handler prints log messages to the console. It supports the following options:
550
+
551
+ * `:styles` - a hash of styling options.
552
+ * `:formatter` - the formatter for log messages. Defaults to `:text`
553
+ * `:output` - the device to log error messages to. Defaults to `$stderr`
554
+ * `:message_format` - uses `sprintf` format to display messages. Defaults to `"%-25s"`.
555
+
556
+ The supported options in the `:styles` are:
557
+
558
+ * `:label` - the name for the log message.
559
+ * `:symbol` - the graphics to display before the log message label.
560
+ * `:color` - the color for the log message.
561
+ * `:levelpad` - the extra amount of padding used to display log label.
562
+
563
+ See the [TTY::Logger::Handlers::Console](https://github.com/piotrmurach/tty-logger/blob/master/lib/tty/logger/handlers/console.rb) for full list of styles.
564
+
565
+ Console handler has many default styles such as `success` and `error`:
566
+
567
+ ```ruby
568
+ logger = TTY::Logger.new
569
+ logger.success("Default success")
570
+ logger.error("Default error")
571
+ # =>
572
+ # ✔ success Default success
573
+ # ⨯ error Default error
574
+ ```
575
+
576
+ You can change the default styling with a tuple of handler name and options hash.
577
+
578
+ In our example, we want to change the styling of `success` and `error`:
579
+
580
+ ```ruby
581
+ new_styles = {
582
+ styles: {
583
+ success: {
584
+ symbol: "+",
585
+ label: "Ohh yes"
586
+ },
587
+ error: {
588
+ symbol: "!",
589
+ label: "Dooh",
590
+ levelpad: 3 # the amount of extra padding to align level names in a column
591
+ }
592
+ }
593
+ }
594
+ ```
595
+
596
+ And then use the `new_styles` when providing `handlers` configuration:
597
+
598
+ ```ruby
599
+ styled_logger = TTY::Logger.new do |config|
600
+ config.handlers = [[:console, new_styles]]
601
+ end
602
+
603
+ styled_logger.success("Custom success")
604
+ styled_logger.error("Custom error")
605
+ # =>
606
+ # + Ohh yes Custom success
607
+ # ! Dooh Custom error
608
+ ```
609
+
610
+ To increase message padding to a percentage of terminal width (depends on [tty-screen](https://github.com/piotrmurach/tty-screen/)):
611
+
612
+ ```ruby
613
+ TTY::Logger.new do |config|
614
+ padding = (TTY::Screen.columns * 0.4).to_i
615
+ config.handlers = [[:console, { message_format: "%-#{padding}s" }]]
616
+ end
617
+ ```
618
+
619
+ #### 2.6.2 Stream handler
620
+
621
+ To send log event data outside of console to another service or `IO` stream, you can use `:stream` handler.
622
+
623
+ ```ruby
624
+ logger = TTY::Logger.new(output: output) do |config|
625
+ config.handlers = [:stream]
626
+ config.metadata = [:all]
627
+ end
628
+ ```
629
+
630
+ By default, the output will be a plain text streamed to console. The text contains key and value pairs of all the metadata and the message of the log event.
631
+
632
+ ```ruby
633
+ logger.info("Info about the deploy", app:"myap", env:"prod")
634
+ # =>
635
+ # pid=18315 date="2019-07-21" time="15:42:12.463" path="examples/stream.rb:17:in`<main>`"
636
+ # level=info message="Info about the deploy" app=myapp env=prod
637
+ ```
638
+
639
+ You can change stream formatter for ease of working with external services such as `Logstash`. For example, to use `:stream` handler with `:json` format do:
640
+
641
+ ```ruby
642
+ logger = TTY::Logger.new(output: output) do |config|
643
+ config.handlers = [[:stream, formatter: :json]]
644
+ config.metadata = [:all]
645
+ end
646
+ ```
647
+
648
+ This will output JSON formatted text streamed to console.
649
+
650
+ ```ruby
651
+ logger.info("Info about the deploy", app="myap", env="prod")
652
+ # =>
653
+ # {"pid":18513,"date":"2019-07-21","time":"15:54:09.924","path":"examples/stream.rb:17:in`<main>`",
654
+ # "level":"info","message":"Info about the deploy","app":"myapp","env":"prod"}
655
+ ```
656
+
657
+ #### 2.6.3 Custom Handler
658
+
659
+ You can create your own log event handler if the default ones don't match your needs.
660
+
661
+ The design of your handler should include two calls:
662
+
663
+ * `initialize` - where all dependencies get injected
664
+ * `call` - where the log event is handled
665
+
666
+ We start with the implementation of the `initialize` method. This method by default is injected with `:config` key that includes all global configuration options. The `:output` key for displaying log message in the console and `:formatter`.
667
+
668
+ In our case we also add custom `:label`:
669
+
670
+ ```ruby
671
+ class MyHandler
672
+ def initialize(output: nil, config: nil, formatter: nil, label: nil)
673
+ @label = label
674
+ @output = output
675
+ end
676
+ end
677
+ ```
678
+
679
+ Next is the `call` method that accepts the log `event`.
680
+
681
+ The `event` has the following attributes:
682
+
683
+ * `message` - the array of message parts to be printed
684
+ * `fields` - the structured data supplied with the event
685
+ * `metadata` - the additional info about the event. See [metadata](#241-metadata) section for details.
686
+
687
+ We add implementation of `call`:
688
+
689
+ ```ruby
690
+ class MyHandler
691
+ def initialize(output: nil, config: nil, label: nil)
692
+ @label = label
693
+ @output = output
694
+ end
695
+
696
+ def call(event)
697
+ @output.puts "(#{@label}) #{event.message.join}"
698
+ end
699
+ end
700
+ ```
701
+
702
+ Once you have your custom handler, you need to register it with the logger. You can do so using the `handlers` configuration option:
703
+
704
+ ```ruby
705
+ logger = TTY::Logger.new do |config|
706
+ config.handlers = [[MyHandler, label: "myhandler"]]
707
+ end
708
+ ```
709
+
710
+ Or add your handler dynamically after logger initialization:
711
+
712
+ ```ruby
713
+ logger = TTY::Logger.new
714
+ logger.add_handler [MyHandler, label: "myhandler"]
715
+ ```
716
+
717
+ #### 2.6.4 Multiple Handlers
718
+
719
+ You can define as many handlers as you need. For example, you may log messages both to console and stream:
720
+
721
+ ```ruby
722
+ logger = TTY::Logger.new do |config|
723
+ config.handlers = [:console, :stream]
724
+ end
725
+ ```
726
+
727
+ Each handler can have its own configuration. For example, you can register `:console` handler to log messages above error level and `:stream` that logs any message with info or higher level:
728
+
729
+ ```ruby
730
+ logger = TTY::Logger.new do |config|
731
+ config.handlers = [
732
+ [:console, level: :error],
733
+ [:stream, level: :info]
734
+ ]
735
+ end
736
+ ```
737
+
738
+ ### 2.7 Formatters
739
+
740
+ The available formatters are:
741
+
742
+ * `:json`
743
+ * `:text`
744
+
745
+ You can configure format for all the handlers:
746
+
747
+ ```ruby
748
+ TTY::Logger.new do |config|
749
+ config.formatter = :json
750
+ end
751
+ ```
752
+
753
+ Or specify a different formatter for each handler. For example, let's say you want to log to console twice, once with default formatter and once with `:json` formatter:
754
+
755
+ ```ruby
756
+ TTY::Logger.new do |config|
757
+ config.handlers = [:console, [:console, formatter: :json]]
758
+ end
759
+ ```
760
+
761
+ ### 2.8 Output Streams
762
+
763
+ By default all log events are output to `stderr`. You can change this using configuration `output` option. Any `IO`-like stream such as file, socket or console can be used. For example, to log all messages to a file do:
764
+
765
+ ```ruby
766
+ logger = TTY::Logger.new do |config|
767
+ config.output = File.open("errors.log", "a")
768
+ end
769
+ ```
770
+
771
+ You can also specify multiple streams that all log messages will be sent to:
772
+
773
+ ```ruby
774
+ logger = TTY::Logger.new do |config|
775
+ config.output = [$stderr, File.open("errors.log", "a")]
776
+ end
777
+ ```
778
+
779
+ Conversely, you can specify different output for each of the handlers used. For example, you can output all messages above info level to a file with a stream handler and only show error messages in the console with a nicely formatted output.
780
+
781
+ ```ruby
782
+ logger = TTY::Logger.new do |config|
783
+ config.handlers = [
784
+ [:console, output: $stderr, level: :error],
785
+ [:stream, output: File.open("errors.log", "a"), level: :info)]
786
+ ]
787
+ end
788
+ ```
789
+
790
+ ## 3. Community Extensions
791
+
792
+ ### 3.1 Sentry Handler
793
+
794
+ [tty-logger-raven](https://github.com/ianks/tty-logger-raven) provides an extension for Sentry.io.
795
+
31
796
  ## Development
32
797
 
33
798
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.