sinlog 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d3fe4df43741e8ad7d0401e9a0c60ec0d7c429f4712ad67b2a930b2f3c40c270
4
+ data.tar.gz: 4a79394fb547e8dc2e16f5418e80ebd170b93930fcd63c4490241e844a9cdc40
5
+ SHA512:
6
+ metadata.gz: bb676571612ede8908f76b040756ee8b4de13be37abe2651f7a12e4e7a1b357bc5b3020be2f0631a4e91ddaf1c5b055ff35f6b3f48fcc3ebd4ae4e317cda92a2
7
+ data.tar.gz: e08d902583da75273357bed1c1615cafd80d128fa9cb5db6c094129ec8a40cff4ea11c2981df403e6b2cf18b260eaf520245fb7e9680874ed71f2356b613008c
data/License ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 2moe
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
13
+ all 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
21
+ THE SOFTWARE.
data/docs/Readme-zh.md ADDED
@@ -0,0 +1,310 @@
1
+ # Sinlog
2
+
3
+ 一个非常非常简单的 ruby 单例日志记录器,其中日志级别带有彩色高亮。
4
+
5
+ > 单例(Singleton)意味着整个程序会共享同一个实例(日志记录器)。
6
+
7
+ ---
8
+
9
+ | Language/語言 | ID |
10
+ | ------------------------------- | ---------- |
11
+ | 简体中文 | zh-Hans-CN |
12
+ | [English](./Readme.md) | en-Latn-US |
13
+ | [繁體中文](./Readme-zh-Hant.md) | zh-Hant-TW |
14
+
15
+ ---
16
+
17
+ <details>
18
+ <summary>
19
+ 目录(点击展开)
20
+ </summary>
21
+
22
+ - [Learn Sinlog API By Example](#learn-sinlog-api-by-example)
23
+ - [include module](#include-module)
24
+ - [LambdaExt](#lambdaext)
25
+ - [LogLambdaExt](#loglambdaext)
26
+ - [Classic Method Call](#classic-method-call)
27
+ - [进阶](#进阶)
28
+ - [Real World Example](#real-world-example)
29
+ - [日志级别](#日志级别)
30
+ - [环境变量](#环境变量)
31
+ - [日志输出设备/路径](#日志输出设备路径)
32
+ - [其他 logger 方法](#其他-logger-方法)
33
+ - [注意事项](#注意事项)
34
+ - [题外话](#题外话)
35
+ - [License](#license)
36
+
37
+ </details>
38
+
39
+ ## Learn Sinlog API By Example
40
+
41
+ 首先,安装 sinlog。
42
+
43
+ ```sh
44
+ gem install sinlog
45
+ ```
46
+
47
+ 然后,我们可以运行 `irb` 来快速体验一番。
48
+
49
+ ### include module
50
+
51
+ #### LambdaExt
52
+
53
+ 当出现: `irb(main):001>` 后,我们就能开始操作了。
54
+
55
+ ```ruby
56
+ irb(main):001> require 'sinlog'
57
+
58
+ irb(main):002> include Sinlog::LambdaExt
59
+ # 它提供了: dbg, info, warning, err, fatal, unk
60
+ # 我们可以用 .tap(&dbg) 或 .then(&dbg) 来调用。
61
+
62
+ irb(main):003> 'debug'.tap(&dbg)
63
+ irb(main):004> 'information'.tap(&info)
64
+
65
+ # 注:创建 warn 方法,会导致 irb 的补全功能出问题。
66
+ # 因此 LambdaExt 用的是 warning, 而不是 warn。
67
+ # 您如果确实需要 warn,那就调用 include Sinlog::LambdaWarnExt
68
+ irb(main):005> 'warning'.tap(&warning)
69
+
70
+ irb(main):006> 'error'.tap(&err)
71
+ irb(main):007> 'fatal'.tap(&fatal)
72
+ irb(main):008> 'unknown'.tap(&unk)
73
+ ```
74
+
75
+ <img src="../assets/img/LambdaExt.jpg" alt="LambdaExt" style="width: 50%; height: 50%">
76
+
77
+ LambdaExt 提供了:
78
+
79
+ - dbg
80
+ - info
81
+ - warning
82
+ - wng (与 warning 相同,只是名称不同)
83
+ - err
84
+ - fatal
85
+ - unk
86
+
87
+ #### LogLambdaExt
88
+
89
+ 有一个与 LambdaExt 特别相似的模块,名为 LogLambdaExt。
90
+ 它们之间最主要的区别在于 lambda 函数的名称。
91
+
92
+ - LogLambdaExt 带有 `log_` 前缀
93
+ - LambdaExt 没有
94
+
95
+ LambdaExt 与 LogLambdaExt 可以同时 include, 不过在一般情况下,我们引入其中一个就够用了。
96
+
97
+ 至于哪一个更好呢?
98
+
99
+ 我们不妨亲自上手试试,了解其中的区别,最后挑一个自己喜欢的。
100
+
101
+ ```ruby
102
+ irb(main):009> include Sinlog::LogLambdaExt
103
+ # 它提供了 log_dbg, log_info, log_warn, log_err, log_fatal, log_unk
104
+ # 我们可以用 .tap(&log_dbg) 或 .then(&log_dbg) 来调用。
105
+
106
+ irb(main):010> "debug".tap(&log_dbg)
107
+ irb(main):011> "information".tap(&log_info)
108
+
109
+ # 注:这里用的是 log_warn,而不是 log_warning
110
+ irb(main):012> "warning".tap(&log_warn)
111
+
112
+ irb(main):013> "error".tap(&log_err)
113
+ irb(main):014> "fatal".tap(&log_fatal)
114
+ irb(main):015> "unknown".tap(&log_unk)
115
+ ```
116
+
117
+ ```ruby
118
+ # 这是一个更复杂的例子
119
+ irb(main):016> require 'pathname'
120
+
121
+ irb(main):017> Pathname('lib/lambda.rb').tap do
122
+ "Filename: #{it}".then(&log_info)
123
+ "size: #{
124
+ it
125
+ .tap{ '⚠️ 获取文件大小可能会失败'.then(&log_warn) }
126
+ .size
127
+ }".then(&log_info)
128
+ end
129
+ ```
130
+
131
+ <img src="../assets/img/LogLambdaExt.jpg" alt="LogLambdaExt" style="width: 90%; height: 90%">
132
+
133
+ LogLambdaExt 提供了:
134
+
135
+ - log_dbg
136
+ - log_info
137
+ - log_warn
138
+ - log_warning (与 log_warn 相同,只是名称不同)
139
+ - log_wng (与 log_warn 相同,只是名称不同)
140
+ - log_err
141
+ - log_fatal
142
+ - log_unk
143
+
144
+ ### Classic Method Call
145
+
146
+ 您如果不喜欢 lambda,那就试试经典的方法调用吧!
147
+
148
+ 先运行 irb 进入 ruby repl,接着一步一步运行。
149
+
150
+ ```ruby
151
+ irb(main):001> require 'sinlog'
152
+
153
+ irb(main):002> log = Sinlog.instance.logger
154
+
155
+ irb(main):003> log.debug 'debug'
156
+ irb(main):004> log.info 'information'
157
+ irb(main):005> log.warn 'warning'
158
+ irb(main):006> log.error 'error'
159
+ irb(main):007> log.fatal 'fatal'
160
+ irb(main):008> log.unknown 'unknown'
161
+ ```
162
+
163
+ Sinlog.instance.logger 提供了 ruby 标准库的 logger 的方法。
164
+
165
+ 最常见的有:
166
+
167
+ - debug
168
+ - info
169
+ - warn
170
+ - error
171
+ - fatal
172
+ - unknown
173
+
174
+ ## 进阶
175
+
176
+ 在亲自上手尝试之后,我们已经对 sinlog 有了初步的了解。
177
+ 在一般情况下,了解其基本用法就已经足够了。
178
+
179
+ 您如果对此感兴趣的话,不妨与我一同继续探索。
180
+
181
+ ### Real World Example
182
+
183
+ 在现实世界中,我们的程序可能会是这样子的:
184
+
185
+ ```ruby
186
+ require 'sinlog'
187
+
188
+ class EpubProcessor
189
+ def initialize(epub_file, logger = nil)
190
+ @epub = epub_file
191
+ @logger = logger || Sinlog.instance.tap { it.fetch_env_and_update_log_level("XX_LOG") }.logger
192
+ @logger.debug "EpubProcessor class 初始化完成。"
193
+
194
+ end
195
+ end
196
+ ```
197
+
198
+ 我们可能会定义类的 @logger 实例变量,并允许自定义 logger。
199
+
200
+ 简单来说,我们可能会对 logger 进行更精细化的配置。
201
+
202
+ 在接下来的内容中,我们将探讨如何进行“更精细化的配置”。
203
+
204
+ ### 日志级别
205
+
206
+ 日志级别从低到高,依次是:
207
+
208
+ - debug = 0
209
+ - info = 1
210
+ - warn = 2
211
+ - error = 3
212
+ - fatal = 4
213
+ - unknown = 5
214
+
215
+ ```ruby
216
+ p Sinlog::LV
217
+ # => {debug: 0, info: 1, warn: 2, error: 3, fatal: 4, unknown: 5}
218
+
219
+ # 将日志级别修改为 warn
220
+ log = Sinlog.instance.logger.tap {it.level = Sinlog::LV[:warn]}
221
+ # 或者是:
222
+ # log = Sinlog.instance.logger.tap {it.level = 2}
223
+
224
+ log.error "这条消息会显示出来!低级别 WARN(2)会显示高级别 ERROR(3) 的日志。"
225
+ log.info "这条消息不会显示出来!高级别 WARN(2) 不会显示低级别 INFO(1) 的日志。"
226
+ ```
227
+
228
+ - 日志级别越低,显示的内容越详细。
229
+ - 低级别 **会** 显示高级别的日志。
230
+ - 高级别 **不会** 显示低级别的日志。
231
+
232
+ ### 环境变量
233
+
234
+ 在现实世界中,对于客户端应用,最后运行程序的是普通用户。
235
+ 为了能让普通用户直接配置 log.level,我们可以通过环境变量来配置。
236
+
237
+ > 使用环境变量足够简单也足够高效。
238
+
239
+ Sinlog 在默认情况下,会尝试读取环境变量 RUBY_LOG 的值。
240
+
241
+ 本质上调用了 `fetch_env_and_update_log_level(env_name = 'RUBY_LOG')` 函数。
242
+
243
+ - 若该环境变量不存在,则使用 debug(0)。
244
+ - 若该环境变量存在,且其值为空,则使用 unknown(5)。
245
+ - 若该环境变量的值无效,则使用 unknown(5)。
246
+
247
+ 我们可以用 POSIX-sh 设置环境变量,然后 logger 在初始化的时候,就会自动将日志级别设置为(RUBY_LOG的值)warn。
248
+
249
+ ```sh
250
+ # 可选值:debug, info, warn, error, fatal
251
+ export RUBY_LOG=warn
252
+ ```
253
+
254
+ 您如果不想要默认的 RUBY_LOG 环境变量,而是想要 XX_CLI_LOG 的值,那可以这样子做:
255
+
256
+ POSIX-sh:
257
+
258
+ ```sh
259
+ export XX_CLI_LOG=info
260
+ ```
261
+
262
+ ruby:
263
+
264
+ ```ruby
265
+ logger = Sinlog.instance.tap { it.fetch_env_and_update_log_level("XX_CLI_LOG") }.logger
266
+
267
+ logger.debug "由于当前日志级别为 INFO(1),因此不会显示此消息 DEBUG(0)。"
268
+ logger.info "Hello!"
269
+ ```
270
+
271
+ ### 日志输出设备/路径
272
+
273
+ 默认情况下,Sinlog 会输出到 STDERR。
274
+
275
+ 您如果需要自定义日志输出路径的话,那可以调用 logger 的 reopen 方法。
276
+
277
+ ```ruby
278
+ # 日志会输出到 a.log 文件
279
+ log = Sinlog.instance.logger.tap {it.reopen("a.log")}
280
+
281
+ log.error "发生甚么事了!QuQ"
282
+ ```
283
+
284
+ OR:
285
+
286
+ ```ruby
287
+ log = Sinlog.instance.logger
288
+ log.reopen("a.log")
289
+
290
+ log.error "发生甚么事了!QuQ"
291
+ ```
292
+
293
+ ### 其他 logger 方法
294
+
295
+ 除了 `.reopen`, `.level` 外,我们还可以在 `Sinlog.instance.logger` 上调用 ruby 标准库的 logger 的其他方法。
296
+
297
+ ### 注意事项
298
+
299
+ Sinlog 用的是 Singleton 单例模式,整个程序会共享同一个实例(日志记录器)。
300
+
301
+ 在同一个程序的 class A 中修改 Sinlog 后,会影响到 class B 的 Sinlog。
302
+
303
+ ## 题外话
304
+
305
+ 这是我发布的第一个 ruby gem。
306
+ 其中的 api 不一定符合地道的 ruby 用法,还请大家多多谅解。
307
+
308
+ ## License
309
+
310
+ [MIT License](../License)
data/docs/Readme.md ADDED
@@ -0,0 +1,313 @@
1
+ # Sinlog
2
+
3
+ A very, very simple Ruby singleton logger with colored log levels.
4
+
5
+ > Singleton means that the entire program will share the same instance (logger).
6
+
7
+ ---
8
+
9
+ | Language/語言 | ID |
10
+ | ------------------------------- | ---------- |
11
+ | English | en-Latn-US |
12
+ | [简体中文](./Readme-zh.md) | zh-Hans-CN |
13
+ | [繁體中文](./Readme-zh-Hant.md) | zh-Hant-TW |
14
+
15
+ > Want to support Mehr Sprachen/Más idiomas/Autres langues/Другие языки/...?
16
+ >
17
+ > Please feel free to send me an issue!
18
+
19
+ ---
20
+
21
+ <details>
22
+ <summary>
23
+ Table of Contents (click to expand)
24
+ </summary>
25
+
26
+ - [Learn Sinlog API By Example](#learn-sinlog-api-by-example)
27
+ - [include module](#include-module)
28
+ - [LambdaExt](#lambdaext)
29
+ - [LogLambdaExt](#loglambdaext)
30
+ - [Classic Method Call](#classic-method-call)
31
+ - [Advanced](#advanced)
32
+ - [Real World Example](#real-world-example)
33
+ - [Log Levels](#log-levels)
34
+ - [Environment Variables](#environment-variables)
35
+ - [Log Output Device/Path](#log-output-devicepath)
36
+ - [Other Logger Methods](#other-logger-methods)
37
+ - [Notes](#notes)
38
+ - [Side Note](#side-note)
39
+ - [License](#license)
40
+
41
+ </details>
42
+
43
+ ## Learn Sinlog API By Example
44
+
45
+ First, install `sinlog`.
46
+
47
+ ```sh
48
+ gem install sinlog
49
+ ```
50
+
51
+ Then, we can run `irb` to quickly try it out.
52
+
53
+ ### include module
54
+
55
+ #### LambdaExt
56
+
57
+ When you see: `irb(main):001>`, we can start operating.
58
+
59
+ ```ruby
60
+ irb(main):001> require 'sinlog'
61
+
62
+ irb(main):002> include Sinlog::LambdaExt
63
+ # It provides: dbg, info, warning, err, fatal, unk
64
+ # We can call them using .tap(&dbg) or .then(&dbg).
65
+
66
+ irb(main):003> 'debug'.tap(&dbg)
67
+ irb(main):004> 'information'.tap(&info)
68
+
69
+ # Note: Creating a warn method will cause issues with irb's auto-completion.
70
+ # Therefore, LambdaExt uses warning instead of warn.
71
+ # If you really need warn, then call include Sinlog::LambdaWarnExt
72
+ irb(main):005> 'warning'.tap(&warning)
73
+
74
+ irb(main):006> 'error'.tap(&err)
75
+ irb(main):007> 'fatal'.tap(&fatal)
76
+ irb(main):008> 'unknown'.tap(&unk)
77
+ ```
78
+
79
+ <img src="../assets/img/LambdaExt.jpg" alt="LambdaExt" style="width: 50%; height: 50%">
80
+
81
+ LambdaExt provides:
82
+
83
+ - dbg
84
+ - info
85
+ - warning
86
+ - wng (same as warning, just a different name)
87
+ - err
88
+ - fatal
89
+ - unk
90
+
91
+ #### LogLambdaExt
92
+
93
+ There is a module very similar to LambdaExt called LogLambdaExt.
94
+ The main difference between them is the naming of the lambda functions.
95
+
96
+ - LogLambdaExt has a `log_` prefix
97
+ - LambdaExt does not
98
+
99
+ LambdaExt and LogLambdaExt can be included simultaneously, but in general, including one of them is sufficient.
100
+
101
+ Which one is better?
102
+
103
+ Let's try them out to understand the differences and choose the one we prefer.
104
+
105
+ ```ruby
106
+ irb(main):009> include Sinlog::LogLambdaExt
107
+ # It provides log_dbg, log_info, log_warn, log_err, log_fatal, log_unk
108
+ # We can call them using .tap(&log_dbg) or .then(&log_dbg).
109
+
110
+ irb(main):010> "debug".tap(&log_dbg)
111
+ irb(main):011> "information".tap(&log_info)
112
+
113
+ # Note: Here we use log_warn, not log_warning
114
+ irb(main):012> "warning".tap(&log_warn)
115
+
116
+ irb(main):013> "error".tap(&log_err)
117
+ irb(main):014> "fatal".tap(&log_fatal)
118
+ irb(main):015> "unknown".tap(&log_unk)
119
+ ```
120
+
121
+ ```ruby
122
+ # Here is a more complex example
123
+ irb(main):016> require 'pathname'
124
+
125
+ irb(main):017> Pathname('lib/lambda.rb').tap do
126
+ "Filename: #{it}".then(&log_info)
127
+ "size: #{
128
+ it
129
+ .tap{ '⚠️ Getting file size might fail'.then(&log_warn) }
130
+ .size
131
+ }".then(&log_info)
132
+ end
133
+ ```
134
+
135
+ <img src="../assets/img/LogLambdaExt.jpg" alt="LogLambdaExt" style="width: 90%; height: 90%">
136
+
137
+ LogLambdaExt provides:
138
+
139
+ - log_dbg
140
+ - log_info
141
+ - log_warn
142
+ - log_warning (same as log_warn, just a different name)
143
+ - log_wng (same as log_warn, just a different name)
144
+ - log_err
145
+ - log_fatal
146
+ - log_unk
147
+
148
+ ### Classic Method Call
149
+
150
+ If you don't like lambdas, you can try the classic method call!
151
+
152
+ First, run `irb` to enter the Ruby REPL, then follow these steps:
153
+
154
+ ```ruby
155
+ irb(main):001> require 'sinlog'
156
+
157
+ irb(main):002> log = Sinlog.instance.logger
158
+
159
+ irb(main):003> log.debug 'debug'
160
+ irb(main):004> log.info 'information'
161
+ irb(main):005> log.warn 'warning'
162
+ irb(main):006> log.error 'error'
163
+ irb(main):007> log.fatal 'fatal'
164
+ irb(main):008> log.unknown 'unknown'
165
+ ```
166
+
167
+ Sinlog.instance.logger provides methods from Ruby's standard library logger.
168
+
169
+ The most common ones are:
170
+
171
+ - debug
172
+ - info
173
+ - warn
174
+ - error
175
+ - fatal
176
+ - unknown
177
+
178
+ ## Advanced
179
+
180
+ After trying it out ourselves, we have a basic understanding of `sinlog`.
181
+ In most cases, knowing its basic usage is sufficient.
182
+
183
+ If you're interested, let's continue exploring together.
184
+
185
+ ### Real World Example
186
+
187
+ In the real world, our program might look like this:
188
+
189
+ ```ruby
190
+ require 'sinlog'
191
+
192
+ class EpubProcessor
193
+ def initialize(epub_file, logger = nil)
194
+ @epub = epub_file
195
+ @logger = logger || Sinlog.instance.tap { it.fetch_env_and_update_log_level("XX_LOG") }.logger
196
+ @logger.debug "EpubProcessor class initialization completed."
197
+ end
198
+ end
199
+ ```
200
+
201
+ We might define a class's `@logger` instance variable and allow for a custom logger.
202
+
203
+ Simply put, we might configure the logger more finely.
204
+
205
+ In the following content, we will explore how to perform "more fine-grained configuration."
206
+
207
+ ### Log Levels
208
+
209
+ Log levels from low to high are:
210
+
211
+ - debug = 0
212
+ - info = 1
213
+ - warn = 2
214
+ - error = 3
215
+ - fatal = 4
216
+ - unknown = 5
217
+
218
+ ```ruby
219
+ p Sinlog::LV
220
+ # => {debug: 0, info: 1, warn: 2, error: 3, fatal: 4, unknown: 5}
221
+
222
+ # Change the log level to warn
223
+ log = Sinlog.instance.logger.tap { it.level = Sinlog::LV[:warn] }
224
+ # Or:
225
+ # log = Sinlog.instance.logger.tap { it.level = 2 }
226
+
227
+ log.error "This message will be displayed! Lower level WARN (2) will display higher level ERROR (3) logs."
228
+ log.info "This message will not be displayed! Higher level WARN (2) will not display lower level INFO (1) logs."
229
+ ```
230
+
231
+ - The lower the log level, the more detailed the content displayed.
232
+ - Lower levels **will** display higher level logs.
233
+ - Higher levels **will not** display lower level logs.
234
+
235
+ ### Environment Variables
236
+
237
+ In the real world, for client applications, the end users are typically regular users.
238
+ To allow them to configure `log.level` directly, we can use environment variables.
239
+
240
+ > Using environment variables is simple and efficient.
241
+
242
+ By default, Sinlog will attempt to read the value of the environment variable `RUBY_LOG`.
243
+
244
+ It essentially calls the function `fetch_env_and_update_log_level(env_name = 'RUBY_LOG')`.
245
+
246
+ - If the environment variable does not exist, it uses `debug(0)`.
247
+ - If the environment variable exists but is empty, it uses `unknown(5)`.
248
+ - If the environment variable's value is invalid, it uses `unknown(5)`.
249
+
250
+ We can set the environment variable using POSIX-sh, and then the logger will automatically set the log level to `warn` (the value of `RUBY_LOG`) during initialization.
251
+
252
+ ```sh
253
+ # Possible values: debug, info, warn, error, fatal
254
+ export RUBY_LOG=warn
255
+ ```
256
+
257
+ If you don't want to use the default `RUBY_LOG` environment variable and prefer to use the value of `XX_CLI_LOG`, you can do it like this:
258
+
259
+ POSIX-sh:
260
+
261
+ ```sh
262
+ export XX_CLI_LOG=info
263
+ ```
264
+
265
+ Ruby:
266
+
267
+ ```ruby
268
+ logger = Sinlog.instance.tap { it.fetch_env_and_update_log_level("XX_CLI_LOG") }.logger
269
+
270
+ logger.debug "This message will not be displayed because the current log level is INFO(1)."
271
+ logger.info "Hello!"
272
+ ```
273
+
274
+ ### Log Output Device/Path
275
+
276
+ By default, Sinlog outputs to `STDERR`.
277
+
278
+ If you need to customize the log output path, you can call the logger's `reopen` method.
279
+
280
+ ```ruby
281
+ # Logs will be output to the file a.log
282
+ log = Sinlog.instance.logger.tap { it.reopen("a.log") }
283
+
284
+ log.error "What happened! QuQ"
285
+ ```
286
+
287
+ OR:
288
+
289
+ ```ruby
290
+ log = Sinlog.instance.logger
291
+ log.reopen("a.log")
292
+
293
+ log.error "What happened! QuQ"
294
+ ```
295
+
296
+ ### Other Logger Methods
297
+
298
+ In addition to `.reopen` and `.level`, we can also call other methods from Ruby's standard library logger on `Sinlog.instance.logger`.
299
+
300
+ ### Notes
301
+
302
+ Sinlog uses the Singleton pattern, meaning the entire program will share the same instance (logger).
303
+
304
+ Modifying Sinlog in class A of the same program will affect Sinlog in class B.
305
+
306
+ ## Side Note
307
+
308
+ This is the first Ruby gem I have released.
309
+ The API might not fully adhere to idiomatic Ruby usage, so I appreciate your understanding.
310
+
311
+ ## License
312
+
313
+ [MIT License](../License)
data/lib/init.rb ADDED
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Logger Singleton Class
4
+ class Sinlog
5
+ include Singleton
6
+ attr_reader :logger
7
+
8
+ # Define colors for different log levels
9
+ COLORS = {
10
+ debug: "\e[34m", # Blue
11
+ info: "\e[36m", # Cyan
12
+ warn: "\e[33m", # Yellow
13
+ error: "\e[31m", # Red
14
+ fatal: "\e[35m", # Magenta
15
+ unknown: "\e[0m" # Reset
16
+ }.freeze
17
+
18
+ # log levels
19
+ LV = {
20
+ debug: Logger::DEBUG,
21
+ info: Logger::INFO,
22
+ warn: Logger::WARN,
23
+ error: Logger::ERROR,
24
+ fatal: Logger::FATAL,
25
+ unknown: Logger::UNKNOWN
26
+ }.freeze
27
+
28
+ # Example:
29
+ #
30
+ # logger = Sinlog.instance.logger
31
+ # logger.info "Information"
32
+ # logger.debug "This is a debug message"
33
+ #
34
+ # The log output format will be similar to:
35
+ #
36
+ # [INFO] 21:29:22.004 Information
37
+ # [DEBUG] 21:29:22.005 This is a debug message
38
+ #
39
+ # Where "INFO" is highlighted in cyan and "DEBUG" is highlighted in blue.
40
+ #
41
+ # The default log level is set based on the RUBY_LOG environment variable.
42
+ # If this variable is not set, the default level is DEBUG.
43
+ def initialize
44
+ @logger = Logger.new($stderr)
45
+ fetch_env_and_update_log_level
46
+ @logger.formatter = proc do |severity, datetime, progname, msg|
47
+ color = COLORS[severity.downcase.to_sym]
48
+ reset = COLORS[:unknown]
49
+ formatted_datetime = datetime.strftime('%H:%M:%S.%L')
50
+ prog = format_prog_name(progname)
51
+ "[#{color}#{severity}#{reset}] #{formatted_datetime} #{prog}#{msg}\n"
52
+ end
53
+ end
54
+
55
+ # Set the `@logger.level` (**log level**) based on the value of an environment variable.
56
+ #
57
+ # If env_name is not specified, it reads the value of the `RUBY_LOG` environment variable.
58
+ #
59
+ # - If the value exists, it is converted to lowercase, then to a symbol, and looked up in the LV hash;
60
+ # - If it does not exist, the default level is DEBUG;
61
+ # - If the lookup result is invalid, the level is set to UNKNOWN;
62
+ # - If the environment variable value is empty, the lookup result will be invalid,
63
+ # and the level will be set to UNKNOWN.
64
+ #
65
+ # Example:
66
+ #
67
+ # ENV["XX_LOG"] = "info" # or setenv in posix-sh: export XX_LOG=info
68
+ #
69
+ # logger = Sinlog.instance.tap { it.fetch_env_and_update_log_level("XX_LOG") }.logger
70
+ #
71
+ # logger.debug "This message will not be displayed because the current log level is info"
72
+ # logger.info "Hello!"
73
+ def fetch_env_and_update_log_level(env_name = 'RUBY_LOG')
74
+ env_lv = ENV[env_name]&.downcase&.to_sym || :debug
75
+
76
+ (LV[env_lv] || Logger::UNKNOWN)
77
+ .then { @logger.level = _1 }
78
+ end
79
+
80
+ private
81
+
82
+ def format_prog_name(progname)
83
+ return '' if progname.to_s.empty?
84
+
85
+ green = "\e[32m"
86
+ reset = "\e[0m"
87
+ space = ' '
88
+ "<#{green}#{progname}#{reset}>#{space}"
89
+ end
90
+ end
data/lib/lambda.rb ADDED
@@ -0,0 +1,184 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Lint/MissingCopEnableDirective, Style/ClassVars
4
+
5
+ class Sinlog
6
+ # Module to simplify logger calls using lambda expressions.
7
+ #
8
+ # The following methods define lambda expressions for different log levels.
9
+ # These lambdas can be used to log messages at the corresponding log level.
10
+ module LogLambdaExt
11
+ @@_sinlog_lambda_ext_logger_ = Sinlog.instance.logger
12
+
13
+ # Debug Logging
14
+ #
15
+ # Example:
16
+ #
17
+ # require 'sinlog'
18
+ # include Sinlog::LogLambdaExt
19
+ #
20
+ # 'Debug Message'.then(&log_dbg)
21
+ #
22
+ # cmd = %w[ls -l -h]
23
+ # "cmd_arr: #{cmd}".tap(&log_dbg)
24
+ def log_dbg
25
+ ->(msg) { @@_sinlog_lambda_ext_logger_.debug(msg) }
26
+ end
27
+
28
+ # Info(a.k.a. Information) Logging
29
+ #
30
+ # Example:
31
+ #
32
+ # require 'pathname'
33
+ #
34
+ # Pathname('lib/lambda.rb').tap {
35
+ # "Filename: #{it}".then(&log_info)
36
+ # "size: #{it.size}".then(&log_info)
37
+ # }
38
+ def log_info
39
+ ->(msg) { @@_sinlog_lambda_ext_logger_.info(msg) }
40
+ end
41
+
42
+ # Warning Logging
43
+ #
44
+ # Example:
45
+ #
46
+ # arr = (0..256).to_a
47
+ # 'This array is too large'.then(&log_warn) if arr.length > 128
48
+ def log_warn
49
+ ->(msg) { @@_sinlog_lambda_ext_logger_.warn(msg) }
50
+ end
51
+
52
+ # Warning Logging
53
+ #
54
+ # Same as `log_warn`
55
+ def log_warning
56
+ ->(msg) { @@_sinlog_lambda_ext_logger_.warn(msg) }
57
+ end
58
+
59
+ # Warning Logging
60
+ #
61
+ # Same as `log_warn`
62
+ def log_wng
63
+ ->(msg) { @@_sinlog_lambda_ext_logger_.warn(msg) }
64
+ end
65
+
66
+ # Error Logging
67
+ #
68
+ # Example:
69
+ #
70
+ # 'CLI error: You should pass the --url'.then(&log_err)
71
+ def log_err
72
+ ->(msg) { @@_sinlog_lambda_ext_logger_.error(msg) }
73
+ end
74
+
75
+ # Fatal error logging
76
+ #
77
+ # Example:
78
+ #
79
+ # 'Failed to open xxx'.then(&log_fatal)
80
+ def log_fatal
81
+ ->(msg) { @@_sinlog_lambda_ext_logger_.fatal(msg) }
82
+ end
83
+
84
+ # Example:
85
+ #
86
+ # 'Unknown'.then(&log_unk)
87
+ def log_unk
88
+ ->(msg) { @@_sinlog_lambda_ext_logger_.unknown(msg) }
89
+ end
90
+ end
91
+
92
+ # Similar to LogLambdaExt, but the lambda functions do not have the `log_` prefix.
93
+ # One important thing to note is that LambdaExt defines the `warning` function, not `warn`.
94
+ #
95
+ # The following methods define lambda expressions for different log levels.
96
+ # These lambdas can be used to log messages at the corresponding log level.
97
+ module LambdaExt
98
+ @@_sinlog_lambda_ext_logger_ = Sinlog.instance.logger
99
+
100
+ # Debug Logging
101
+ #
102
+ # Example:
103
+ #
104
+ # require 'sinlog'
105
+ # include Sinlog::LambdaExt
106
+ #
107
+ # 'Debug Message'.then(&dbg)
108
+ #
109
+ # cmd = %w[ls -l -h]
110
+ # "cmd_arr: #{cmd}".tap(&dbg)
111
+ def dbg
112
+ ->(msg) { @@_sinlog_lambda_ext_logger_.debug(msg) }
113
+ end
114
+
115
+ # Info(a.k.a. Information) Logging
116
+ #
117
+ # Example:
118
+ #
119
+ # require 'pathname'
120
+ #
121
+ # Pathname('lib/lambda.rb').tap {
122
+ # "Filename: #{it}".then(&info)
123
+ # "size: #{it.size}".then(&info)
124
+ # }
125
+ def info
126
+ ->(msg) { @@_sinlog_lambda_ext_logger_.info(msg) }
127
+ end
128
+
129
+ # Warning Logging
130
+ #
131
+ # Example:
132
+ #
133
+ # arr = (0..1024).to_a
134
+ # 'This array is too large'.then(&warning) if arr.length > 128
135
+ def warning
136
+ ->(msg) { @@_sinlog_lambda_ext_logger_.warn(msg) }
137
+ end
138
+
139
+ # Warning Logging
140
+ #
141
+ # Same as `warning`
142
+ def wng
143
+ ->(msg) { @@_sinlog_lambda_ext_logger_.warn(msg) }
144
+ end
145
+
146
+ # Error Logging
147
+ #
148
+ # Example:
149
+ #
150
+ # 'CLI error: You should pass the --url'.then(&err)
151
+ def err
152
+ ->(msg) { @@_sinlog_lambda_ext_logger_.error(msg) }
153
+ end
154
+
155
+ # Fatal error logging
156
+ #
157
+ # Example:
158
+ #
159
+ # 'Failed to open xxx'.then(&fatal)
160
+ def fatal
161
+ ->(msg) { @@_sinlog_lambda_ext_logger_.fatal(msg) }
162
+ end
163
+
164
+ # Unknown(log-level) logging
165
+ #
166
+ # Example:
167
+ #
168
+ # 'Unknown'.then(&unk)
169
+ def unk
170
+ ->(msg) { @@_sinlog_lambda_ext_logger_.unknown(msg) }
171
+ end
172
+ end
173
+
174
+ # Defines the `warn` lambda expression
175
+ module LambdaWarnExt
176
+ @@_sinlog_lambda_ext_logger_ = Sinlog.instance.logger
177
+ # Warning Logging
178
+ #
179
+ # Same as `LambdaExt.warning`
180
+ def warn
181
+ ->(msg) { @@_sinlog_lambda_ext_logger_.warn(msg) }
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Sinlog
4
+ VERSION = '0.0.1'
5
+ end
data/lib/sinlog.rb ADDED
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Usage:
4
+ #
5
+ # require 'sinlog'
6
+ #
7
+ # logger = Sinlog.instance.logger
8
+ # logger.info "Information"
9
+ # logger.debug "This is a debug message"
10
+ #
11
+ # Read more: https://github.com/2moe/sinlog-gem
12
+
13
+ require_relative 'sinlog/version'
14
+ require 'singleton'
15
+ require 'logger'
16
+
17
+ require_relative 'init'
18
+ require_relative 'lambda'
metadata ADDED
@@ -0,0 +1,45 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sinlog
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - 2moe
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2025-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: A very, very simple singleton logger with colored log levels.
13
+ executables: []
14
+ extensions: []
15
+ extra_rdoc_files: []
16
+ files:
17
+ - License
18
+ - docs/Readme-zh.md
19
+ - docs/Readme.md
20
+ - lib/init.rb
21
+ - lib/lambda.rb
22
+ - lib/sinlog.rb
23
+ - lib/sinlog/version.rb
24
+ homepage: https://github.com/2moe/sinlog-gem
25
+ licenses:
26
+ - MIT
27
+ metadata: {}
28
+ rdoc_options: []
29
+ require_paths:
30
+ - lib
31
+ required_ruby_version: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: 3.0.0
36
+ required_rubygems_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ requirements: []
42
+ rubygems_version: 3.6.2
43
+ specification_version: 4
44
+ summary: colorful singleton logger
45
+ test_files: []