shell_helpers 0.6.0 → 0.7.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 +4 -4
- data/ChangeLog.md +236 -2
- data/LICENSE.txt +1 -1
- data/Rakefile +7 -10
- data/gemspec.yml +1 -0
- data/lib/shell_helpers.rb +61 -23
- data/lib/shell_helpers/export.rb +4 -4
- data/lib/shell_helpers/logger.bak.rb +481 -0
- data/lib/shell_helpers/logger.rb +274 -56
- data/lib/shell_helpers/pathname.rb +16 -3
- data/lib/shell_helpers/run.rb +48 -19
- data/lib/shell_helpers/sh.rb +97 -30
- data/lib/shell_helpers/sysutils.rb +71 -8
- data/lib/shell_helpers/utils.rb +12 -8
- data/lib/shell_helpers/version.rb +1 -1
- data/test/test_logger.rb +48 -0
- metadata +20 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 42ce008456bb9f4bd37bea823f9d0886483c647e76ff265c571810c0e72963bf
|
4
|
+
data.tar.gz: cd0ee20f0fb67c4d48eb1571ae90679fc310ffc2cfc0b7b980f0beeae28e086c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7c50ff0378d2e1519a595eed19988e49be241c9d437e0d4338f7be8761eb468e3656162ebe78504d36decdc57dad3c52472d47c1ad6a330a98cb45d05ce4ded5
|
7
|
+
data.tar.gz: 6abc73549c5699ff2ae5355a475f1cffafcf0b584461764f6aa249c86df97e2f74602ce30fb9ffca3db865307917c56360caab44606c2b33775500ac51934ab8
|
data/ChangeLog.md
CHANGED
@@ -1,4 +1,238 @@
|
|
1
|
-
|
1
|
+
== Release v0.7.0 (2020-02-18) ==
|
2
2
|
|
3
|
-
*
|
3
|
+
* Copyright
|
4
|
+
* rsync: Use -zz instead of -z (--new-compress)
|
5
|
+
* Fixes for ruby 2.7
|
6
|
+
* rsync: add clobber option
|
7
|
+
* logger: fix logger.debug {"foo"}
|
8
|
+
* sysutils.rb: add exemples
|
9
|
+
* Misc bug fixes and doc
|
10
|
+
* sh.rb: correct examples
|
11
|
+
* Pathname.read!
|
12
|
+
* pathname: old_glob -> rel_glob
|
13
|
+
* pathname: deprecate glob, it is now a builtin
|
14
|
+
* logger: fix output showing twice
|
15
|
+
* Bug fix
|
16
|
+
* Fix tests
|
17
|
+
* Small tweaks
|
18
|
+
* logger2.rb is now logger.rb
|
19
|
+
* logger2: bug fixes
|
20
|
+
* logger2.rb: continue merge
|
21
|
+
* logger2: Merge MoreLogger and ColorLogger
|
22
|
+
* Rework MoreLogger
|
23
|
+
* SH: log_options (for OptParse)
|
24
|
+
* Fix tests
|
25
|
+
* logger.rb: new class ColorLogger
|
26
|
+
* logger: cli_info -> not bold
|
27
|
+
* Logger: cli_methods configuration
|
28
|
+
* logger: small tweaks
|
29
|
+
* tests for logger
|
30
|
+
* Logger: cli_*
|
31
|
+
* Update Rakefile
|
32
|
+
* Update Rakefile
|
33
|
+
* logger.rb: add bold
|
34
|
+
* logger: color_add
|
35
|
+
* severity: debug1=debug and verbose1=verbose
|
36
|
+
* logger: verbose* levels
|
37
|
+
* Logger#cli_level: helper to set up log level
|
38
|
+
* logger: debug1, debug2, debug3
|
39
|
+
* run.rb: bug fixes
|
40
|
+
* Bug fixes
|
41
|
+
* SH.log
|
42
|
+
* Sh: use instance_method for run_sudo_loop
|
43
|
+
* pathname: add missing require
|
44
|
+
* ssh: ssh_env option to pass env to the remote
|
45
|
+
* Run#run_success
|
46
|
+
* SH.sh: by default only return success
|
47
|
+
* Sh.sh: specify argv0
|
48
|
+
* Sh.sh: accept a block
|
49
|
+
* Sh: sudo_loop
|
50
|
+
* Sh#sh_or_proc
|
51
|
+
* Bug fixes
|
52
|
+
|
53
|
+
== Release v0.6.0 (2018-09-07) ==
|
54
|
+
|
55
|
+
* Bump version to 0.6.0
|
56
|
+
|
57
|
+
== Release v0.3.0 (2018-09-07) ==
|
58
|
+
|
59
|
+
* Bump version
|
60
|
+
* run+sh: unify command handling
|
61
|
+
* logger.rb: add quiet
|
62
|
+
* SH.sh: sudo can be a complex command
|
63
|
+
* Bug fixes + better logging for sh
|
64
|
+
* ShConfig + VirtualFile
|
65
|
+
* ShConfig: remove context
|
66
|
+
* ShConfig: wrap
|
67
|
+
* Sh: ShConfig
|
68
|
+
* Excpetions: subclass StandardError
|
69
|
+
* Run: bug fix
|
70
|
+
* Run.run: add :quiet alias for error: :quiet
|
71
|
+
* Run.run: replace status_mode by error_mode to be clearer
|
72
|
+
* run: accept environments, wrap into ProcessStatus if needed
|
73
|
+
* run.rb: run_simple now calls run
|
74
|
+
* sysutils: small improvements
|
75
|
+
* Sysutils: partition_infos + bug fixes
|
76
|
+
* Pathname#readbin
|
77
|
+
* SH.sh: detach vs spawn
|
78
|
+
* ssh: use URI::Ssh to be more lenient for host name
|
79
|
+
* Rsync exclude: escape
|
80
|
+
* Clean up
|
81
|
+
* Rsync: exclude
|
82
|
+
* utils: refactor
|
83
|
+
* rsync: add sshcommand option
|
84
|
+
* ssh: :sshkit and :net_ssh modes
|
85
|
+
* Pathname: add logging
|
86
|
+
* SH.sh: allow mode to be :exec too
|
87
|
+
* Utils.ssh: more mode, and split ssh_command
|
88
|
+
* Utils.ssh: user
|
89
|
+
* Utils.ssh:
|
90
|
+
* sysutils: bug fix
|
91
|
+
* Pathname#glob: expand to pathname
|
92
|
+
* Pathname#rel_path_to: convert the target to a Pathname
|
93
|
+
* sysutils: allows mountoptions to be a String
|
94
|
+
* Remove LogHelper
|
95
|
+
* sh.rb: change execute log level to be :info
|
96
|
+
* Utils: eval_shell
|
97
|
+
* Bug fix
|
98
|
+
* Bug fix
|
99
|
+
* make_dir_or_subvolume
|
100
|
+
* sysutils: stat_files
|
101
|
+
* sysutils: bug fixes
|
102
|
+
* sysutils: check devices for name too
|
103
|
+
* sysutils: block handling to automatically unmount
|
104
|
+
* sysutils: use Pathname#<=>
|
105
|
+
* run/sh: add a 'sudo' option
|
106
|
+
* sysutils: improve find_devices
|
107
|
+
* sysutils: fs_infos return a hash rather than an array
|
108
|
+
* sysutils: unify keywords for blkid, lsblk, findmnt
|
109
|
+
* sysutils: lsblk and findmnt
|
110
|
+
* sysutils: partition types
|
111
|
+
* sysutils: commands for sysadmins
|
112
|
+
* pathname: chown
|
113
|
+
* rsync: more convenient options passing
|
114
|
+
* Copyright
|
115
|
+
* rsync: correct a typo
|
116
|
+
* Wrap chdir
|
117
|
+
* SH::Sh: Add DryRun module
|
118
|
+
* Pathname: misc utils functions
|
119
|
+
* Sh.sh: correctly handle false options
|
120
|
+
* docs typos
|
121
|
+
* Sh: activates logging by default
|
122
|
+
* sh_commands: chomp newline
|
123
|
+
|
124
|
+
== Release v0.2.0 (2018-02-01) ==
|
125
|
+
|
126
|
+
* Bump versions
|
127
|
+
* rsync: add chown option
|
128
|
+
* Pathname#{text?,binary?}: return false on a directory
|
129
|
+
* run.rb: improve api
|
130
|
+
* Import changes from methadone
|
131
|
+
* binary?: Encoding to ascii would lose to many bits
|
132
|
+
* Pathname#text? Don't invoke 'file'
|
133
|
+
* Pathname#text?, Pathname#glob
|
134
|
+
* run_lazy: simplify the implementation
|
135
|
+
* run: add run_enum
|
136
|
+
* run_pager: can pass arguments to the pager
|
137
|
+
* Be explicit about require
|
138
|
+
* Add missing options lib
|
139
|
+
* Fix require
|
140
|
+
* Split utils.rb into utils.rb and export.rb
|
141
|
+
* Pathname#hidden? was defined twice
|
142
|
+
* Add badges
|
143
|
+
* In travis the test should be under 'bundle exec'
|
144
|
+
* We need the git version of drain for now
|
145
|
+
* Update gemspec
|
146
|
+
* Add rake dependency
|
147
|
+
* Configure travis and streamline rake and test files
|
148
|
+
* rsync: using relative we usually want no-implied-dirs
|
149
|
+
* utils: more rsync options
|
150
|
+
* import_parse: inline mode
|
151
|
+
* utils: add capture_stdout
|
152
|
+
* shrun: helper to run system/spawn with the correct options
|
153
|
+
* sh: Pass along options
|
154
|
+
* logger: progname should affect the error logger too
|
155
|
+
* SH.sh: allow to detach
|
156
|
+
* bugfix
|
157
|
+
* Unquote value
|
158
|
+
* utils.rb: put import_parse in the correct Module
|
159
|
+
* import_variable: match variable name on \S*
|
160
|
+
* test import_parse
|
161
|
+
* Import variables
|
162
|
+
* Copyright
|
163
|
+
* export_value: test if the object respond to :to_a or :to_h
|
164
|
+
* test: test_utils.rb (only ShellExport for now)
|
165
|
+
* Fixes for ruby 2.4
|
166
|
+
* find: handle max_depth
|
167
|
+
* utils: when exporting a group/name variable replace '/' with '_'
|
168
|
+
* Copyright
|
169
|
+
* Add examples
|
170
|
+
* Pathname.cd
|
171
|
+
* find_files
|
172
|
+
* find_file
|
173
|
+
* RunSimple: add error: nil
|
174
|
+
* Add pathname#split_all
|
175
|
+
* Pathname: add may_exist?
|
176
|
+
* Pathname: add chattr
|
177
|
+
* More documentation on SH.find
|
178
|
+
* FileUtils#chmod requires the mode as first parameter
|
179
|
+
* rsync: the option is --suffix, not --backup-suffix
|
180
|
+
* rsync: Add expected: 23
|
181
|
+
* rsync: add option to clean output directory
|
182
|
+
* rsync: add :delete option
|
183
|
+
* Add rsync helper
|
184
|
+
* Rename Sh#commands to Sh#sh_commands
|
185
|
+
* Add Sh.commands
|
186
|
+
* Add Pathname#copy_entry
|
187
|
+
* rmtree should be an alias to rm_rf
|
188
|
+
* Add backup mode to filewrite, and mkpath mode ton on_*
|
189
|
+
* Add rsync helper
|
190
|
+
* Pathname#squel_dir: allow to pass mkpath
|
191
|
+
* SH.find now accept a :prune options
|
192
|
+
* We should check if CLILogging has @@logger, not Module
|
193
|
+
* Move options.rb to another library
|
194
|
+
* mv_and_ln.rb: dereference
|
195
|
+
* mv_and_ln: options tweaks
|
196
|
+
* Add mv_and_ln.rb
|
197
|
+
* Pathname: use convert_path in rel_path_to
|
198
|
+
* Handle relative_path_from errors
|
199
|
+
* Add new mode to not dereference symlink in some actions
|
200
|
+
* abs_to_rel.rb
|
201
|
+
* A binary using the features of pathname
|
202
|
+
* Inner scope
|
203
|
+
* Add helper functions
|
204
|
+
* find: add a depth first option
|
205
|
+
* find: rename depth to max_depth
|
206
|
+
* Pathname#squel_dir: we need to use squel inside the find
|
207
|
+
* Bugfixes in Pathname and ShUtils.find
|
208
|
+
* Pathname#squel_dir
|
209
|
+
* Start working on Pathname#squel
|
210
|
+
* Pathname: add rel_path_from
|
211
|
+
* Use included hooks
|
212
|
+
* Put pathname functionality into Modules and Classes
|
213
|
+
* Correct constant reference
|
214
|
+
* Pathname: fix aliases
|
215
|
+
* SH::Pathname: put changes in module so that other class can use them
|
216
|
+
* Merge branch 'master-imb'
|
217
|
+
* Small bug fixes
|
218
|
+
* SH.find: any filter should prevent the yield
|
219
|
+
* Pathname: factorize the new name usual method
|
220
|
+
* Pathname: new_name
|
221
|
+
* Implement SH::Pathname#follow
|
222
|
+
* Add Pathname#rel_path
|
223
|
+
* Readme: Add warning
|
224
|
+
* pathname.rb: raise some Errors if needed
|
225
|
+
* Implement a 'magic' rm function
|
226
|
+
* Pathname: use FileUtils when possible
|
227
|
+
* Add a module LogHelper to help setup logging
|
228
|
+
* Add a fallback if SimpleColor is not found
|
229
|
+
* Some files/names have changed
|
230
|
+
* Rename SH to ShellHelpers
|
231
|
+
|
232
|
+
== Release v0.1.0 (2015-02-24) ==
|
233
|
+
|
234
|
+
* Add dependency
|
235
|
+
* Description
|
236
|
+
* Add library
|
237
|
+
* Initial commit.
|
4
238
|
|
data/LICENSE.txt
CHANGED
data/Rakefile
CHANGED
@@ -1,15 +1,5 @@
|
|
1
1
|
require 'rake'
|
2
2
|
|
3
|
-
begin
|
4
|
-
require 'rubygems/tasks'
|
5
|
-
Gem::Tasks.new(sign: {checksum: true, pgp: true},
|
6
|
-
scm: {status: true}) do |tasks|
|
7
|
-
tasks.console.command = 'pry'
|
8
|
-
end
|
9
|
-
rescue LoadError => e
|
10
|
-
warn e.message
|
11
|
-
end
|
12
|
-
|
13
3
|
require 'rake/testtask'
|
14
4
|
Rake::TestTask.new do |test|
|
15
5
|
test.libs << 'test'
|
@@ -27,3 +17,10 @@ rescue LoadError => e
|
|
27
17
|
end
|
28
18
|
task :doc => :yard
|
29
19
|
|
20
|
+
begin
|
21
|
+
require 'dr/rake_gems'
|
22
|
+
Gem::MyTasks.new
|
23
|
+
rescue LoadError => e
|
24
|
+
warn e.message
|
25
|
+
end
|
26
|
+
|
data/gemspec.yml
CHANGED
data/lib/shell_helpers.rb
CHANGED
@@ -23,37 +23,75 @@ module ShellHelpers
|
|
23
23
|
include Export #export
|
24
24
|
include Utils #find, run_pager, rsync...
|
25
25
|
include SysUtils #mount, find_devices...
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
26
|
+
|
27
|
+
module LogHelpers
|
28
|
+
#activates debug mode
|
29
|
+
def debug(level=true)
|
30
|
+
#activates logging on Pathname
|
31
|
+
Pathname.send(:include, CLILogging)
|
32
|
+
logger.cli_level(level, active: Logger::DEBUG)
|
33
|
+
end
|
34
|
+
|
35
|
+
def log(*args, **kw)
|
36
|
+
logger.add(*args, **kw)
|
37
|
+
end
|
38
|
+
|
39
|
+
# add standard log options to an OptParse instance
|
40
|
+
def log_options(opt, recipient)
|
41
|
+
opt.on("--[no-]color", "Colorize output", "Default to #{recipient[:color]}") do |v|
|
42
|
+
recipient[:color]=v
|
43
|
+
end
|
44
|
+
|
45
|
+
opt.on("--debug", "=[level]", "Activate debug informations", "Use `--debug=pry` to launch the pry debugger", "Default to #{recipient[:debug]}") do |v|
|
46
|
+
recipient[:debug]=v
|
47
|
+
end
|
48
|
+
|
49
|
+
opt.on("--log", "=[level]", "Set log level", "Default to #{recipient[:loglevel]}.") do |v|
|
50
|
+
recipient[:loglevel]=v
|
51
|
+
end
|
52
|
+
|
53
|
+
opt.on("--[no-]verbose", "-v", "Verbose mode", "Similar to --log=verbose") do |v|
|
54
|
+
recipient[:loglevel]=:verbose if v
|
55
|
+
end
|
56
|
+
|
57
|
+
opt.on("--vv", "Verbose mode 2", "Similar to --log=verbose2") do |v|
|
58
|
+
recipient[:loglevel]=:verbose2 if v
|
59
|
+
end
|
60
|
+
|
61
|
+
opt.on("--vvv", "Verbose mode 3", "Similar to --log=verbose3") do |v|
|
62
|
+
recipient[:loglevel]=:verbose3 if v
|
63
|
+
end
|
64
|
+
|
65
|
+
opt.on("--[no-]quiet", "-q", "Quiet mode", "Similar to --log=warn") do |v|
|
66
|
+
recipient[:loglevel]=:warn if v
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def process_log_options(recipient)
|
71
|
+
SimpleColor.enabled=recipient[:color] if recipient.key?(:color)
|
72
|
+
SH.logger.cli_level(recipient[:loglevel]) if recipient.key?(:loglevel)
|
73
|
+
if recipient.key?(:debug)
|
74
|
+
debug=recipient[:debug]
|
75
|
+
if debug=="pry"
|
76
|
+
puts "# Launching pry"
|
77
|
+
require 'pry'; binding.pry
|
78
|
+
elsif debug
|
79
|
+
SH.debug(debug)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
32
83
|
end
|
84
|
+
include LogHelpers
|
85
|
+
|
86
|
+
extend self
|
87
|
+
|
33
88
|
#include SH::FU to add FileUtils
|
34
89
|
module FU
|
35
90
|
include ::FileUtils
|
36
91
|
include ::ShellHelpers
|
37
92
|
extend self
|
38
93
|
end
|
39
|
-
|
40
|
-
# #include LogHelper to set up CLILogging with some convenience facilities
|
41
|
-
# module LogHelper
|
42
|
-
# include CLILogging
|
43
|
-
# CLILogging.logger.progname||=$0
|
44
|
-
# # #Activates Sh.sh in klass
|
45
|
-
# # def self.included(klass)
|
46
|
-
# # klass.const_set(:Sh,ShellHelpers::Sh)
|
47
|
-
# # end
|
48
|
-
# end
|
49
94
|
end
|
50
95
|
|
51
96
|
#for the lazy
|
52
97
|
SH=ShellHelpers
|
53
|
-
|
54
|
-
## # SHLog.sh to get logging
|
55
|
-
## module SHLog
|
56
|
-
## include ShellHelpers
|
57
|
-
## include ShellHelpers::ShLog
|
58
|
-
## end
|
59
|
-
|
data/lib/shell_helpers/export.rb
CHANGED
@@ -82,13 +82,13 @@ module ShellHelpers
|
|
82
82
|
#from {ploum: plim} return something like
|
83
83
|
#PLOUM=plim
|
84
84
|
#that can be evaluated by the shell
|
85
|
-
def export_variables(hash, local: false, export: false, prefix:"",upcase:true)
|
85
|
+
def export_variables(hash, inline: false, local: false, export: false, prefix:"",upcase:true)
|
86
86
|
names=hash.keys.map {|s| escape_name(s,prefix:prefix,upcase:upcase)}
|
87
87
|
r=""
|
88
88
|
r+="local #{names.join(" ")}\n" if local
|
89
|
-
hash.
|
90
|
-
|
91
|
-
end
|
89
|
+
r+=hash.map do |k,v|
|
90
|
+
export_variable(k,v,prefix:prefix,upcase:upcase).chomp
|
91
|
+
end.join(inline ? " " : "\n") + (inline ? "" : "\n")
|
92
92
|
r+="export #{names.join(" ")}\n" if export
|
93
93
|
return r
|
94
94
|
end
|
@@ -0,0 +1,481 @@
|
|
1
|
+
# vim: foldmethod=marker
|
2
|
+
#From methadone (cli_logger.rb, cli_logging.rb, last import: 4626a2bca9b6e54077a06a0f8e11a04fadc6e7ae; 2017-01-19)
|
3
|
+
require 'logger'
|
4
|
+
require 'simplecolor'
|
5
|
+
|
6
|
+
module ShellHelpers
|
7
|
+
# like Logger but with more levels
|
8
|
+
class MoreLogger < Logger #{{{1
|
9
|
+
class Formatter < Logger::Formatter
|
10
|
+
def self.format_severity(severity)
|
11
|
+
Logger::SEV_LABEL[severity] || 'ANY'
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(severity, time, progname, msg)
|
15
|
+
severity=self.class.format_severity(severity)
|
16
|
+
Format % [severity[0..0], format_datetime(time), $$, severity, progname,
|
17
|
+
msg2str(msg)]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
WrongLevel=Class.new(StandardError)
|
22
|
+
|
23
|
+
module Levels
|
24
|
+
#note Logger::Severity is included into Logger, so we can access the severity levels directly
|
25
|
+
DEBUG1=0 #=DEBUG
|
26
|
+
DEBUG2=-0.1
|
27
|
+
DEBUG3=-0.2
|
28
|
+
IMPORTANT=1.5 #between warning and info
|
29
|
+
SUCCESS=1.3 #between warning and info
|
30
|
+
VERBOSE=0.9
|
31
|
+
VERBOSE1=0.9
|
32
|
+
VERBOSE2=0.8
|
33
|
+
VERBOSE3=0.7
|
34
|
+
QUIET=-9
|
35
|
+
DEBUG = Logger::DEBUG # 0
|
36
|
+
INFO = Logger::INFO # 1
|
37
|
+
WARN = Logger::WARN # 2
|
38
|
+
ERROR = Logger::ERROR # 3
|
39
|
+
FATAL = Logger::FATAL # 4
|
40
|
+
UNKNOWN = Logger::UNKNOWN # 5
|
41
|
+
end
|
42
|
+
|
43
|
+
LOG_LEVELS=
|
44
|
+
{
|
45
|
+
'quiet' => Levels::QUIET,
|
46
|
+
'debug3' => Levels::DEBUG3,
|
47
|
+
'debug2' => Levels::DEBUG2,
|
48
|
+
'debug1' => Levels::DEBUG1,
|
49
|
+
'debug' => Levels::DEBUG, #0
|
50
|
+
'verbose' => Levels::VERBOSE,
|
51
|
+
'verbose1' => Levels::VERBOSE1,
|
52
|
+
'verbose2' => Levels::VERBOSE2,
|
53
|
+
'verbose3' => Levels::VERBOSE3,
|
54
|
+
'info' => Levels::INFO, #1
|
55
|
+
'success' => Levels::SUCCESS,
|
56
|
+
'important' => Levels::IMPORTANT,
|
57
|
+
'warn' => Levels::WARN, #2
|
58
|
+
'error' => Levels::ERROR, #3
|
59
|
+
'fatal' => Levels::FATAL, #4
|
60
|
+
'unknown' => Levels::UNKNOWN, #5
|
61
|
+
}
|
62
|
+
|
63
|
+
def log_levels
|
64
|
+
@levels ||= LOG_LEVELS.dup
|
65
|
+
@levels
|
66
|
+
end
|
67
|
+
|
68
|
+
attr_accessor :default, :active, :quiet
|
69
|
+
|
70
|
+
def initialize(*args, levels: {}, default: :info, active: :verbose, quiet: :warn, **kwds)
|
71
|
+
@default=default
|
72
|
+
@active=active
|
73
|
+
@quiet=quiet
|
74
|
+
super(*args, **kwds)
|
75
|
+
@default_formatter = Formatter.new
|
76
|
+
@level=severity_lvl(@default)
|
77
|
+
klass=self.singleton_class
|
78
|
+
levels=log_levels.merge!(levels)
|
79
|
+
levels.keys.each do |lvl, cst|
|
80
|
+
klass.define_method(lvl.to_sym) do |progname=nil, **opts, &block|
|
81
|
+
add(lvl.to_sym, nil, progname, **opts, &block)
|
82
|
+
end
|
83
|
+
klass.define_method("#{lvl}?".to_sym) do
|
84
|
+
@level <= cst
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# log with given security. Also accepts 'true'
|
90
|
+
def add(severity, message = nil, progname = nil, default: @default, quiet: @quiet, callback: nil)
|
91
|
+
severity=severity(severity, default: default, quiet: quiet)
|
92
|
+
severity_lvl=severity_lvl(severity)
|
93
|
+
if @logdev.nil? or severity_lvl < @level
|
94
|
+
return true
|
95
|
+
end
|
96
|
+
if progname.nil?
|
97
|
+
progname = @progname
|
98
|
+
end
|
99
|
+
if message.nil?
|
100
|
+
if block_given?
|
101
|
+
message = yield
|
102
|
+
else
|
103
|
+
message = progname
|
104
|
+
progname = @progname
|
105
|
+
end
|
106
|
+
end
|
107
|
+
callback.call(message, progname, severity) if callback
|
108
|
+
@logdev.write(
|
109
|
+
format_message(format_severity(severity), Time.now, progname, message))
|
110
|
+
true
|
111
|
+
end
|
112
|
+
|
113
|
+
def severity(severity, default: @default, quiet: @quiet)
|
114
|
+
severity ||= UNKNOWN
|
115
|
+
severity=default if severity == true
|
116
|
+
severity=quiet if severity == false
|
117
|
+
severity
|
118
|
+
end
|
119
|
+
|
120
|
+
def severity_lvl(severity, **opts)
|
121
|
+
severity=severity(severity, **opts)
|
122
|
+
if severity.is_a?(Numeric)
|
123
|
+
return severity
|
124
|
+
else
|
125
|
+
sev=severity.to_s.downcase
|
126
|
+
if log_levels.key?(sev)
|
127
|
+
return log_levels[sev]
|
128
|
+
else
|
129
|
+
raise WrongLevel.new(severity)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def level=(severity)
|
135
|
+
@level = severity_lvl(severity)
|
136
|
+
end
|
137
|
+
|
138
|
+
# like level= but for clis, so we can pass a default if level=true
|
139
|
+
def cli_level(level, active: @active, quiet: @quiet)
|
140
|
+
level=active if level==true #for cli
|
141
|
+
level=quiet if level==false #for cli
|
142
|
+
self.level=level
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
class ColorLogger < MoreLogger #{{{1
|
147
|
+
CLI_COLORS_BASE={
|
148
|
+
# info: [:bold],
|
149
|
+
success: [:green, :bold],
|
150
|
+
important: [:blue, :bold],
|
151
|
+
warn: [:yellow, :bold],
|
152
|
+
error: [:red, :bold],
|
153
|
+
fatal: [:red, :bold]
|
154
|
+
}
|
155
|
+
|
156
|
+
CLI_COLORS={
|
157
|
+
mark: {lvl: :info, colors: :bold}
|
158
|
+
}
|
159
|
+
|
160
|
+
def cli_colors
|
161
|
+
return @cli_colors if defined?(@cli_colors)
|
162
|
+
@cli_colors={}
|
163
|
+
base_colors=CLI_COLORS_BASE
|
164
|
+
base_colors.each do |k,v|
|
165
|
+
r={colors: v}
|
166
|
+
@cli_colors[k.to_sym]=r
|
167
|
+
end
|
168
|
+
@cli_colors.merge!(CLI_COLORS)
|
169
|
+
@cli_colors
|
170
|
+
#mode => {lvl: lvl, colors: colors }
|
171
|
+
end
|
172
|
+
|
173
|
+
def add(severity, message = nil, progname = nil, color: [], raw: @raw, **args)
|
174
|
+
severity ||= UNKNOWN
|
175
|
+
severity=severity(severity)
|
176
|
+
color=[*color]
|
177
|
+
unless severity.is_a?(Numeric)
|
178
|
+
cli=cli_colors[severity.to_sym]
|
179
|
+
if cli
|
180
|
+
severity=cli[:lvl] if cli.key?(:lvl)
|
181
|
+
if !raw
|
182
|
+
color=[*cli[:colors]] + color
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
severity_lvl=severity_lvl(severity)
|
187
|
+
|
188
|
+
if @logdev.nil? or severity_lvl < @level
|
189
|
+
return true
|
190
|
+
end
|
191
|
+
if progname.nil?
|
192
|
+
progname = @progname
|
193
|
+
end
|
194
|
+
if message.nil?
|
195
|
+
if block_given?
|
196
|
+
message = yield
|
197
|
+
else
|
198
|
+
message = progname
|
199
|
+
progname = @progname
|
200
|
+
end
|
201
|
+
end
|
202
|
+
message = SimpleColor.color(message.to_s, *color)
|
203
|
+
super(severity_lvl, message, progname)
|
204
|
+
end
|
205
|
+
|
206
|
+
attr_accessor :raw
|
207
|
+
|
208
|
+
def initialize(*args, cli: {}, **kwds)
|
209
|
+
@raw=false
|
210
|
+
super(*args, **kwds)
|
211
|
+
klass=self.singleton_class
|
212
|
+
cli=cli_colors.merge!(cli)
|
213
|
+
(cli.keys - CLI_COLORS_BASE.keys).each do |lvl|
|
214
|
+
klass.define_method(lvl.to_sym) do |progname=nil, **opts, &block|
|
215
|
+
add(lvl, nil, progname, **opts, &block)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
yield self if block_given?
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
# CLILogger {{{1
|
223
|
+
# A Logger instance that gives better control of messaging the user and
|
224
|
+
# logging app activity. At it's most basic, you would use <tt>info</tt>
|
225
|
+
# as a replacement for +puts+ and <tt>error</tt> as a replacement for
|
226
|
+
# <tt>STDERR.puts</tt>. Since this is a logger, however, you can also
|
227
|
+
# use #debug, #warn, and #fatal, and you can control the format and
|
228
|
+
# "logging level" as such.
|
229
|
+
#
|
230
|
+
# So, by default:
|
231
|
+
# * debug messages do not appear anywhere
|
232
|
+
# * info messages appear on the standard output
|
233
|
+
# * warn, error, and fatal message appear on the standard error
|
234
|
+
# * The default format of messages is simply the message, no logging
|
235
|
+
# cruft, however if your output is redirected to a file, a better
|
236
|
+
# timestamped logging format is used
|
237
|
+
#
|
238
|
+
# You can customize this in several ways:
|
239
|
+
# * You can override the devices used by passing different devices to the constructor
|
240
|
+
# * You can adjust the level of message that goes to the error logger via error_level=
|
241
|
+
# * You can adjust the format for messages to the error logger separately via error_formatter=
|
242
|
+
#
|
243
|
+
# === Example
|
244
|
+
#
|
245
|
+
# logger = CLILogger.new
|
246
|
+
# logger.debug("Starting up") # => only the standard output gets this
|
247
|
+
# logger.warn("careful!") # => only the standard error gets this
|
248
|
+
# logger.error("Something went wrong!") # => only the standard error gets this
|
249
|
+
#
|
250
|
+
# logger = CLILogger.new
|
251
|
+
# logger.error_level = Logger::ERROR
|
252
|
+
# logger.debug("Starting up") # => only the standard output gets this
|
253
|
+
# logger.warn("careful!") # => only the standard OUTPUT gets this
|
254
|
+
# logger.error("Something went wrong!") # => only the standard error gets this
|
255
|
+
#
|
256
|
+
# logger = CLILogger.new('logfile.txt')
|
257
|
+
# logger.debug("Starting up") #=> logfile.txt gets this
|
258
|
+
# logger.error("Something went wrong!") # => BOTH logfile.txt AND the standard error get this
|
259
|
+
class CLILogger < ColorLogger
|
260
|
+
BLANK_FORMAT = lambda { |severity,datetime,progname,msg|
|
261
|
+
msg + "\n"
|
262
|
+
}
|
263
|
+
|
264
|
+
# Helper to proxy methods to the super class AND to the internal error logger
|
265
|
+
# +symbol+:: Symbol for name of the method to proxy
|
266
|
+
def self.proxy_method(symbol) #:nodoc:
|
267
|
+
old_name = "old_#{symbol}".to_sym
|
268
|
+
alias_method old_name,symbol
|
269
|
+
define_method symbol do |*args,&block|
|
270
|
+
send(old_name,*args,&block)
|
271
|
+
@stderr_logger.send(symbol,*args,&block)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
proxy_method :'formatter='
|
276
|
+
proxy_method :'progname='
|
277
|
+
proxy_method :'datetime_format='
|
278
|
+
|
279
|
+
def add(severity, message = nil, progname = nil, **opts, &block) #:nodoc:
|
280
|
+
severity_lvl = severity_lvl(severity)
|
281
|
+
if @split_logs
|
282
|
+
unless severity_lvl >= @stderr_logger.level
|
283
|
+
super(severity,message,progname, **opts, &block)
|
284
|
+
end
|
285
|
+
else
|
286
|
+
super(severity,message,progname,**opts, &block)
|
287
|
+
end
|
288
|
+
@stderr_logger.add(severity,message,progname,**opts, &block)
|
289
|
+
end
|
290
|
+
|
291
|
+
DEFAULT_ERROR_LEVEL = Logger::Severity::WARN
|
292
|
+
|
293
|
+
# A logger that logs error-type messages to a second device; useful for
|
294
|
+
# ensuring that error messages go to standard error. This should be
|
295
|
+
# pretty smart about doing the right thing. If both log devices are
|
296
|
+
# ttys, e.g. one is going to standard error and the other to the
|
297
|
+
# standard output, messages only appear once in the overall output
|
298
|
+
# stream. In other words, an ERROR logged will show up *only* in the
|
299
|
+
# standard error. If either log device is NOT a tty, then all messages
|
300
|
+
# go to +log_device+ and only errors go to +error_device+
|
301
|
+
#
|
302
|
+
# +log_device+:: device where all log messages should go, based on level
|
303
|
+
# By default, this is Logger::Severity::WARN
|
304
|
+
# +error_device+:: device where all error messages should go.
|
305
|
+
def initialize(log_device=$stdout,error_device=$stderr,
|
306
|
+
split_log: :auto, default_error: DEFAULT_ERROR_LEVEL, **kwds)
|
307
|
+
@stderr_logger = MoreLogger.new(error_device, default: default_error, **kwds)
|
308
|
+
|
309
|
+
super(log_device, **kwds)
|
310
|
+
|
311
|
+
log_device_tty = tty?(log_device)
|
312
|
+
error_device_tty = tty?(error_device)
|
313
|
+
|
314
|
+
@split_logs = log_device_tty && error_device_tty if split_log==:auto
|
315
|
+
|
316
|
+
self.level = Logger::Severity::INFO
|
317
|
+
@stderr_logger.level = @stderr_logger.default
|
318
|
+
|
319
|
+
self.formatter = BLANK_FORMAT if log_device_tty
|
320
|
+
@stderr_logger.formatter = BLANK_FORMAT if error_device_tty
|
321
|
+
yield self, @stderr_logger if block_given?
|
322
|
+
end
|
323
|
+
|
324
|
+
def level=(level)
|
325
|
+
super
|
326
|
+
#current_error_level = @stderr_logger.level
|
327
|
+
if (self.level > @stderr_logger.default) && @split_logs
|
328
|
+
@stderr_logger.level = self.level
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
def cli_level(*args)
|
333
|
+
super
|
334
|
+
if (self.level > @stderr_logger.default) && @split_logs
|
335
|
+
@stderr_logger.level = self.level
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
# Set the threshold for what messages go to the error device. Note
|
340
|
+
# that calling #level= will *not* affect the error logger *unless* both
|
341
|
+
# devices are TTYs.
|
342
|
+
# +level+:: a constant from Logger::Severity for the level of messages that should go to the error logger
|
343
|
+
def error_level=(level)
|
344
|
+
@stderr_logger.level = level
|
345
|
+
end
|
346
|
+
|
347
|
+
# Overrides the formatter for the error logger. A future call to
|
348
|
+
# #formatter= will affect both, so the order of the calls matters.
|
349
|
+
# +formatter+:: Proc that handles the formatting, the same as for #formatter=
|
350
|
+
def error_formatter=(formatter)
|
351
|
+
@stderr_logger.formatter=formatter
|
352
|
+
end
|
353
|
+
|
354
|
+
private def tty?(device_or_string)
|
355
|
+
return device_or_string.tty? if device_or_string.respond_to? :tty?
|
356
|
+
false
|
357
|
+
end
|
358
|
+
|
359
|
+
#log the action and execute it
|
360
|
+
#Severity is Logger:: DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN
|
361
|
+
def log_and_do(*args, severity: INFO, definee: self, **opts, &block)
|
362
|
+
msg="log_and_do #{args} on #{self}"
|
363
|
+
msg+=" with options #{opts}" unless opts.empty?
|
364
|
+
msg+=" with block #{block}" if block
|
365
|
+
add(severity,msg)
|
366
|
+
if opts.empty?
|
367
|
+
definee.send(*args, &block)
|
368
|
+
else
|
369
|
+
definee.send(*args, **opts, &block)
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
private def toggle_log_level(toggle='debug')
|
374
|
+
@log_level_original = self.level unless @log_level_toggled
|
375
|
+
logger.level = if @log_level_toggled
|
376
|
+
@log_level_original
|
377
|
+
else
|
378
|
+
log_levels.fetch(toggle)
|
379
|
+
end
|
380
|
+
@log_level_toggled = !@log_level_toggled
|
381
|
+
@log_level = logger.level
|
382
|
+
end
|
383
|
+
|
384
|
+
#call logger.setup_toggle_trap('USR1') to change the log level to
|
385
|
+
#:debug when USR1 is received
|
386
|
+
def setup_toggle_trap(signal)
|
387
|
+
if signal
|
388
|
+
Signal.trap(signal) do
|
389
|
+
toggle_log_level
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
end
|
395
|
+
|
396
|
+
# CLILogging {{{1
|
397
|
+
# Provides easier access to a shared DR::CLILogger instance.
|
398
|
+
# Include this module into your class, and #logger provides access to a
|
399
|
+
# shared logger. This is handy if you want all of your clases to have
|
400
|
+
# access to the same logger, but don't want to (or aren't able to) pass
|
401
|
+
# it around to each class.
|
402
|
+
# This also provides methods for direct logging without going through the
|
403
|
+
# #logger
|
404
|
+
#
|
405
|
+
# === Example
|
406
|
+
#
|
407
|
+
# class MyClass
|
408
|
+
# include DR::CLILogging
|
409
|
+
#
|
410
|
+
# def doit
|
411
|
+
# debug("About to doit!")
|
412
|
+
# if results
|
413
|
+
# info("We did it!")
|
414
|
+
# else
|
415
|
+
# error("Something went wrong")
|
416
|
+
# end
|
417
|
+
# debug("Done doing it")
|
418
|
+
# end
|
419
|
+
# end
|
420
|
+
#
|
421
|
+
# Note that every class that mixes this in shares the *same logger
|
422
|
+
# instance*, so if you call #change_logger, this will change the logger
|
423
|
+
# for all classes that mix this in. This is likely what you want.
|
424
|
+
module CLILogging
|
425
|
+
extend self
|
426
|
+
|
427
|
+
# Access the shared logger. All classes that include this module
|
428
|
+
# will get the same logger via this method.
|
429
|
+
def logger
|
430
|
+
unless CLILogging.class_variable_defined?(:@@logger)
|
431
|
+
@@logger = CLILogger.new
|
432
|
+
@@logger.progname=$0
|
433
|
+
end
|
434
|
+
@@logger
|
435
|
+
end
|
436
|
+
|
437
|
+
self.logger.progname||=$0
|
438
|
+
|
439
|
+
# Change the global logger that includers will use. Useful if you
|
440
|
+
# don't want the default configured logger. Note that the
|
441
|
+
# +change_logger+ version is preferred because Ruby will often parse
|
442
|
+
# <tt>logger = Logger.new</tt> as the declaration of, and assignment
|
443
|
+
# to, of a local variable. You'd need to do
|
444
|
+
# <tt>self.logger=Logger.new</tt> to be sure. This method is a bit
|
445
|
+
# easier.
|
446
|
+
#
|
447
|
+
# +new_logger+:: the new logger. May not be nil and should be a logger of some kind
|
448
|
+
def change_logger(new_logger)
|
449
|
+
raise ArgumentError,"Logger may not be nil" if new_logger.nil?
|
450
|
+
@@logger = new_logger
|
451
|
+
@@logger.level = @log_level if defined?(@log_level) && @log_level
|
452
|
+
end
|
453
|
+
|
454
|
+
alias logger= change_logger
|
455
|
+
|
456
|
+
#call CLILogging.setup_toggle_trap('USR1') to change the log level to
|
457
|
+
#:debug when USR1 is received
|
458
|
+
def self.setup_toggle_trap(signal)
|
459
|
+
logger.setup_toggle_trap(signal)
|
460
|
+
end
|
461
|
+
|
462
|
+
def log_and_do(*args)
|
463
|
+
logger.log_and_do(*args)
|
464
|
+
end
|
465
|
+
|
466
|
+
LOG_LEVELS=logger.log_levels
|
467
|
+
|
468
|
+
#Include this in place of CLILogging if you prefer to use
|
469
|
+
#info directly rather than logger.info
|
470
|
+
module Shortcuts #{{{
|
471
|
+
extend self
|
472
|
+
include CLILogging
|
473
|
+
LOG_LEVELS.each do |lvl, _cst|
|
474
|
+
define_method(lvl.to_sym) do |progname=nil, &block|
|
475
|
+
logger.send(lvl.to_sym, progname, &block)
|
476
|
+
end
|
477
|
+
end
|
478
|
+
end
|
479
|
+
#}}}
|
480
|
+
end #}}}
|
481
|
+
end
|