kscript 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: aa60e97dd2e5ebd0282945488335f81226666cc547f74608eb5b6d04370eec69
4
+ data.tar.gz: 628577d54846ee750cf65e648bb13bce7c286182b49ef52813edb551b182a6f2
5
+ SHA512:
6
+ metadata.gz: 79c677939e03109beac01c935773433218d95b663f7a4fea56980d3e6dbfc652eaadec7ed37ce1da38e77089cd6ff7524631e46a7dda71744e3868d8badf1734
7
+ data.tar.gz: f29744a4e376a0476f91ee3a5788cd713f7a5c333114a3e4ef3616f5b3cb45783cceaca02b5798b2bf88c3038a28c50fe168d9e08c4700c31a5c3fd48376687c
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ # See kscript.gemspec for gem dependencies
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 kevin197011
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 all
13
+ 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 THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,169 @@
1
+ # kscript
2
+ ```
3
+ ______ _____ _____
4
+ ___ /________________________(_)_________ /_
5
+ __ //_/_ ___/ ___/_ ___/_ /___ __ \\ __/
6
+ _ ,< _(__ )/ /__ _ / _ / __ /_/ / /_
7
+ /_/|_| /____/ \\___/ /_/ /_/ _ .___/\\__/
8
+ /_/
9
+ ```
10
+
11
+ A collection of Ruby utility scripts for various system administration and development tasks.
12
+
13
+ [![Gem Version](https://img.shields.io/gem/v/kscript)](https://rubygems.org/gems/kscript)
14
+ [![Ruby](https://github.com/kevin197011/kscript/actions/workflows/gem-push.yml/badge.svg)](https://github.com/kevin197011/kscript/actions/workflows/gem-push.yml)
15
+
16
+ ## Installation
17
+
18
+ ### Gem install (recommended)
19
+
20
+ ```bash
21
+ gem install kscript
22
+ ```
23
+
24
+ Or from local source:
25
+
26
+ ```bash
27
+ git clone https://github.com/kevin197011/kscript.git
28
+ cd kscript
29
+ gem build kscript.gemspec
30
+ gem install ./kscript-*.gem
31
+ ```
32
+
33
+ ### Bundler (for development)
34
+
35
+ ```bash
36
+ git clone https://github.com/kevin197011/kscript.git
37
+ cd kscript
38
+ bundle install
39
+ ```
40
+
41
+ ## Usage
42
+
43
+ Most scripts can be executed directly via command line after gem install:
44
+
45
+ ```bash
46
+ kscript SCRIPT_NAME [args]
47
+ ```
48
+
49
+ Or, for legacy usage via curl:
50
+
51
+ ```bash
52
+ curl -sSL https://raw.githubusercontent.com/kevin197011/kscript/main/bin/SCRIPT_NAME.rb | ruby
53
+ ```
54
+
55
+ ## Available Scripts
56
+
57
+ ### System Tools
58
+ - `mac-top-usage.rb` - Display top CPU and memory usage processes on macOS
59
+ - `port-scanner.rb` - Multi-threaded port scanner
60
+ - `mouse-simulator.rb` - Simulate mouse movement to prevent system idle
61
+ - `source-cleaner.rb` - Clean up old source code versions
62
+ - `ffmpeg-installer.rb` - FFmpeg installation script for Linux systems
63
+
64
+ ### Network Tools
65
+ - `ip-api.rb` - Query IP geolocation information (supports auto-detecting public IP)
66
+ - `apnic-ip-range.rb` - Fetch IP ranges from APNIC database
67
+ - `wireguard-acl.rb` - Configure WireGuard firewall access control
68
+ - `wireguard-password.rb` - Generate WireGuard password hashes
69
+
70
+ ### Development Tools
71
+ - `shell-helper.rb` - Quick shell command reference tool
72
+ - `rename.rb` - Batch rename files using regular expressions
73
+ - `jenkins-job-manager.rb` - Manage Jenkins jobs (export/import)
74
+ - `kibana-utils.rb` - Kibana management utilities
75
+
76
+ ### Windows Specific
77
+ - `windows-font-enhancer.rb` - Enhance Windows font rendering (macOS-like)
78
+
79
+ ### Infrastructure Tools
80
+ - `elastic-cert-fingerprint.rb` - Generate Elasticsearch certificate fingerprints
81
+ - `lvm-mounter.rb` - LVM volume creation and mounting utility
82
+
83
+ ## Examples
84
+
85
+ 1. Query IP geolocation:
86
+ ```bash
87
+ # Query specific IP
88
+ curl -sSL https://raw.githubusercontent.com/kevin197011/kscript/main/bin/ip-api.rb | ruby 8.8.8.8
89
+
90
+ # Query your public IP
91
+ curl -sSL https://raw.githubusercontent.com/kevin197011/kscript/main/bin/ip-api.rb | ruby
92
+ ```
93
+
94
+ 2. View system resource usage:
95
+ ```bash
96
+ curl -sSL https://raw.githubusercontent.com/kevin197011/kscript/main/bin/mac-top-usage.rb | ruby
97
+ ```
98
+
99
+ 3. Scan ports:
100
+ ```bash
101
+ curl -sSL https://raw.githubusercontent.com/kevin197011/kscript/main/bin/port-scanner.rb | ruby
102
+ ```
103
+
104
+ ## Dependencies
105
+
106
+ Required gems:
107
+ ```ruby
108
+ gem 'http'
109
+ gem 'rubocop'
110
+ ```
111
+
112
+ ## License
113
+
114
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
115
+
116
+ ## Contributing
117
+
118
+ 1. Fork it
119
+ 2. Create your feature branch (`git checkout -b feature/my-new-feature`)
120
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
121
+ 4. Push to the branch (`git push origin feature/my-new-feature`)
122
+ 5. Create new Pull Request
123
+
124
+ ## Unified CLI Usage
125
+
126
+ After gem install, you can use the unified kk command:
127
+
128
+ ```bash
129
+ kk <command> [args...]
130
+
131
+ # List all available tools
132
+ kk --help
133
+
134
+ # Example: scan ports
135
+ kk port-scanner 192.168.1.1
136
+
137
+ # Example: check macOS system
138
+ kk mac-sys-check
139
+ ```
140
+
141
+ Each subcommand supports --help for its own usage.
142
+
143
+ ## Global Configuration
144
+
145
+ You can set global options for all kk tools in `~/.kscriptrc` (YAML format):
146
+
147
+ ```yaml
148
+ log_level: debug
149
+ trace_id: my-global-trace
150
+ ```
151
+
152
+ - These settings will be used by default for all commands unless overridden by CLI options or environment variables.
153
+
154
+ ## Shell Completion 自动补全
155
+
156
+ kscript 现已支持 zsh 和 bash 的命令自动补全,且**安装后首次运行会自动为你部署补全脚本**,无需手动操作。
157
+
158
+ - **zsh 补全脚本路径**:`~/.zsh/completions/_kscript`
159
+ - **bash 补全脚本路径**:`~/.bash_completion.d/kscript`
160
+
161
+ 如需手动重新生成补全脚本,可运行:
162
+
163
+ ```bash
164
+ kscript completion zsh > ~/.zsh/completions/_kscript
165
+ kscript completion bash > ~/.bash_completion.d/kscript
166
+ ```
167
+
168
+ > 每次升级或新增命令后,补全脚本也会自动更新。
169
+
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2025 kk
4
+ #
5
+ # This software is released under the MIT License.
6
+ # https://opensource.org/licenses/MIT
7
+
8
+ require 'time'
9
+ require 'rake'
10
+ require 'bundler/gem_tasks'
11
+
12
+ task default: %w[push]
13
+
14
+ task :push do
15
+ system <<-SHELL
16
+ rubocop -A
17
+ git update-index --chmod=+x push
18
+ git add .
19
+ git commit -m "Update #{Time.now}"
20
+ git pull
21
+ git push origin main
22
+ SHELL
23
+ end
24
+
25
+ # 其他自定义任务可在此添加
26
+ task :dev do
27
+ sh <<-SHELL
28
+ gem uninstall kscript -aIx
29
+ gem build kscript.gemspec
30
+ gem install kscript-*.gem
31
+ kscript help
32
+ kscript list
33
+ kscript version
34
+ SHELL
35
+ end
data/bin/kscript ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'kscript/cli'
5
+
6
+ Kscript::CLI.start(ARGV)
data/kscript.gemspec ADDED
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/kscript/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'kscript'
7
+ spec.version = Kscript::VERSION
8
+ spec.authors = ['Kk']
9
+ spec.email = ['kevin197011@outlook.com']
10
+
11
+ spec.summary = 'A collection of Ruby utility scripts for sysadmin and development.'
12
+ spec.description = 'Kscript provides a set of handy Ruby scripts for system administration, development, and automation.'
13
+ spec.homepage = 'https://github.com/kevin197011/kscript'
14
+ spec.license = 'MIT'
15
+
16
+ spec.required_ruby_version = '>= 3.0.0'
17
+
18
+ spec.files = Dir.chdir(__dir__) do
19
+ files = `git ls-files -z`.split("\x0")
20
+ files.select! do |f|
21
+ f =~ %r{^(lib/|bin/kscript|README|LICENSE|Rakefile|Gemfile|kscript\.gemspec)}
22
+ end
23
+ files
24
+ end
25
+ spec.bindir = 'bin'
26
+ spec.executables = ['kscript']
27
+ spec.require_paths = ['lib']
28
+
29
+ # Runtime dependencies
30
+ spec.add_dependency 'http', '>= 4.0', '< 6.0'
31
+ spec.add_dependency 'thor', '1.3.2'
32
+
33
+ # Development dependencies
34
+ spec.add_development_dependency 'rubocop', '~> 1.0'
35
+ spec.metadata['rubygems_mfa_required'] = 'true'
36
+
37
+ # spec.extensions = ['ext/install.rb'] # 已移除,防止 native extension build 错误
38
+
39
+ spec.post_install_message = <<~MSG
40
+ [kscript] Shell completion is available!
41
+ To enable shell completion for your shell, please run:
42
+ ruby ext/install.rb
43
+ Or see README for more details.
44
+ MSG
45
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kscript
4
+ BANNER = <<~BANNER
5
+ ______ _____ _____
6
+ ___ /________________________(_)_________ /_
7
+ __ //_/_ ___/ ___/_ ___/_ /___ __ \\ __/
8
+ _ ,< _(__ )/ /__ _ / _ / __ /_/ / /_
9
+ /_/|_| /____/ \\___/ /_/ /_/ _ .___/\\__/
10
+ /_/
11
+ BANNER
12
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kscript'
4
+
5
+ module Kscript
6
+ # Base class for all kscript scripts
7
+ class Base
8
+ attr_reader :logger
9
+
10
+ def initialize(service: nil, log_level: nil, trace_id: nil)
11
+ config = defined?(Kscript::Utils::Config) ? Kscript::Utils::Config.load : nil
12
+ log_level ||= config&.log_level || ENV['KSCRIPT_LOG_LEVEL'] || :info
13
+ trace_id ||= config&.trace_id || ENV['KSCRIPT_TRACE_ID']
14
+ @trace_id = trace_id
15
+ @logger = Kscript::Logger.new(service: service || self.class.name, level: log_level)
16
+ @logger.set_human_output(human_output?)
17
+ end
18
+
19
+ # 通用工具方法可在此扩展
20
+ def with_error_handling
21
+ yield
22
+ rescue StandardError => e
23
+ logger.error("Unhandled error: #{e.class} - #{e.message}", error: e.class.name, backtrace: e.backtrace&.first(5))
24
+ exit(1)
25
+ end
26
+
27
+ # 提供 trace_id 给 logger
28
+ def logger
29
+ @logger.define_singleton_method(:default_trace_id) { @trace_id } if @trace_id
30
+ @logger
31
+ end
32
+
33
+ # 自动注册所有 Kscript::Base 的子类为插件
34
+ def self.inherited(subclass)
35
+ name = subclass.name.split('::').last
36
+ if name.start_with?('Kk') && name.end_with?('Utils')
37
+ cmd = name[2..-6] # 去掉 Kk 和 Utils
38
+ # 转 snake_case
39
+ cmd = cmd.gsub(/([A-Z])/, '_\1').downcase.sub(/^_/, '').sub(/_$/, '')
40
+ Kscript::Plugin.register(cmd.to_sym, subclass)
41
+ end
42
+ super if defined?(super)
43
+ end
44
+
45
+ # 判断是否为人性化输出模式(无 --log/--log-level 参数且 ENV['LOG'] 未设置)
46
+ def human_output?
47
+ !(ARGV.include?('--log') || ARGV.include?('--log-level') || ENV['LOG'])
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,184 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kscript'
4
+ require 'io/console'
5
+ require 'kscript/banner'
6
+ require 'thor'
7
+
8
+ module Kscript
9
+ class CLI < Thor
10
+ class_option :log_level, type: :string, desc: 'Set log level (debug, info, warn, error, fatal)',
11
+ aliases: '--log-level'
12
+ class_option :log, type: :boolean, desc: 'Enable structured log output', default: false
13
+
14
+ def self.banner(command, _namespace = nil, _subcommand = false)
15
+ "kscript #{command.usage}" if command.respond_to?(:usage)
16
+ end
17
+
18
+ desc 'version', 'Show kscript version'
19
+ map %w[--version -v] => :version
20
+ def version
21
+ puts Kscript::BANNER
22
+ puts color('─' * 80, 90)
23
+ require 'kscript/version'
24
+ puts "kscript version: #{Kscript::VERSION}"
25
+ end
26
+
27
+ desc 'list', 'List all available commands'
28
+ def list
29
+ puts Kscript::BANNER
30
+ puts color('─' * 80, 90)
31
+ plugin_infos = Kscript::PluginLoader.plugin_infos
32
+ grouped = plugin_infos.group_by { |info| info[:group] || 'other' }
33
+ group_colors = %w[36 32 35 34 33 31 90 37]
34
+ group_names = grouped.keys.sort
35
+ group_names.each_with_index do |group, idx|
36
+ color_code = group_colors[idx % group_colors.size]
37
+ group_label = color("[#{group.capitalize}]", color_code)
38
+ puts group_label
39
+ puts color('─' * 80, 90)
40
+ grouped[group].each do |info|
41
+ command = info[:name].to_s.sub(/^kk_/, '')
42
+ desc = info[:description] || ''
43
+ usage = info[:class].respond_to?(:usage) ? info[:class].usage : nil
44
+ arguments = info[:class].respond_to?(:arguments) ? info[:class].arguments : nil
45
+ author = info[:class].respond_to?(:author) ? info[:class].author : nil
46
+ print " #{green(command.ljust(16))}"
47
+ print gray(desc)
48
+ puts
49
+ if usage && !usage.to_s.strip.empty?
50
+ usage.to_s.split("\n").each_with_index do |line, idx|
51
+ prefix = idx.zero? ? gray(' usage:') : gray(' ')
52
+ puts "#{prefix} #{cyan(line.strip)}"
53
+ end
54
+ end
55
+ puts gray(" args: #{arguments}") if arguments && !arguments.to_s.strip.empty?
56
+ puts gray(" by: #{author}") if author && !author.to_s.strip.empty?
57
+ puts gray(" #{'-' * 60}")
58
+ end
59
+ puts
60
+ end
61
+ end
62
+
63
+ # 动态注册所有插件为子命令
64
+ reserved = if defined?(Thor::Util::THOR_RESERVED_WORDS)
65
+ Thor::Util::THOR_RESERVED_WORDS.map(&:to_s)
66
+ else
67
+ %w[shell help start version list exit invoke method_missing]
68
+ end
69
+ Kscript::PluginLoader.plugin_infos.each do |info|
70
+ orig_command = info[:name].to_s.sub(/^kk_/, '')
71
+ # shell -> sh
72
+ command = orig_command == 'shell' ? 'sh' : orig_command
73
+ command = reserved.include?(command) ? "#{command}_cmd" : command
74
+ klass = info[:class]
75
+ desc command,
76
+ (info[:description] || 'No description') + (reserved.include?(orig_command) ? " (original: #{orig_command})" : '')
77
+ define_method(command) do |*args|
78
+ puts Kscript::BANNER
79
+ puts color('─' * 80, 90)
80
+ begin
81
+ instance = klass.new(*args)
82
+ instance.run
83
+ rescue ArgumentError => e
84
+ warn "Argument error: #{e.message}"
85
+ puts "Usage: kscript #{command} #{klass.respond_to?(:arguments) ? klass.arguments : '[args...]'}"
86
+ exit 1
87
+ end
88
+ end
89
+ end
90
+
91
+ desc 'completion SHELL', 'Generate shell completion script (zsh or bash)'
92
+ def completion(shell = 'zsh', capture: false)
93
+ commands = %w[version help
94
+ list] + Kscript::PluginLoader.plugin_infos.map do |info|
95
+ cmd = info[:name].to_s.sub(/^kk_/, '')
96
+ cmd = 'sh' if cmd == 'shell'
97
+ cmd
98
+ end
99
+ output = StringIO.new
100
+ case shell
101
+ when 'zsh'
102
+ output.puts "#compdef kscript\n"
103
+ output.puts '_kscript() {'
104
+ output.puts ' local -a commands'
105
+ output.puts ' commands=('
106
+ commands.each do |cmd|
107
+ output.puts " '#{cmd}:kscript command'"
108
+ end
109
+ output.puts ' )'
110
+ output.puts " _describe 'command' commands"
111
+ output.puts '}'
112
+ output.puts 'compdef _kscript kscript'
113
+ when 'bash'
114
+ output.puts '_kscript_completions() {'
115
+ output.puts " COMPREPLY=($(compgen -W \"#{commands.join(' ')}\" -- \"${COMP_WORDS[1]}\"))"
116
+ output.puts '}'
117
+ output.puts 'complete -F _kscript_completions kscript'
118
+ else
119
+ output.puts "Unsupported shell: #{shell}. Only 'zsh' and 'bash' are supported."
120
+ return capture ? output.string : (puts output.string)
121
+ end
122
+ capture ? output.string : (puts output.string)
123
+ end
124
+
125
+ no_commands do
126
+ def color(str, code)
127
+ "\e[#{code}m#{str}\e[0m"
128
+ end
129
+
130
+ def cyan(str)
131
+ color(str, 36)
132
+ end
133
+
134
+ def green(str)
135
+ color(str, 32)
136
+ end
137
+
138
+ def gray(str)
139
+ color(str, 90)
140
+ end
141
+
142
+ def bold(str)
143
+ color(str, 1)
144
+ end
145
+ end
146
+
147
+ # Thor help 美化
148
+ def self.exit_on_failure?
149
+ true
150
+ end
151
+
152
+ def help(*_args)
153
+ puts Kscript::BANNER
154
+ puts color('─' * 80, 90)
155
+ # 只展示主命令(version、help、list)
156
+ Kscript::PluginLoader.plugin_infos.select do |info|
157
+ info[:name].to_s.sub(/^kk_/, '')
158
+ false # 不输出插件命令
159
+ end
160
+ # 只输出主命令
161
+ puts bold('Available commands:')
162
+ puts green(' version'.ljust(16)) + gray('Show kscript version')
163
+ puts green(' help'.ljust(16)) + gray('Describe available commands or one specific command')
164
+ puts green(' list'.ljust(16)) + gray('List all available commands')
165
+ puts
166
+ puts bold('Options:')
167
+ puts gray(' --log-level, [--log-level=LOG_LEVEL] # Set log level (debug, info, warn, error, fatal)')
168
+ puts gray(' [--log], [--no-log], [--skip-log] # Enable structured log output')
169
+ puts gray(' --log # Enable structured log output')
170
+ puts gray(' # Default: false')
171
+ puts
172
+ puts gray("Use 'kscript list' to see all business subcommands.")
173
+ nil
174
+ end
175
+ end
176
+ end
177
+
178
+ # CLI 启动时自动检测并安装 shell 补全脚本
179
+ begin
180
+ require_relative 'utils'
181
+ Kscript::Utils::Config.ensure_completion_installed
182
+ rescue StandardError => e
183
+ warn "[kscript] Shell completion auto-install failed: #{e.message}"
184
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kscript
4
+ # Structured logger for all scripts
5
+ class Logger
6
+ LEVELS = %i[debug info warn error fatal unknown].freeze
7
+
8
+ # 极客风格终端输出
9
+ COLORS = {
10
+ info: "\e[32m", # green
11
+ warn: "\e[33m", # yellow
12
+ error: "\e[31m", # red
13
+ debug: "\e[90m", # gray
14
+ reset: "\e[0m"
15
+ }.freeze
16
+
17
+ def initialize(service: 'kscript', level: :info, out: $stdout, human_output: nil)
18
+ @service = service
19
+ @logger = ::Logger.new(out)
20
+ @logger.level = ::Logger.const_get(level.to_s.upcase)
21
+ @human_output = human_output
22
+ end
23
+
24
+ # 允许外部设置输出模式
25
+ def set_human_output(val)
26
+ @human_output = val
27
+ end
28
+
29
+ def human_output?
30
+ @human_output == true
31
+ end
32
+
33
+ def log_mode?
34
+ @human_output == false
35
+ end
36
+
37
+ LEVELS.each do |level|
38
+ define_method(level) do |message, context = {}|
39
+ if human_output?
40
+ puts "[#{level.to_s.upcase}] #{message} #{context.map { |k, v| "#{k}=#{v}" }.join(' ')}".strip
41
+ else
42
+ log(level, message, context)
43
+ end
44
+ end
45
+ end
46
+
47
+ def log(level, message, context = {})
48
+ trace_id = context[:trace_id] || (respond_to?(:default_trace_id) ? default_trace_id : nil) || SecureRandom.hex(8)
49
+ entry = {
50
+ timestamp: Time.now.utc.iso8601,
51
+ level: level.to_s.upcase,
52
+ service: @service,
53
+ message: message,
54
+ trace_id: trace_id,
55
+ context: context.reject { |k, _| k == :trace_id }
56
+ }
57
+ @logger.send(level, entry.to_json)
58
+ end
59
+
60
+ # 极客风格终端输出
61
+ def klog(level, message, context = {})
62
+ if human_output?
63
+ puts "[#{level.to_s.upcase}] #{message} #{context.map { |k, v| "#{k}=#{v}" }.join(' ')}".strip
64
+ else
65
+ ts = Time.now.strftime('%Y-%m-%d %H:%M:%S')
66
+ lvl = level.to_s.upcase
67
+ svc = @service || 'kscript'
68
+ trace = context[:trace_id] || (respond_to?(:default_trace_id) ? default_trace_id : nil) || '-'
69
+ color = COLORS[level] || COLORS[:info]
70
+ ctx_str = context.reject { |k, _| k == :trace_id }.map { |k, v| "#{k}=#{v}" }.join(' ')
71
+ line = "[#{ts}] [#{lvl}] [#{svc}] [#{trace}] #{message}"
72
+ line += " | #{ctx_str}" unless ctx_str.empty?
73
+ $stdout.puts "#{color}#{line}#{COLORS[:reset]}"
74
+ end
75
+ end
76
+
77
+ # 便捷方法
78
+ def kinfo(msg, ctx = {})
79
+ klog(:info, msg, ctx)
80
+ end
81
+
82
+ def kwarn(msg, ctx = {})
83
+ klog(:warn, msg, ctx)
84
+ end
85
+
86
+ def kerror(msg, ctx = {})
87
+ klog(:error, msg, ctx)
88
+ end
89
+
90
+ def kdebug(msg, ctx = {})
91
+ klog(:debug, msg, ctx)
92
+ end
93
+ end
94
+ end