shell_helpers 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|