kubetailrb 0.1.0 → 0.3.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 +18 -1
- data/README.md +49 -14
- data/Rakefile +11 -0
- data/journey_log.md +1 -7
- data/kubetailrb.png +0 -0
- data/lib/kubetailrb/cmd/file.rb +2 -2
- data/lib/kubetailrb/cmd/help.rb +11 -8
- data/lib/kubetailrb/cmd/k8s.rb +77 -23
- data/lib/kubetailrb/filter/log_filter.rb +62 -0
- data/lib/kubetailrb/formatter/json_formatter.rb +93 -0
- data/lib/kubetailrb/formatter/no_op_formatter.rb +12 -0
- data/lib/kubetailrb/formatter/pod_metadata_formatter.rb +32 -0
- data/lib/kubetailrb/k8s_opts.rb +24 -3
- data/lib/kubetailrb/painter.rb +36 -0
- data/lib/kubetailrb/reader/file_reader.rb +124 -0
- data/lib/kubetailrb/reader/k8s_pod_reader.rb +115 -0
- data/lib/kubetailrb/reader/k8s_pods_reader.rb +123 -0
- data/lib/kubetailrb/reader/with_k8s_client.rb +27 -0
- data/lib/kubetailrb/validated.rb +4 -0
- data/lib/kubetailrb/version.rb +1 -1
- metadata +19 -16
- data/lib/kubetailrb/file_reader.rb +0 -122
- data/lib/kubetailrb/json_formatter.rb +0 -66
- data/lib/kubetailrb/k8s_pod_reader.rb +0 -83
- data/lib/kubetailrb/k8s_pods_reader.rb +0 -86
- data/lib/kubetailrb/no_op_formatter.rb +0 -10
- data/lib/kubetailrb/with_k8s_client.rb +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0916dd6a8baf85b2f7b4771e9d0623fdbe1ff22ca0ea4111cc54ba1fcfab7fd4'
|
4
|
+
data.tar.gz: 051cc8d7bae42f86ce12b2ac43a567674d187170c73f5079aee14f66cb91a44b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35a876c94e0a0c812e92d9e1828996c186a7e9179ada8222265ee30925cb20d91ea67f0047661df4c87eb0508b26752ee50de37ef5078740288bb6cb75c38214
|
7
|
+
data.tar.gz: 776f10f0896164550dee36bdae375852b8e374aef9d304c5ddde313202c809fa59ffcf46dbc5a7252464425b695cc62f1536895874c0c4ceeed242d8c41a6763
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
|
-
## [
|
1
|
+
## [0.3.0] - 2024-12-11
|
2
|
+
|
3
|
+
- rename `--exclude` flag to `--excludes`
|
4
|
+
- add `--mdcs` flag to include specific MDCs
|
5
|
+
|
6
|
+
## [0.2.0] - 2024-12-05
|
7
|
+
|
8
|
+
- remove `--pretty` flag
|
9
|
+
- now by default, it's displayed in pretty mode
|
10
|
+
- to display the log without formatting, use `--raw` flag
|
11
|
+
- add possibility to filter out the access and datadog logs
|
12
|
+
- support displaying stack trace in pretty mode
|
13
|
+
- support formatting rails logs
|
14
|
+
- fix color when receiving HTTP 500 in access logs
|
15
|
+
- add `--display-names` flag to display the pod and container names
|
16
|
+
- colorize pod events (blue for new pod events, red for deleted pod events)
|
2
17
|
|
3
18
|
## [0.1.0] - 2024-10-27
|
4
19
|
|
5
20
|
- Initial release
|
21
|
+
|
22
|
+
## [Unreleased]
|
data/README.md
CHANGED
@@ -1,21 +1,20 @@
|
|
1
1
|
# Kubetailrb
|
2
2
|
|
3
|
-
|
3
|
+
[](https://github.com/HazAT/badge/blob/master/LICENSE)
|
4
|
+
[](http://rubygems.org/gems/kubetailrb)
|
4
5
|
|
5
|
-
>
|
6
|
-
> This project is a pet project I used to learn the [Ruby programming language](https://www.ruby-lang.org/en/).
|
7
|
-
> So you might find lots of my [personal notes](./journey_log.md) in the codebase.
|
8
|
-
>
|
9
|
-
> If you want to have something that works, please look at the following
|
10
|
-
> projects that were used as inspirations instead:
|
11
|
-
>
|
12
|
-
> - https://github.com/stern/stern
|
13
|
-
> - https://github.com/johanhaleby/kubetail
|
6
|
+
> Tail your Kubernetes pod logs at the same time.
|
14
7
|
|
15
8
|

|
16
9
|
|
17
10
|
## Installation
|
18
11
|
|
12
|
+
```sh
|
13
|
+
gem install kubetailrb
|
14
|
+
```
|
15
|
+
|
16
|
+
If you want to install directly from the repository instead:
|
17
|
+
|
19
18
|
```sh
|
20
19
|
# Install dependencies.
|
21
20
|
./bin/setup
|
@@ -31,16 +30,28 @@ bundle exec rake install
|
|
31
30
|
kubetailrb -h
|
32
31
|
|
33
32
|
# follow pod logs
|
34
|
-
kubetailrb 'clock' --namespace sandbox
|
33
|
+
kubetailrb 'clock' --namespace sandbox --raw
|
35
34
|
|
36
35
|
# follow pod structured JSON logs and display in human friendly way
|
37
|
-
kubetailrb 'clock-json' --namespace sandbox --
|
36
|
+
kubetailrb 'clock-json' --namespace sandbox --follow
|
38
37
|
# or with shorter flags
|
39
|
-
kubetailrb 'clock-json' -n sandbox -
|
38
|
+
kubetailrb 'clock-json' -n sandbox -f
|
40
39
|
|
41
40
|
# you can filter the pods using regex on the pod names
|
42
|
-
kubetailrb '^clock(?!-json)' -n sandbox -
|
41
|
+
kubetailrb '^clock(?!-json)' -n sandbox -f
|
42
|
+
|
43
|
+
# you can also filter the containers using regex on the container names
|
44
|
+
kubetailrb 'clock' -n sandbox -f -c 'my-container'
|
43
45
|
|
46
|
+
# you can exclude access logs
|
47
|
+
kubetailrb 'clock' -n sandbox -f --excludes access-logs
|
48
|
+
# or Datadog logs
|
49
|
+
kubetailrb 'clock' -n sandbox -f --excludes dd-logs
|
50
|
+
# or both
|
51
|
+
kubetailrb 'clock' -n sandbox -f --excludes access-logs,dd-logs
|
52
|
+
|
53
|
+
# you can include your MDCs by adding your MDC names separated by a comma
|
54
|
+
kubetailrb 'clock' -n sandbox -f --mdcs thread.name,service.version
|
44
55
|
```
|
45
56
|
|
46
57
|
## Development
|
@@ -64,10 +75,34 @@ bundle exec rake test:watch
|
|
64
75
|
bundle exec rake
|
65
76
|
```
|
66
77
|
|
78
|
+
## Release a new version
|
79
|
+
|
80
|
+
Update the version in
|
81
|
+
[`lib/kubetailrb/version.rb`](./lib/kubetailrb/version.rb).
|
82
|
+
|
83
|
+
> [!WARNING]
|
84
|
+
> You may have to update the tests...
|
85
|
+
> Too lazy to update the script to also update the tests...
|
86
|
+
|
87
|
+
Then execute the script:
|
88
|
+
|
89
|
+
```sh
|
90
|
+
./bin/release
|
91
|
+
```
|
92
|
+
|
67
93
|
## Contributing
|
68
94
|
|
69
95
|
Bug reports and pull requests are welcome on GitHub at https://github.com/l-lin/kubetailrb.
|
70
96
|
|
97
|
+
## Note
|
98
|
+
|
99
|
+
This project is a pet project I used to learn the [Ruby programming language](https://www.ruby-lang.org/en/).
|
100
|
+
So you might find lots of my [personal notes](./journey_log.md) in the codebase.
|
101
|
+
|
102
|
+
If you want to have something that is more complete, please look at
|
103
|
+
[stern](https://github.com/stern/stern) project that was used as inspirations
|
104
|
+
instead.
|
105
|
+
|
71
106
|
## License
|
72
107
|
|
73
108
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
@@ -66,6 +66,17 @@ namespace :k8s do
|
|
66
66
|
puts `kubectl run #{app_name} --image #{docker_image}`
|
67
67
|
end
|
68
68
|
|
69
|
+
desc 'Delete application from k8s.'
|
70
|
+
task :delete, [:app_name] do |_, args|
|
71
|
+
next unless k8s_up?
|
72
|
+
|
73
|
+
app_name = args[:app_name]
|
74
|
+
|
75
|
+
next if app_name.nil? || app_name.strip.empty?
|
76
|
+
|
77
|
+
puts `kubectl delete po #{app_name} --force true` if pod_up?(app_name)
|
78
|
+
end
|
79
|
+
|
69
80
|
desc 'Deploy all applications to k8s.'
|
70
81
|
task :deploy_all do
|
71
82
|
Rake::Task['k8s:deploy'].invoke('clock')
|
data/journey_log.md
CHANGED
@@ -4,12 +4,6 @@
|
|
4
4
|
> tribulations, and triumphs as I navigated the world of this dynamic
|
5
5
|
> programming language.
|
6
6
|
|
7
|
-
|
8
|
-
## 🤔 Things that I'm curious about
|
9
|
-
|
10
|
-
|
11
|
-
---
|
12
|
-
|
13
7
|
## 2024-10-27
|
14
8
|
### Context
|
15
9
|
|
@@ -90,7 +84,7 @@ It seems the convention is:
|
|
90
84
|
- `features` contains the cucumber scenarios, i.e. integration tests.
|
91
85
|
- `bin/` contains some scripts that can help the developer experience,
|
92
86
|
- Rails projects also have scripts in this `bin/` directory.
|
93
|
-
- `
|
87
|
+
- `exe/` contains the executables that will be installed to the user system if
|
94
88
|
the latter is installing the gem
|
95
89
|
- It seems to be a convention from Bundler, but that is configurable in the
|
96
90
|
`gemspec` file.
|
data/kubetailrb.png
CHANGED
Binary file
|
data/lib/kubetailrb/cmd/file.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'kubetailrb/file_reader'
|
3
|
+
require 'kubetailrb/reader/file_reader'
|
4
4
|
|
5
5
|
module Kubetailrb
|
6
6
|
module Cmd
|
@@ -13,7 +13,7 @@ module Kubetailrb
|
|
13
13
|
attr_reader :reader
|
14
14
|
|
15
15
|
def initialize(filepath:, last_nb_lines: DEFAULT_NB_LINES, follow: DEFAULT_FOLLOW)
|
16
|
-
@reader = Kubetailrb::FileReader.new(filepath: filepath, last_nb_lines: last_nb_lines, follow: follow)
|
16
|
+
@reader = Kubetailrb::Reader::FileReader.new(filepath: filepath, last_nb_lines: last_nb_lines, follow: follow)
|
17
17
|
end
|
18
18
|
|
19
19
|
def execute
|
data/lib/kubetailrb/cmd/help.rb
CHANGED
@@ -14,14 +14,17 @@ module Kubetailrb
|
|
14
14
|
kubetailrb pod-query [flags]
|
15
15
|
|
16
16
|
Flags:
|
17
|
-
-v, --version
|
18
|
-
-h, --help
|
19
|
-
--tail
|
20
|
-
-f, --follow
|
21
|
-
--file
|
22
|
-
-
|
23
|
-
|
24
|
-
-n, --namespace
|
17
|
+
-v, --version Display version.
|
18
|
+
-h, --help Display help.
|
19
|
+
--tail The number of lines from the end of the logs to show. Defaults to 10.
|
20
|
+
-f, --follow Output appended data as the file grows.
|
21
|
+
--file Display file content.
|
22
|
+
-r, --raw Only display pod logs, without any special formatting.
|
23
|
+
--display-names Display pod and container names.
|
24
|
+
-n, --namespace Kubernetes namespace to use.
|
25
|
+
-c, --container Container name when multiple containers in pod. Default to '.'.
|
26
|
+
-e, --excludes Exclude types of log, separated by a comma. Supporting only 'access-logs' and 'dd-logs'.
|
27
|
+
-m, --mdcs Include MDCs if present in the JSON log, separated by a comma. E.g. 'account.id,thread.name'.
|
25
28
|
HELP
|
26
29
|
end
|
27
30
|
|
data/lib/kubetailrb/cmd/k8s.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'kubetailrb/k8s_pods_reader'
|
4
|
-
require 'kubetailrb/json_formatter'
|
5
|
-
require 'kubetailrb/no_op_formatter'
|
3
|
+
require 'kubetailrb/reader/k8s_pods_reader'
|
6
4
|
|
7
5
|
module Kubetailrb
|
8
6
|
module Cmd
|
@@ -10,17 +8,27 @@ module Kubetailrb
|
|
10
8
|
class K8s
|
11
9
|
DEFAULT_NB_LINES = 10
|
12
10
|
DEFAULT_NAMESPACE = 'default'
|
11
|
+
DEFAULT_CONTAINER_QUERY = '.*'
|
13
12
|
|
14
13
|
NAMESPACE_FLAGS = %w[-n --namespace].freeze
|
15
14
|
TAIL_FLAG = '--tail'
|
16
15
|
FOLLOW_FLAGS = %w[-f --follow].freeze
|
17
16
|
RAW_FLAGS = %w[-r --raw].freeze
|
18
|
-
|
17
|
+
DISPLAY_NAMES_FLAG = '--display-names'
|
18
|
+
|
19
|
+
CONTAINER_FLAGS = %w[-c --container].freeze
|
20
|
+
EXCLUDES_FLAGS = %w[-e --excludes].freeze
|
21
|
+
|
22
|
+
MDCS_FLAGS = %w[-m --mdcs].freeze
|
19
23
|
|
20
24
|
attr_reader :reader
|
21
25
|
|
22
|
-
def initialize(pod_query:,
|
23
|
-
@reader = Kubetailrb::K8sPodsReader.new(
|
26
|
+
def initialize(pod_query:, container_query:, opts:)
|
27
|
+
@reader = Kubetailrb::Reader::K8sPodsReader.new(
|
28
|
+
pod_query: pod_query,
|
29
|
+
container_query: container_query,
|
30
|
+
opts: opts
|
31
|
+
)
|
24
32
|
end
|
25
33
|
|
26
34
|
def execute
|
@@ -31,12 +39,15 @@ module Kubetailrb
|
|
31
39
|
def create(*args)
|
32
40
|
new(
|
33
41
|
pod_query: parse_pod_query(*args),
|
34
|
-
|
42
|
+
container_query: parse_container_query(*args),
|
35
43
|
opts: K8sOpts.new(
|
36
44
|
namespace: parse_namespace(*args),
|
37
45
|
last_nb_lines: parse_nb_lines(*args),
|
38
46
|
follow: parse_follow(*args),
|
39
|
-
raw: parse_raw(*args)
|
47
|
+
raw: parse_raw(*args),
|
48
|
+
display_names: parse_display_names(*args),
|
49
|
+
excludes: parse_excludes(*args),
|
50
|
+
mdcs: parse_mdcs(*args)
|
40
51
|
)
|
41
52
|
)
|
42
53
|
end
|
@@ -57,7 +68,7 @@ module Kubetailrb
|
|
57
68
|
#
|
58
69
|
# will return 'sandbox'.
|
59
70
|
#
|
60
|
-
# Will raise `
|
71
|
+
# Will raise `ArgumentError` if the value is not provided:
|
61
72
|
#
|
62
73
|
# kubetailrb some-pod -n
|
63
74
|
#
|
@@ -66,7 +77,7 @@ module Kubetailrb
|
|
66
77
|
|
67
78
|
index = args.find_index { |arg| NAMESPACE_FLAGS.include?(arg) }.to_i
|
68
79
|
|
69
|
-
raise
|
80
|
+
raise ArgumentError, "Missing #{NAMESPACE_FLAGS} value." if args[index + 1].nil?
|
70
81
|
|
71
82
|
args[index + 1]
|
72
83
|
end
|
@@ -78,11 +89,11 @@ module Kubetailrb
|
|
78
89
|
#
|
79
90
|
# will return 3.
|
80
91
|
#
|
81
|
-
# Will raise `
|
92
|
+
# Will raise `ArgumentError` if the value is not provided:
|
82
93
|
#
|
83
94
|
# kubetailrb some-pod --tail
|
84
95
|
#
|
85
|
-
# Will raise `
|
96
|
+
# Will raise `ArgumentError` if the provided value is not a
|
86
97
|
# number:
|
87
98
|
#
|
88
99
|
# kubetailrb some-pod --tail some-string
|
@@ -92,11 +103,11 @@ module Kubetailrb
|
|
92
103
|
|
93
104
|
index = args.find_index { |arg| arg == TAIL_FLAG }.to_i
|
94
105
|
|
95
|
-
raise
|
106
|
+
raise ArgumentError, "Missing #{TAIL_FLAG} value." if args[index + 1].nil?
|
96
107
|
|
97
108
|
last_nb_lines = args[index + 1].to_i
|
98
109
|
|
99
|
-
raise
|
110
|
+
raise ArgumentError, "Invalid #{TAIL_FLAG} value: #{args[index + 1]}." if last_nb_lines.zero?
|
100
111
|
|
101
112
|
last_nb_lines
|
102
113
|
end
|
@@ -109,19 +120,62 @@ module Kubetailrb
|
|
109
120
|
args.any? { |arg| RAW_FLAGS.include?(arg) }
|
110
121
|
end
|
111
122
|
|
112
|
-
def
|
113
|
-
args.any? { |arg|
|
123
|
+
def parse_container_query(*args)
|
124
|
+
return DEFAULT_CONTAINER_QUERY unless args.any? { |arg| CONTAINER_FLAGS.include?(arg) }
|
125
|
+
|
126
|
+
index = args.find_index { |arg| CONTAINER_FLAGS.include?(arg) }.to_i
|
127
|
+
|
128
|
+
raise ArgumentError, "Missing #{CONTAINER_FLAGS} value." if args[index + 1].nil?
|
129
|
+
|
130
|
+
args[index + 1]
|
114
131
|
end
|
115
|
-
end
|
116
|
-
end
|
117
132
|
|
118
|
-
|
119
|
-
|
133
|
+
def parse_display_names(*args)
|
134
|
+
args.include?(DISPLAY_NAMES_FLAG)
|
135
|
+
end
|
120
136
|
|
121
|
-
|
122
|
-
|
137
|
+
#
|
138
|
+
# Parse log exclusion from arguments provided in the CLI, e.g.
|
139
|
+
#
|
140
|
+
# kubetailrb some-pod --exclude access-logs,dd-logs
|
141
|
+
#
|
142
|
+
# will return [access-logs, dd-logs].
|
143
|
+
#
|
144
|
+
# Will raise `ArgumentError` if the value is not provided:
|
145
|
+
#
|
146
|
+
# kubetailrb some-pod --exclude
|
147
|
+
#
|
148
|
+
def parse_excludes(*args)
|
149
|
+
return [] unless args.any? { |arg| EXCLUDES_FLAGS.include?(arg) }
|
150
|
+
|
151
|
+
index = args.find_index { |arg| EXCLUDES_FLAGS.include?(arg) }.to_i
|
152
|
+
|
153
|
+
raise ArgumentError, "Missing #{EXCLUDES_FLAGS} value." if args[index + 1].nil?
|
154
|
+
|
155
|
+
args[index + 1].split(',')
|
156
|
+
end
|
157
|
+
|
158
|
+
#
|
159
|
+
# Parse MDCs to include from arguments provided in the CLI, e.g.
|
160
|
+
#
|
161
|
+
# kubetailrb some-pod --mdc account.id,process.thread.name
|
162
|
+
#
|
163
|
+
# will return [account.id, thread.name].
|
164
|
+
#
|
165
|
+
# Will raise `ArgumentError` if the value is not provided:
|
166
|
+
#
|
167
|
+
# kubetailrb some-pod --mdc
|
168
|
+
#
|
169
|
+
def parse_mdcs(*args)
|
170
|
+
return [] unless args.any? { |arg| MDCS_FLAGS.include?(arg) }
|
123
171
|
|
124
|
-
|
172
|
+
index = args.find_index { |arg| MDCS_FLAGS.include?(arg) }.to_i
|
173
|
+
|
174
|
+
raise ArgumentError, "Missing #{MDCS_FLAGS} value." if args[index + 1].nil?
|
175
|
+
|
176
|
+
args[index + 1].split(',')
|
177
|
+
end
|
178
|
+
end
|
125
179
|
end
|
126
180
|
end
|
127
181
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kubetailrb/validated'
|
4
|
+
|
5
|
+
module Kubetailrb
|
6
|
+
module Filter
|
7
|
+
# Filter the logs that we do not want to see.
|
8
|
+
# Currently only supporting excluding access logs and datadog logs.
|
9
|
+
class LogFilter
|
10
|
+
include Validated
|
11
|
+
|
12
|
+
def self.create(exclude)
|
13
|
+
new(exclude.include?('access-logs'), exclude.include?('dd-logs'))
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(exclude_access_logs, exclude_dd_logs)
|
17
|
+
@exclude_access_logs = exclude_access_logs
|
18
|
+
@exclude_dd_logs = exclude_dd_logs
|
19
|
+
|
20
|
+
validate
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns true if the log should be print, false otherwise.
|
24
|
+
def test(log)
|
25
|
+
return false if @exclude_access_logs && access_log?(log)
|
26
|
+
return false if @exclude_dd_logs && dd_log?(log)
|
27
|
+
|
28
|
+
true
|
29
|
+
end
|
30
|
+
|
31
|
+
def exclude_access_logs?
|
32
|
+
@exclude_access_logs
|
33
|
+
end
|
34
|
+
|
35
|
+
def exclude_dd_logs?
|
36
|
+
@exclude_dd_logs
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def validate
|
42
|
+
validate_boolean @exclude_access_logs, "Invalid exclude_access_logs: #{@exclude_access_logs}."
|
43
|
+
validate_boolean @exclude_dd_logs, "Invalid exclude_dd_logs: #{@exclude_dd_logs}."
|
44
|
+
end
|
45
|
+
|
46
|
+
def access_log?(log)
|
47
|
+
json = JSON.parse(log)
|
48
|
+
# NOTE: Shall I mutualize this function, as it's also used in
|
49
|
+
# JsonFormatter? It's only implemented in 2 places... Maybe I shall wait
|
50
|
+
# until there's a third one before applying DRY.
|
51
|
+
json.include?('http.response.status_code') || json.include?('http_status')
|
52
|
+
rescue JSON::ParserError
|
53
|
+
false
|
54
|
+
end
|
55
|
+
|
56
|
+
def dd_log?(log)
|
57
|
+
# NOTE: Is there's a better way to detect if it's a datadog log?
|
58
|
+
log.include?('[dd') || log.include?('[datadog]')
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kubetailrb/painter'
|
4
|
+
require 'kubetailrb/validated'
|
5
|
+
|
6
|
+
module Kubetailrb
|
7
|
+
module Formatter
|
8
|
+
# Format JSON to human readable.
|
9
|
+
class JsonFormatter
|
10
|
+
include Painter
|
11
|
+
include Validated
|
12
|
+
|
13
|
+
def initialize(mdcs = [])
|
14
|
+
@mdcs = mdcs
|
15
|
+
|
16
|
+
validate
|
17
|
+
end
|
18
|
+
|
19
|
+
def format(log)
|
20
|
+
json = JSON.parse(log)
|
21
|
+
|
22
|
+
return format_access_log(json) if access_log?(json)
|
23
|
+
|
24
|
+
format_application_log(json)
|
25
|
+
rescue JSON::ParserError
|
26
|
+
log
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def validate
|
32
|
+
raise_if_nil @mdcs, 'MDCs not set.'
|
33
|
+
end
|
34
|
+
|
35
|
+
def access_log?(json)
|
36
|
+
json.include?('http.response.status_code') || json.include?('http_status')
|
37
|
+
end
|
38
|
+
|
39
|
+
def format_access_log(json)
|
40
|
+
"#{json["@timestamp"]}#{format_http_status_code json}#{http_method json} #{url_path json}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def format_application_log(json)
|
44
|
+
"#{json["@timestamp"]}#{format_log_level json}#{format_mdcs json}#{json["message"]}#{format_stack_trace json}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def format_stack_trace(json)
|
48
|
+
stack_trace = json['error.stack_trace']
|
49
|
+
|
50
|
+
return '' if stack_trace.nil? || stack_trace.strip&.empty?
|
51
|
+
|
52
|
+
"\n#{stack_trace}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def format_http_status_code(json)
|
56
|
+
code = json['http.response.status_code'] || json['http_status']
|
57
|
+
|
58
|
+
return " #{highlight_blue(" I ")} [#{code}] " if code >= 200 && code < 400
|
59
|
+
return " #{highlight_yellow(" W ")} [#{code}] " if code >= 400 && code < 500
|
60
|
+
return " #{highlight_red(" E ")} [#{code}] " if code >= 500
|
61
|
+
|
62
|
+
" #{code} "
|
63
|
+
end
|
64
|
+
|
65
|
+
def format_log_level(json)
|
66
|
+
level = json['log.level'] || json.dig('log', 'level')
|
67
|
+
return ' ' if level.nil? || level.strip.empty?
|
68
|
+
return " #{highlight_blue(" I ")} " if level == 'INFO'
|
69
|
+
return " #{highlight_yellow(" W ")} " if level == 'WARN'
|
70
|
+
return " #{highlight_red(" E ")} " if level == 'ERROR'
|
71
|
+
|
72
|
+
" #{level} "
|
73
|
+
end
|
74
|
+
|
75
|
+
def http_method(json)
|
76
|
+
json['http.request.method'] || json['http_method']
|
77
|
+
end
|
78
|
+
|
79
|
+
def url_path(json)
|
80
|
+
json['url.path'] || json['http_path']
|
81
|
+
end
|
82
|
+
|
83
|
+
def format_mdcs(json)
|
84
|
+
result = ''
|
85
|
+
@mdcs.each do |mdc|
|
86
|
+
value = json[mdc] || json.dig(*mdc.split('.'))
|
87
|
+
result += "#{cyan("#{mdc}=#{value}")} " unless value.nil?
|
88
|
+
end
|
89
|
+
result.to_s
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kubetailrb/validated'
|
4
|
+
|
5
|
+
module Kubetailrb
|
6
|
+
module Formatter
|
7
|
+
# Display the pod and container name.
|
8
|
+
class PodMetadataFormatter
|
9
|
+
include Validated
|
10
|
+
|
11
|
+
def initialize(pod_name, container_name, formatter)
|
12
|
+
@pod_name = pod_name
|
13
|
+
@container_name = container_name
|
14
|
+
@formatter = formatter
|
15
|
+
|
16
|
+
validate
|
17
|
+
end
|
18
|
+
|
19
|
+
def format(log)
|
20
|
+
"#{@pod_name}/#{@container_name} | #{@formatter.format(log)}"
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def validate
|
26
|
+
raise_if_blank @pod_name, 'Pod name not set.'
|
27
|
+
raise_if_blank @container_name, 'Container name not set.'
|
28
|
+
raise_if_nil @formatter, 'Formatter not set.'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/kubetailrb/k8s_opts.rb
CHANGED
@@ -7,13 +7,27 @@ module Kubetailrb
|
|
7
7
|
class K8sOpts
|
8
8
|
include Validated
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
DEFAULT_NAMESPACE = 'default'
|
11
|
+
DEFAULT_NB_LINES = 10
|
12
|
+
|
13
|
+
attr_reader :namespace, :last_nb_lines, :excludes, :mdcs
|
14
|
+
|
15
|
+
def initialize( # rubocop:disable Metrics/ParameterLists
|
16
|
+
namespace: DEFAULT_NAMESPACE,
|
17
|
+
last_nb_lines: DEFAULT_NB_LINES,
|
18
|
+
follow: false,
|
19
|
+
raw: false,
|
20
|
+
display_names: false,
|
21
|
+
excludes: [],
|
22
|
+
mdcs: []
|
23
|
+
)
|
13
24
|
@namespace = namespace
|
14
25
|
@last_nb_lines = last_nb_lines
|
15
26
|
@follow = follow
|
16
27
|
@raw = raw
|
28
|
+
@display_names = display_names
|
29
|
+
@excludes = excludes
|
30
|
+
@mdcs = mdcs
|
17
31
|
|
18
32
|
validate
|
19
33
|
end
|
@@ -26,6 +40,10 @@ module Kubetailrb
|
|
26
40
|
@raw
|
27
41
|
end
|
28
42
|
|
43
|
+
def display_names?
|
44
|
+
@display_names
|
45
|
+
end
|
46
|
+
|
29
47
|
private
|
30
48
|
|
31
49
|
def validate
|
@@ -33,6 +51,9 @@ module Kubetailrb
|
|
33
51
|
validate_last_nb_lines @last_nb_lines
|
34
52
|
validate_boolean @follow, "Invalid follow: #{@follow}."
|
35
53
|
validate_boolean @raw, "Invalid raw: #{@raw}."
|
54
|
+
validate_boolean @display_names, "Invalid display names: #{@display_names}."
|
55
|
+
raise_if_nil @excludes, 'Excludes not set.'
|
56
|
+
raise_if_nil @mdcs, 'MDCs not set.'
|
36
57
|
end
|
37
58
|
end
|
38
59
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kubetailrb
|
4
|
+
# Add behaviors to colorize console output.
|
5
|
+
module Painter
|
6
|
+
def blue(text)
|
7
|
+
colorize(text, '34')
|
8
|
+
end
|
9
|
+
|
10
|
+
def red(text)
|
11
|
+
colorize(text, '31')
|
12
|
+
end
|
13
|
+
|
14
|
+
def cyan(text)
|
15
|
+
colorize(text, '36')
|
16
|
+
end
|
17
|
+
|
18
|
+
def highlight_blue(text)
|
19
|
+
colorize(text, '1;30;44')
|
20
|
+
end
|
21
|
+
|
22
|
+
def highlight_yellow(text)
|
23
|
+
colorize(text, '1;30;43')
|
24
|
+
end
|
25
|
+
|
26
|
+
def highlight_red(text)
|
27
|
+
colorize(text, '1;30;41')
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def colorize(text, color_code)
|
33
|
+
"\e[#{color_code}m#{text}\e[0m"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|