makit 0.0.140 → 0.0.141
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/README.md +41 -41
- data/exe/makit +5 -5
- data/lib/makit/apache.rb +28 -28
- data/lib/makit/auto.rb +48 -48
- data/lib/makit/cli/build_commands.rb +500 -500
- data/lib/makit/cli/generators/base_generator.rb +74 -74
- data/lib/makit/cli/generators/dotnet_generator.rb +50 -50
- data/lib/makit/cli/generators/generator_factory.rb +49 -49
- data/lib/makit/cli/generators/node_generator.rb +50 -50
- data/lib/makit/cli/generators/ruby_generator.rb +77 -77
- data/lib/makit/cli/generators/rust_generator.rb +50 -50
- data/lib/makit/cli/generators/templates/dotnet_templates.rb +167 -167
- data/lib/makit/cli/generators/templates/node_templates.rb +161 -161
- data/lib/makit/cli/generators/templates/ruby/gemfile.rb +26 -26
- data/lib/makit/cli/generators/templates/ruby/gemspec.rb +40 -40
- data/lib/makit/cli/generators/templates/ruby/main_lib.rb +33 -33
- data/lib/makit/cli/generators/templates/ruby/rakefile.rb +35 -35
- data/lib/makit/cli/generators/templates/ruby/readme.rb +63 -63
- data/lib/makit/cli/generators/templates/ruby/test.rb +39 -39
- data/lib/makit/cli/generators/templates/ruby/test_helper.rb +29 -29
- data/lib/makit/cli/generators/templates/ruby/version.rb +29 -29
- data/lib/makit/cli/generators/templates/rust_templates.rb +128 -128
- data/lib/makit/cli/main.rb +69 -69
- data/lib/makit/cli/project_commands.rb +868 -868
- data/lib/makit/cli/repository_commands.rb +661 -661
- data/lib/makit/cli/strategy_commands.rb +203 -203
- data/lib/makit/cli/utility_commands.rb +521 -521
- data/lib/makit/commands/factory.rb +359 -359
- data/lib/makit/commands/middleware/base.rb +73 -73
- data/lib/makit/commands/middleware/cache.rb +248 -248
- data/lib/makit/commands/middleware/command_logger.rb +312 -312
- data/lib/makit/commands/middleware/validator.rb +269 -269
- data/lib/makit/commands/request.rb +316 -316
- data/lib/makit/commands/result.rb +323 -323
- data/lib/makit/commands/runner.rb +388 -385
- data/lib/makit/commands/strategies/base.rb +171 -171
- data/lib/makit/commands/strategies/child_process.rb +165 -165
- data/lib/makit/commands/strategies/factory.rb +136 -136
- data/lib/makit/commands/strategies/synchronous.rb +139 -139
- data/lib/makit/commands.rb +50 -50
- data/lib/makit/configuration/dotnet_project.rb +12 -12
- data/lib/makit/configuration/gitlab_helper.rb +58 -58
- data/lib/makit/configuration/project.rb +168 -168
- data/lib/makit/configuration/rakefile_helper.rb +43 -43
- data/lib/makit/configuration/step.rb +34 -34
- data/lib/makit/configuration/timeout.rb +74 -74
- data/lib/makit/configuration.rb +15 -15
- data/lib/makit/content/default_gitignore.rb +7 -7
- data/lib/makit/content/default_gitignore.txt +225 -225
- data/lib/makit/content/default_rakefile.rb +13 -13
- data/lib/makit/content/gem_rakefile.rb +16 -16
- data/lib/makit/context.rb +1 -1
- data/lib/makit/data.rb +49 -49
- data/lib/makit/directories.rb +140 -140
- data/lib/makit/directory.rb +262 -262
- data/lib/makit/docs/files.rb +89 -89
- data/lib/makit/docs/rake.rb +102 -102
- data/lib/makit/dotnet/cli.rb +69 -69
- data/lib/makit/dotnet/project.rb +217 -217
- data/lib/makit/dotnet/solution.rb +38 -38
- data/lib/makit/dotnet/solution_classlib.rb +239 -239
- data/lib/makit/dotnet/solution_console.rb +264 -264
- data/lib/makit/dotnet/solution_maui.rb +354 -354
- data/lib/makit/dotnet/solution_wasm.rb +275 -275
- data/lib/makit/dotnet/solution_wpf.rb +304 -304
- data/lib/makit/dotnet.rb +102 -102
- data/lib/makit/email.rb +90 -90
- data/lib/makit/environment.rb +142 -142
- data/lib/makit/examples/runner.rb +370 -370
- data/lib/makit/exceptions.rb +45 -45
- data/lib/makit/fileinfo.rb +24 -24
- data/lib/makit/files.rb +43 -43
- data/lib/makit/gems.rb +40 -40
- data/lib/makit/git/cli.rb +54 -54
- data/lib/makit/git/repository.rb +90 -90
- data/lib/makit/git.rb +98 -98
- data/lib/makit/gitlab_runner.rb +59 -59
- data/lib/makit/humanize.rb +137 -137
- data/lib/makit/indexer.rb +47 -47
- data/lib/makit/logging/configuration.rb +308 -308
- data/lib/makit/logging/format_registry.rb +84 -84
- data/lib/makit/logging/formatters/base.rb +39 -39
- data/lib/makit/logging/formatters/console_formatter.rb +140 -140
- data/lib/makit/logging/formatters/json_formatter.rb +65 -65
- data/lib/makit/logging/formatters/plain_text_formatter.rb +71 -71
- data/lib/makit/logging/formatters/text_formatter.rb +64 -64
- data/lib/makit/logging/log_request.rb +119 -119
- data/lib/makit/logging/logger.rb +199 -199
- data/lib/makit/logging/sinks/base.rb +91 -91
- data/lib/makit/logging/sinks/console.rb +72 -72
- data/lib/makit/logging/sinks/file_sink.rb +92 -92
- data/lib/makit/logging/sinks/structured.rb +123 -123
- data/lib/makit/logging/sinks/unified_file_sink.rb +296 -296
- data/lib/makit/logging.rb +565 -565
- data/lib/makit/markdown.rb +75 -75
- data/lib/makit/mp/basic_object_mp.rb +17 -17
- data/lib/makit/mp/command_mp.rb +13 -13
- data/lib/makit/mp/command_request.mp.rb +17 -17
- data/lib/makit/mp/project_mp.rb +199 -199
- data/lib/makit/mp/string_mp.rb +199 -191
- data/lib/makit/nuget.rb +74 -74
- data/lib/makit/port.rb +32 -32
- data/lib/makit/process.rb +163 -163
- data/lib/makit/protoc.rb +107 -107
- data/lib/makit/rake/cli.rb +196 -196
- data/lib/makit/rake/trace_controller.rb +173 -173
- data/lib/makit/rake.rb +80 -80
- data/lib/makit/ruby/cli.rb +185 -185
- data/lib/makit/ruby.rb +25 -25
- data/lib/makit/secrets.rb +51 -51
- data/lib/makit/serializer.rb +130 -130
- data/lib/makit/services/builder.rb +186 -186
- data/lib/makit/services/error_handler.rb +226 -226
- data/lib/makit/services/repository_manager.rb +231 -231
- data/lib/makit/services/validator.rb +112 -112
- data/lib/makit/setup/classlib.rb +101 -101
- data/lib/makit/setup/gem.rb +268 -268
- data/lib/makit/setup/razorclasslib.rb +101 -101
- data/lib/makit/setup/runner.rb +54 -54
- data/lib/makit/setup.rb +5 -5
- data/lib/makit/show.rb +110 -110
- data/lib/makit/storage.rb +126 -126
- data/lib/makit/symbols.rb +170 -170
- data/lib/makit/task_info.rb +130 -130
- data/lib/makit/tasks/at_exit.rb +15 -15
- data/lib/makit/tasks/build.rb +22 -22
- data/lib/makit/tasks/clean.rb +13 -13
- data/lib/makit/tasks/configure.rb +10 -10
- data/lib/makit/tasks/format.rb +10 -10
- data/lib/makit/tasks/hook_manager.rb +443 -443
- data/lib/makit/tasks/init.rb +49 -49
- data/lib/makit/tasks/integrate.rb +29 -29
- data/lib/makit/tasks/pull_incoming.rb +13 -13
- data/lib/makit/tasks/setup.rb +13 -13
- data/lib/makit/tasks/sync.rb +17 -17
- data/lib/makit/tasks/tag.rb +16 -16
- data/lib/makit/tasks/task_monkey_patch.rb +81 -81
- data/lib/makit/tasks/test.rb +22 -22
- data/lib/makit/tasks/update.rb +18 -18
- data/lib/makit/tasks.rb +20 -20
- data/lib/makit/test_cache.rb +239 -239
- data/lib/makit/tree.rb +37 -37
- data/lib/makit/v1/makit.v1_pb.rb +35 -35
- data/lib/makit/v1/makit.v1_services_pb.rb +27 -27
- data/lib/makit/version.rb +99 -99
- data/lib/makit/version_util.rb +21 -21
- data/lib/makit/wix.rb +95 -95
- data/lib/makit/yaml.rb +29 -29
- data/lib/makit/zip.rb +17 -17
- data/lib/makit copy.rb +44 -44
- data/lib/makit.rb +42 -42
- metadata +2 -2
@@ -1,312 +1,312 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "base"
|
4
|
-
require_relative "../../logging/logger"
|
5
|
-
|
6
|
-
module Makit
|
7
|
-
module Commands
|
8
|
-
module Middleware
|
9
|
-
# Command-specific logging middleware with enhanced output handling
|
10
|
-
#
|
11
|
-
# This middleware provides specialized logging for command execution with
|
12
|
-
# enhanced output handling, performance tracking, and structured data
|
13
|
-
# logging. It's designed to work alongside the base Logger middleware
|
14
|
-
# to provide comprehensive command execution logging.
|
15
|
-
#
|
16
|
-
# @example Basic usage
|
17
|
-
# logger = CommandLogger.new
|
18
|
-
# runner = Runner.new(middleware: [logger])
|
19
|
-
# result = runner.execute("git --version")
|
20
|
-
#
|
21
|
-
# @example With custom configuration
|
22
|
-
# logger = CommandLogger.new(
|
23
|
-
# log_stdout: true,
|
24
|
-
# log_stderr: true,
|
25
|
-
# log_performance: true,
|
26
|
-
# max_output_lines: 50
|
27
|
-
# )
|
28
|
-
class CommandLogger < Base
|
29
|
-
# @!attribute [r] logger
|
30
|
-
# @return [Makit::Logging::Logger] the logging instance
|
31
|
-
attr_reader :logger
|
32
|
-
|
33
|
-
# Initialize command logging middleware.
|
34
|
-
#
|
35
|
-
# @param log_stdout [Boolean] whether to log stdout output
|
36
|
-
# @param log_stderr [Boolean] whether to log stderr output
|
37
|
-
# @param log_performance [Boolean] whether to log performance metrics
|
38
|
-
# @param max_output_lines [Integer] maximum lines of output to log
|
39
|
-
def initialize(
|
40
|
-
log_stdout: false,
|
41
|
-
log_stderr: true,
|
42
|
-
log_performance: true,
|
43
|
-
max_output_lines: 100
|
44
|
-
)
|
45
|
-
@log_stdout = log_stdout
|
46
|
-
@log_stderr = log_stderr
|
47
|
-
@log_performance = log_performance
|
48
|
-
@max_output_lines = max_output_lines
|
49
|
-
|
50
|
-
# Use the default logger for all logging
|
51
|
-
@logger = Makit::Logging.default_logger
|
52
|
-
end
|
53
|
-
|
54
|
-
# Execute command with enhanced logging.
|
55
|
-
#
|
56
|
-
# @param request [Request] command request to execute
|
57
|
-
# @yield [Request] yields request to next middleware
|
58
|
-
# @return [Result] execution result
|
59
|
-
def call(request, &block)
|
60
|
-
start_time = Time.now
|
61
|
-
command_id = generate_command_id
|
62
|
-
|
63
|
-
# Log command start with enhanced context
|
64
|
-
log_command_start(request, command_id)
|
65
|
-
|
66
|
-
begin
|
67
|
-
# Execute the command
|
68
|
-
result = block.call(request)
|
69
|
-
duration = Time.now - start_time
|
70
|
-
|
71
|
-
# Log command completion with performance data
|
72
|
-
log_command_completion(request, result, duration, command_id)
|
73
|
-
|
74
|
-
# Log command output if configured
|
75
|
-
log_command_output(request, result, command_id)
|
76
|
-
|
77
|
-
result
|
78
|
-
rescue StandardError => e
|
79
|
-
duration = Time.now - start_time
|
80
|
-
|
81
|
-
# Log command error with full context
|
82
|
-
log_command_error(request, e, duration, command_id)
|
83
|
-
|
84
|
-
raise
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
# Check if this middleware applies to the given request.
|
89
|
-
#
|
90
|
-
# @param request [Request] command request
|
91
|
-
# @return [Boolean] true if middleware applies
|
92
|
-
def applicable?(_request)
|
93
|
-
# Apply to all requests
|
94
|
-
true
|
95
|
-
end
|
96
|
-
|
97
|
-
# Get middleware configuration.
|
98
|
-
#
|
99
|
-
# @return [Hash] middleware configuration
|
100
|
-
def config
|
101
|
-
{
|
102
|
-
name: "CommandLogger",
|
103
|
-
description: "Command execution logging with performance tracking and output handling",
|
104
|
-
log_stdout: @log_stdout,
|
105
|
-
log_stderr: @log_stderr,
|
106
|
-
log_performance: @log_performance,
|
107
|
-
max_output_lines: @max_output_lines,
|
108
|
-
logger_class: @logger.class.name,
|
109
|
-
}
|
110
|
-
end
|
111
|
-
|
112
|
-
private
|
113
|
-
|
114
|
-
# Log command execution start with enhanced context.
|
115
|
-
#
|
116
|
-
# @param request [Request] command request
|
117
|
-
# @param command_id [String] unique command identifier
|
118
|
-
def log_command_start(request, command_id)
|
119
|
-
log_data = {
|
120
|
-
command_id: command_id,
|
121
|
-
command: request.command,
|
122
|
-
arguments: request.arguments,
|
123
|
-
working_directory: request.directory,
|
124
|
-
timeout: request.timeout,
|
125
|
-
environment: request.environment&.keys || [],
|
126
|
-
timestamp: Time.now.iso8601,
|
127
|
-
}
|
128
|
-
|
129
|
-
@logger.debug("Command execution started", log_data)
|
130
|
-
end
|
131
|
-
|
132
|
-
# Log command execution completion with performance data.
|
133
|
-
#
|
134
|
-
# @param request [Request] command request
|
135
|
-
# @param result [Result] execution result
|
136
|
-
# @param duration [Float] execution duration
|
137
|
-
# @param command_id [String] unique command identifier
|
138
|
-
def log_command_completion(request, result, duration, command_id)
|
139
|
-
log_data = {
|
140
|
-
command_id: command_id,
|
141
|
-
command: request.command,
|
142
|
-
success: result.success?,
|
143
|
-
exit_code: result.exit_code,
|
144
|
-
duration: duration,
|
145
|
-
}
|
146
|
-
|
147
|
-
# Add performance metrics if enabled
|
148
|
-
if @log_performance
|
149
|
-
log_data.merge!(
|
150
|
-
memory_usage: get_memory_usage,
|
151
|
-
timestamp: Time.now.iso8601,
|
152
|
-
output_size: calculate_output_size(result),
|
153
|
-
)
|
154
|
-
end
|
155
|
-
|
156
|
-
# Log detailed information at debug level
|
157
|
-
@logger.debug("Command completed successfully", log_data) if result.success?
|
158
|
-
@logger.debug("Command completed with errors", log_data) unless result.success?
|
159
|
-
|
160
|
-
# Log performance warnings
|
161
|
-
log_performance_warnings(request, result, duration, command_id) if @log_performance
|
162
|
-
end
|
163
|
-
|
164
|
-
# Log command output (stdout/stderr) with line limits.
|
165
|
-
#
|
166
|
-
# @param request [Request] command request
|
167
|
-
# @param result [Result] execution result
|
168
|
-
# @param command_id [String] unique command identifier
|
169
|
-
def log_command_output(request, result, command_id)
|
170
|
-
# Log stdout if enabled and present
|
171
|
-
if @log_stdout && result.stdout && !result.stdout.empty?
|
172
|
-
output_lines = result.stdout.split("\n")
|
173
|
-
truncated = output_lines.length > @max_output_lines
|
174
|
-
|
175
|
-
log_data = {
|
176
|
-
command_id: command_id,
|
177
|
-
command: request.command,
|
178
|
-
output: truncated ? output_lines.first(@max_output_lines).join("\n") : result.stdout,
|
179
|
-
total_lines: output_lines.length,
|
180
|
-
truncated: truncated,
|
181
|
-
}
|
182
|
-
|
183
|
-
@logger.debug("Command stdout", log_data)
|
184
|
-
end
|
185
|
-
|
186
|
-
# Log stderr if enabled and present
|
187
|
-
return unless @log_stderr && result.stderr && !result.stderr.empty?
|
188
|
-
|
189
|
-
output_lines = result.stderr.split("\n")
|
190
|
-
truncated = output_lines.length > @max_output_lines
|
191
|
-
|
192
|
-
{
|
193
|
-
command_id: command_id,
|
194
|
-
command: request.command,
|
195
|
-
output: truncated ? output_lines.first(@max_output_lines).join("\n") : result.stderr,
|
196
|
-
total_lines: output_lines.length,
|
197
|
-
truncated: truncated,
|
198
|
-
}
|
199
|
-
|
200
|
-
# @logger.warn("Command stderr", log_data)
|
201
|
-
end
|
202
|
-
|
203
|
-
# Log command execution error with full context.
|
204
|
-
#
|
205
|
-
# @param request [Request] command request
|
206
|
-
# @param error [Exception] execution error
|
207
|
-
# @param duration [Float] execution duration
|
208
|
-
# @param command_id [String] unique command identifier
|
209
|
-
def log_command_error(request, error, duration, command_id)
|
210
|
-
log_data = {
|
211
|
-
command_id: command_id,
|
212
|
-
command: request.command,
|
213
|
-
arguments: request.arguments,
|
214
|
-
working_directory: request.directory,
|
215
|
-
environment: request.environment&.keys || [],
|
216
|
-
error_class: error.class.name,
|
217
|
-
error_message: error.message,
|
218
|
-
duration: duration,
|
219
|
-
backtrace: error.backtrace&.first(10),
|
220
|
-
timestamp: Time.now.iso8601,
|
221
|
-
}
|
222
|
-
|
223
|
-
@logger.error("Command execution failed", log_data)
|
224
|
-
end
|
225
|
-
|
226
|
-
# Log performance warnings for slow or resource-intensive commands.
|
227
|
-
#
|
228
|
-
# @param request [Request] command request
|
229
|
-
# @param result [Result] execution result
|
230
|
-
# @param duration [Float] execution duration
|
231
|
-
# @param command_id [String] unique command identifier
|
232
|
-
def log_performance_warnings(request, result, duration, command_id)
|
233
|
-
duration_threshold_seconds = 60.0
|
234
|
-
# Log slow commands
|
235
|
-
if duration > duration_threshold_seconds
|
236
|
-
@logger.warn("Slow command detected",
|
237
|
-
command_id: command_id,
|
238
|
-
command: request.command,
|
239
|
-
duration: duration,
|
240
|
-
threshold: "5.0s")
|
241
|
-
end
|
242
|
-
|
243
|
-
# Log commands with large output
|
244
|
-
output_size = calculate_output_size(result)
|
245
|
-
if output_size > 100_000 # 100KB
|
246
|
-
@logger.warn("Command with large output detected",
|
247
|
-
command_id: command_id,
|
248
|
-
command: request.command,
|
249
|
-
output_size: "#{output_size / 1024}KB",
|
250
|
-
threshold: "100KB")
|
251
|
-
end
|
252
|
-
|
253
|
-
# Log commands with high exit codes (potential errors)
|
254
|
-
return unless result.exit_code.positive?
|
255
|
-
|
256
|
-
@logger.debug("Command completed with non-zero exit code",
|
257
|
-
command_id: command_id,
|
258
|
-
command: request.command,
|
259
|
-
exit_code: result.exit_code,
|
260
|
-
success: result.success?)
|
261
|
-
end
|
262
|
-
|
263
|
-
# Calculate total output size (stdout + stderr).
|
264
|
-
#
|
265
|
-
# @param result [Result] execution result
|
266
|
-
# @return [Integer] total output size in bytes
|
267
|
-
def calculate_output_size(result)
|
268
|
-
size = 0
|
269
|
-
size += result.stdout&.bytesize || 0
|
270
|
-
size += result.stderr&.bytesize || 0
|
271
|
-
size
|
272
|
-
end
|
273
|
-
|
274
|
-
# Generate a unique command identifier.
|
275
|
-
#
|
276
|
-
# @return [String] unique command ID
|
277
|
-
def generate_command_id
|
278
|
-
"#{::Process.pid}-#{Time.now.to_f}-#{rand(1000)}"
|
279
|
-
end
|
280
|
-
|
281
|
-
# Get current memory usage (if available).
|
282
|
-
#
|
283
|
-
# @return [String, nil] memory usage string or nil if unavailable
|
284
|
-
def get_memory_usage
|
285
|
-
# Try to get memory usage on different platforms
|
286
|
-
if RUBY_PLATFORM.match?(/mswin|mingw/)
|
287
|
-
# Windows - try to get memory usage
|
288
|
-
begin
|
289
|
-
# This is a simplified approach for Windows
|
290
|
-
"Unknown"
|
291
|
-
rescue StandardError
|
292
|
-
nil
|
293
|
-
end
|
294
|
-
else
|
295
|
-
# Unix-like systems
|
296
|
-
begin
|
297
|
-
# Try to get memory usage from /proc/self/status
|
298
|
-
if File.exist?("/proc/self/status")
|
299
|
-
status = File.read("/proc/self/status")
|
300
|
-
if (match = status.match(/VmRSS:\s+(\d+)\s+kB/))
|
301
|
-
"#{match[1].to_i / 1024}MB"
|
302
|
-
end
|
303
|
-
end
|
304
|
-
rescue StandardError
|
305
|
-
nil
|
306
|
-
end
|
307
|
-
end
|
308
|
-
end
|
309
|
-
end
|
310
|
-
end
|
311
|
-
end
|
312
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
require_relative "../../logging/logger"
|
5
|
+
|
6
|
+
module Makit
|
7
|
+
module Commands
|
8
|
+
module Middleware
|
9
|
+
# Command-specific logging middleware with enhanced output handling
|
10
|
+
#
|
11
|
+
# This middleware provides specialized logging for command execution with
|
12
|
+
# enhanced output handling, performance tracking, and structured data
|
13
|
+
# logging. It's designed to work alongside the base Logger middleware
|
14
|
+
# to provide comprehensive command execution logging.
|
15
|
+
#
|
16
|
+
# @example Basic usage
|
17
|
+
# logger = CommandLogger.new
|
18
|
+
# runner = Runner.new(middleware: [logger])
|
19
|
+
# result = runner.execute("git --version")
|
20
|
+
#
|
21
|
+
# @example With custom configuration
|
22
|
+
# logger = CommandLogger.new(
|
23
|
+
# log_stdout: true,
|
24
|
+
# log_stderr: true,
|
25
|
+
# log_performance: true,
|
26
|
+
# max_output_lines: 50
|
27
|
+
# )
|
28
|
+
class CommandLogger < Base
|
29
|
+
# @!attribute [r] logger
|
30
|
+
# @return [Makit::Logging::Logger] the logging instance
|
31
|
+
attr_reader :logger
|
32
|
+
|
33
|
+
# Initialize command logging middleware.
|
34
|
+
#
|
35
|
+
# @param log_stdout [Boolean] whether to log stdout output
|
36
|
+
# @param log_stderr [Boolean] whether to log stderr output
|
37
|
+
# @param log_performance [Boolean] whether to log performance metrics
|
38
|
+
# @param max_output_lines [Integer] maximum lines of output to log
|
39
|
+
def initialize(
|
40
|
+
log_stdout: false,
|
41
|
+
log_stderr: true,
|
42
|
+
log_performance: true,
|
43
|
+
max_output_lines: 100
|
44
|
+
)
|
45
|
+
@log_stdout = log_stdout
|
46
|
+
@log_stderr = log_stderr
|
47
|
+
@log_performance = log_performance
|
48
|
+
@max_output_lines = max_output_lines
|
49
|
+
|
50
|
+
# Use the default logger for all logging
|
51
|
+
@logger = Makit::Logging.default_logger
|
52
|
+
end
|
53
|
+
|
54
|
+
# Execute command with enhanced logging.
|
55
|
+
#
|
56
|
+
# @param request [Request] command request to execute
|
57
|
+
# @yield [Request] yields request to next middleware
|
58
|
+
# @return [Result] execution result
|
59
|
+
def call(request, &block)
|
60
|
+
start_time = Time.now
|
61
|
+
command_id = generate_command_id
|
62
|
+
|
63
|
+
# Log command start with enhanced context
|
64
|
+
log_command_start(request, command_id)
|
65
|
+
|
66
|
+
begin
|
67
|
+
# Execute the command
|
68
|
+
result = block.call(request)
|
69
|
+
duration = Time.now - start_time
|
70
|
+
|
71
|
+
# Log command completion with performance data
|
72
|
+
log_command_completion(request, result, duration, command_id)
|
73
|
+
|
74
|
+
# Log command output if configured
|
75
|
+
log_command_output(request, result, command_id)
|
76
|
+
|
77
|
+
result
|
78
|
+
rescue StandardError => e
|
79
|
+
duration = Time.now - start_time
|
80
|
+
|
81
|
+
# Log command error with full context
|
82
|
+
log_command_error(request, e, duration, command_id)
|
83
|
+
|
84
|
+
raise
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Check if this middleware applies to the given request.
|
89
|
+
#
|
90
|
+
# @param request [Request] command request
|
91
|
+
# @return [Boolean] true if middleware applies
|
92
|
+
def applicable?(_request)
|
93
|
+
# Apply to all requests
|
94
|
+
true
|
95
|
+
end
|
96
|
+
|
97
|
+
# Get middleware configuration.
|
98
|
+
#
|
99
|
+
# @return [Hash] middleware configuration
|
100
|
+
def config
|
101
|
+
{
|
102
|
+
name: "CommandLogger",
|
103
|
+
description: "Command execution logging with performance tracking and output handling",
|
104
|
+
log_stdout: @log_stdout,
|
105
|
+
log_stderr: @log_stderr,
|
106
|
+
log_performance: @log_performance,
|
107
|
+
max_output_lines: @max_output_lines,
|
108
|
+
logger_class: @logger.class.name,
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
# Log command execution start with enhanced context.
|
115
|
+
#
|
116
|
+
# @param request [Request] command request
|
117
|
+
# @param command_id [String] unique command identifier
|
118
|
+
def log_command_start(request, command_id)
|
119
|
+
log_data = {
|
120
|
+
command_id: command_id,
|
121
|
+
command: request.command,
|
122
|
+
arguments: request.arguments,
|
123
|
+
working_directory: request.directory,
|
124
|
+
timeout: request.timeout,
|
125
|
+
environment: request.environment&.keys || [],
|
126
|
+
timestamp: Time.now.iso8601,
|
127
|
+
}
|
128
|
+
|
129
|
+
@logger.debug("Command execution started", log_data)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Log command execution completion with performance data.
|
133
|
+
#
|
134
|
+
# @param request [Request] command request
|
135
|
+
# @param result [Result] execution result
|
136
|
+
# @param duration [Float] execution duration
|
137
|
+
# @param command_id [String] unique command identifier
|
138
|
+
def log_command_completion(request, result, duration, command_id)
|
139
|
+
log_data = {
|
140
|
+
command_id: command_id,
|
141
|
+
command: request.command,
|
142
|
+
success: result.success?,
|
143
|
+
exit_code: result.exit_code,
|
144
|
+
duration: duration,
|
145
|
+
}
|
146
|
+
|
147
|
+
# Add performance metrics if enabled
|
148
|
+
if @log_performance
|
149
|
+
log_data.merge!(
|
150
|
+
memory_usage: get_memory_usage,
|
151
|
+
timestamp: Time.now.iso8601,
|
152
|
+
output_size: calculate_output_size(result),
|
153
|
+
)
|
154
|
+
end
|
155
|
+
|
156
|
+
# Log detailed information at debug level
|
157
|
+
@logger.debug("Command completed successfully", log_data) if result.success?
|
158
|
+
@logger.debug("Command completed with errors", log_data) unless result.success?
|
159
|
+
|
160
|
+
# Log performance warnings
|
161
|
+
log_performance_warnings(request, result, duration, command_id) if @log_performance
|
162
|
+
end
|
163
|
+
|
164
|
+
# Log command output (stdout/stderr) with line limits.
|
165
|
+
#
|
166
|
+
# @param request [Request] command request
|
167
|
+
# @param result [Result] execution result
|
168
|
+
# @param command_id [String] unique command identifier
|
169
|
+
def log_command_output(request, result, command_id)
|
170
|
+
# Log stdout if enabled and present
|
171
|
+
if @log_stdout && result.stdout && !result.stdout.empty?
|
172
|
+
output_lines = result.stdout.split("\n")
|
173
|
+
truncated = output_lines.length > @max_output_lines
|
174
|
+
|
175
|
+
log_data = {
|
176
|
+
command_id: command_id,
|
177
|
+
command: request.command,
|
178
|
+
output: truncated ? output_lines.first(@max_output_lines).join("\n") : result.stdout,
|
179
|
+
total_lines: output_lines.length,
|
180
|
+
truncated: truncated,
|
181
|
+
}
|
182
|
+
|
183
|
+
@logger.debug("Command stdout", log_data)
|
184
|
+
end
|
185
|
+
|
186
|
+
# Log stderr if enabled and present
|
187
|
+
return unless @log_stderr && result.stderr && !result.stderr.empty?
|
188
|
+
|
189
|
+
output_lines = result.stderr.split("\n")
|
190
|
+
truncated = output_lines.length > @max_output_lines
|
191
|
+
|
192
|
+
{
|
193
|
+
command_id: command_id,
|
194
|
+
command: request.command,
|
195
|
+
output: truncated ? output_lines.first(@max_output_lines).join("\n") : result.stderr,
|
196
|
+
total_lines: output_lines.length,
|
197
|
+
truncated: truncated,
|
198
|
+
}
|
199
|
+
|
200
|
+
# @logger.warn("Command stderr", log_data)
|
201
|
+
end
|
202
|
+
|
203
|
+
# Log command execution error with full context.
|
204
|
+
#
|
205
|
+
# @param request [Request] command request
|
206
|
+
# @param error [Exception] execution error
|
207
|
+
# @param duration [Float] execution duration
|
208
|
+
# @param command_id [String] unique command identifier
|
209
|
+
def log_command_error(request, error, duration, command_id)
|
210
|
+
log_data = {
|
211
|
+
command_id: command_id,
|
212
|
+
command: request.command,
|
213
|
+
arguments: request.arguments,
|
214
|
+
working_directory: request.directory,
|
215
|
+
environment: request.environment&.keys || [],
|
216
|
+
error_class: error.class.name,
|
217
|
+
error_message: error.message,
|
218
|
+
duration: duration,
|
219
|
+
backtrace: error.backtrace&.first(10),
|
220
|
+
timestamp: Time.now.iso8601,
|
221
|
+
}
|
222
|
+
|
223
|
+
@logger.error("Command execution failed", log_data)
|
224
|
+
end
|
225
|
+
|
226
|
+
# Log performance warnings for slow or resource-intensive commands.
|
227
|
+
#
|
228
|
+
# @param request [Request] command request
|
229
|
+
# @param result [Result] execution result
|
230
|
+
# @param duration [Float] execution duration
|
231
|
+
# @param command_id [String] unique command identifier
|
232
|
+
def log_performance_warnings(request, result, duration, command_id)
|
233
|
+
duration_threshold_seconds = 60.0
|
234
|
+
# Log slow commands
|
235
|
+
if duration > duration_threshold_seconds
|
236
|
+
@logger.warn("Slow command detected",
|
237
|
+
command_id: command_id,
|
238
|
+
command: request.command,
|
239
|
+
duration: duration,
|
240
|
+
threshold: "5.0s")
|
241
|
+
end
|
242
|
+
|
243
|
+
# Log commands with large output
|
244
|
+
output_size = calculate_output_size(result)
|
245
|
+
if output_size > 100_000 # 100KB
|
246
|
+
@logger.warn("Command with large output detected",
|
247
|
+
command_id: command_id,
|
248
|
+
command: request.command,
|
249
|
+
output_size: "#{output_size / 1024}KB",
|
250
|
+
threshold: "100KB")
|
251
|
+
end
|
252
|
+
|
253
|
+
# Log commands with high exit codes (potential errors)
|
254
|
+
return unless result.exit_code.positive?
|
255
|
+
|
256
|
+
@logger.debug("Command completed with non-zero exit code",
|
257
|
+
command_id: command_id,
|
258
|
+
command: request.command,
|
259
|
+
exit_code: result.exit_code,
|
260
|
+
success: result.success?)
|
261
|
+
end
|
262
|
+
|
263
|
+
# Calculate total output size (stdout + stderr).
|
264
|
+
#
|
265
|
+
# @param result [Result] execution result
|
266
|
+
# @return [Integer] total output size in bytes
|
267
|
+
def calculate_output_size(result)
|
268
|
+
size = 0
|
269
|
+
size += result.stdout&.bytesize || 0
|
270
|
+
size += result.stderr&.bytesize || 0
|
271
|
+
size
|
272
|
+
end
|
273
|
+
|
274
|
+
# Generate a unique command identifier.
|
275
|
+
#
|
276
|
+
# @return [String] unique command ID
|
277
|
+
def generate_command_id
|
278
|
+
"#{::Process.pid}-#{Time.now.to_f}-#{rand(1000)}"
|
279
|
+
end
|
280
|
+
|
281
|
+
# Get current memory usage (if available).
|
282
|
+
#
|
283
|
+
# @return [String, nil] memory usage string or nil if unavailable
|
284
|
+
def get_memory_usage
|
285
|
+
# Try to get memory usage on different platforms
|
286
|
+
if RUBY_PLATFORM.match?(/mswin|mingw/)
|
287
|
+
# Windows - try to get memory usage
|
288
|
+
begin
|
289
|
+
# This is a simplified approach for Windows
|
290
|
+
"Unknown"
|
291
|
+
rescue StandardError
|
292
|
+
nil
|
293
|
+
end
|
294
|
+
else
|
295
|
+
# Unix-like systems
|
296
|
+
begin
|
297
|
+
# Try to get memory usage from /proc/self/status
|
298
|
+
if File.exist?("/proc/self/status")
|
299
|
+
status = File.read("/proc/self/status")
|
300
|
+
if (match = status.match(/VmRSS:\s+(\d+)\s+kB/))
|
301
|
+
"#{match[1].to_i / 1024}MB"
|
302
|
+
end
|
303
|
+
end
|
304
|
+
rescue StandardError
|
305
|
+
nil
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|