logstash-output-qingstor 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +2 -0
- data/CODE_OF_CONDUCT.md +44 -0
- data/CONTRIBUTING.md +43 -0
- data/CONTRIBUTORS +10 -0
- data/DEVELOPER.md +2 -0
- data/Gemfile +3 -0
- data/LICENSE +11 -0
- data/README.md +181 -0
- data/lib/logstash/outputs/qingstor/file_repository.rb +112 -0
- data/lib/logstash/outputs/qingstor/qingstor_validator.rb +30 -0
- data/lib/logstash/outputs/qingstor/size_and_time_rotation_policy.rb +24 -0
- data/lib/logstash/outputs/qingstor/size_rotation_policy.rb +26 -0
- data/lib/logstash/outputs/qingstor/temporary_file.rb +63 -0
- data/lib/logstash/outputs/qingstor/temporary_file_factory.rb +110 -0
- data/lib/logstash/outputs/qingstor/time_rotation_policy.rb +26 -0
- data/lib/logstash/outputs/qingstor/uploader.rb +64 -0
- data/lib/logstash/outputs/qingstor.rb +253 -0
- data/logstash-output-qingstor.gemspec +28 -0
- data/spec/outputs/qingstor/file_repository_spec.rb +26 -0
- data/spec/outputs/qingstor/qingstor_validator_spec.rb +21 -0
- data/spec/outputs/qingstor/size_and_time_rotation_policy_spec.rb +39 -0
- data/spec/outputs/qingstor/size_rotation_policy_spec.rb +25 -0
- data/spec/outputs/qingstor/temporary_file_factory_spec.rb +82 -0
- data/spec/outputs/qingstor/temporary_file_spec.rb +55 -0
- data/spec/outputs/qingstor/time_rotation_policy_spec.rb +35 -0
- data/spec/outputs/qingstor/uploader_spec.rb +49 -0
- data/spec/outputs/qingstor_spec.rb +50 -0
- data/spec/outputs/qs_access_helper.rb +38 -0
- data/spec/outputs/spec_helper.rb +4 -0
- metadata +169 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ff8942eb9e9d52efdd58c06bfdeb77abc8698d2c
|
4
|
+
data.tar.gz: 34704f434b5c411eb7ab5bd3cbf4a553b9e41176
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 80a48c0492352fe8d1b486eccb9e4dc3848bb3aafefc8cc0229a0db3ffb63f7346cce08e8dc4efad7fcfa09f9c3a78681f2d0b81d8e705afc8d1f902068ede28
|
7
|
+
data.tar.gz: cc45d155ba152691d7972e4895f838ea4f3486fccf59bea0dd60480bbb1c6d154b2d5d3a84b356a741c1632d8fb02553c338fa7dd5e38c5966cf2888a7508b3e
|
data/CHANGELOG.md
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
6
|
+
|
7
|
+
## Our Standards
|
8
|
+
|
9
|
+
Examples of behavior that contributes to creating a positive environment
|
10
|
+
include:
|
11
|
+
|
12
|
+
* Using welcoming and inclusive language
|
13
|
+
* Being respectful of differing viewpoints and experiences
|
14
|
+
* Gracefully accepting constructive criticism
|
15
|
+
* Focusing on what is best for the community
|
16
|
+
* Showing empathy towards other community members
|
17
|
+
|
18
|
+
Examples of unacceptable behavior by participants include:
|
19
|
+
|
20
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
21
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
22
|
+
* Public or private harassment
|
23
|
+
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
24
|
+
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
25
|
+
|
26
|
+
## Our Responsibilities
|
27
|
+
|
28
|
+
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
29
|
+
|
30
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
31
|
+
|
32
|
+
## Scope
|
33
|
+
|
34
|
+
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
35
|
+
|
36
|
+
## Enforcement
|
37
|
+
|
38
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at pengsrc@yunify.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
39
|
+
|
40
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
41
|
+
|
42
|
+
## Attribution
|
43
|
+
|
44
|
+
This Code of Conduct is adapted from the [Contributor Covenant][http://contributor-covenant.org], version 1.4, available at [http://contributor-covenant.org/version/1/4][http://contributor-covenant.org/version/1/4].
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Contributing Guidelines
|
2
|
+
|
3
|
+
We love contributions from everyone. By participating in this project, you agree to abide by the [_`Contributor Covenant Code of Conduct`_](./CODE_OF_CONDUCT.md) and the conventions described in this documentation.
|
4
|
+
|
5
|
+
## Getting Started
|
6
|
+
|
7
|
+
### Reporting Issues
|
8
|
+
|
9
|
+
A great way to contribute to the project is to send a detailed report when you encounter an issue. We always appreciate a well-written, thorough bug report, and will thank you for it!
|
10
|
+
|
11
|
+
Check that [our issues list](https://github.com/Tacinight/logstash-output-qingstor/issues) doesn't already include that problem or suggestion before submitting an issue. If you find a match, you can use the "subscribe" button to get notified on updates. Do not leave random "+1" or "I have this too" comments, as they only clutter the discussion, and don't help resolving it. However, if you have ways to reproduce the issue or have additional information that may help resolving the issue, please leave a comment.
|
12
|
+
|
13
|
+
When reporting issues, please describe what happened clearly, and include the steps required to reproduce the problem if possible and applicable. This information will help us review and fix your issue faster. When sending lengthy log-files, consider posting them as a gist (https://gist.github.com). Don't forget to remove sensitive data from your log files before posting (you can replace those parts with "CONFIDENTIAL").
|
14
|
+
|
15
|
+
### Sending Pull Request
|
16
|
+
|
17
|
+
Pull requests are always welcome, we will appreciate it.
|
18
|
+
|
19
|
+
Any significant improvement should be documented as a GitHub [issue](https://github.com/Tacinight/logstash-output-qingstor/issues) before anybody starts working on it.
|
20
|
+
|
21
|
+
1. Fork it ( https://github.com/Tacinight/logstash-output-qingstor/fork )
|
22
|
+
2. Create your feature branch (`git checkout -b new-feature`)
|
23
|
+
3. Commit your changes (`git commit -asm 'Add some feature'`)
|
24
|
+
4. Push to the branch (`git push origin new-feature`)
|
25
|
+
5. Create a new Pull Request
|
26
|
+
|
27
|
+
## Conventions
|
28
|
+
|
29
|
+
### Patch Content
|
30
|
+
|
31
|
+
* Always remove the trailing whitespace
|
32
|
+
* Keep a newline at the end of text files
|
33
|
+
* Exclude irrelevant files, such as `*.a`, `.bundle`, and `.DS_Store`
|
34
|
+
* Always pass all the tests before submitting
|
35
|
+
* Let the propose of each patch simple and clear
|
36
|
+
|
37
|
+
### Commit Message
|
38
|
+
|
39
|
+
* Capitalize the subject line
|
40
|
+
* Limit the subject line to 50 characters
|
41
|
+
* Do not end the subject line with a period
|
42
|
+
* Wrap the body at 72 characters
|
43
|
+
* Use the body to explain what happened
|
data/CONTRIBUTORS
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
The following is a list of people who have contributed ideas, code, bug
|
2
|
+
reports, or in general have helped logstash along its way.
|
3
|
+
|
4
|
+
Contributors:
|
5
|
+
* Evan Zhao - tacingiht@gmail.com
|
6
|
+
|
7
|
+
Note: If you've sent us patches, bug reports, or otherwise contributed to
|
8
|
+
Logstash, and you aren't on the list above and want to be, please let us know
|
9
|
+
and we'll make sure you're here. Contributions from folks like you are what make
|
10
|
+
open source awesome.
|
data/DEVELOPER.md
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
2
|
+
you may not use this file except in compliance with the License.
|
3
|
+
You may obtain a copy of the License at
|
4
|
+
|
5
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
|
7
|
+
Unless required by applicable law or agreed to in writing, software
|
8
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
9
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
10
|
+
See the License for the specific language governing permissions and
|
11
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
# Logstash Output Plugin for Qingstor
|
2
|
+
|
3
|
+
这是一个适配了[Qingstor](https://www.qingcloud.com/products/storage#qingstor), 工作在logstash中的output插件.
|
4
|
+
Qingstor是[Qingcloud](https://www.qingcloud.com/)推出的对象存储服务.
|
5
|
+
作为一个output插件, 可以将logstash输出的结果打包上传至Qingstor中.
|
6
|
+
详细功能参考下面配置说明.
|
7
|
+
|
8
|
+
目前插件已经提交至rubygems.org, 使用以下命令安装:
|
9
|
+
```sh
|
10
|
+
bin/logstash-plugin install logstash-output-qingstor
|
11
|
+
```
|
12
|
+
手动安装本地代码, 安装方法参考下文.
|
13
|
+
|
14
|
+
## 1. 配置说明
|
15
|
+
|
16
|
+
#### 1.1 最小运行配置
|
17
|
+
- 使用'-f'接受一个*.conf文件或者使用'-e'参数时, 最小运行配置至少需要以下三项
|
18
|
+
```sh
|
19
|
+
output {
|
20
|
+
qingstor {
|
21
|
+
access_key_id => 'your_access_key_id' #required
|
22
|
+
secret_access_key => 'your_secret_access_key' #required
|
23
|
+
bucket => 'bucket_name' #required
|
24
|
+
# region => "pek3a" #optional, default value "pek3a"
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
```
|
29
|
+
|
30
|
+
#### 1.2 其他可选参数说明
|
31
|
+
```sh
|
32
|
+
output {
|
33
|
+
qingstor {
|
34
|
+
......
|
35
|
+
# 前缀, 生成本地子目录, 并且添加到上传文件名的前半部分, 形成Qingstor的目录.
|
36
|
+
# 默认空,
|
37
|
+
prefix => 'aprefix'
|
38
|
+
|
39
|
+
# 本地保存临时文件的目录.
|
40
|
+
# 默认: 系统临时文件目录下的logstash2qingstor文件夹, 例如linux下 "/tmp/logstash2qingstor"
|
41
|
+
tmpdir => '/local/temporary/directory'
|
42
|
+
|
43
|
+
# 字符串数组, 添加到文件名中, 例如["a", "b", "c"], 文件名会形成如 xxx-a-b-c-xxx.xx
|
44
|
+
# 默认: 空
|
45
|
+
tags => ["tag1", "tag2", "tag3"]
|
46
|
+
|
47
|
+
# 上传文件的格式. 可选"gzip", 后缀为".gz".
|
48
|
+
# 默认: "none", 后缀".log".
|
49
|
+
encoding => "gzip"
|
50
|
+
|
51
|
+
# 文件上传的策略.
|
52
|
+
# 分别表示结合文件大小和时间的综合策略, 基于文件大小的策略, 以及基于时间的策略.
|
53
|
+
# 基于文件大小的策略表示: 当文件大小满足预设值之后, 将文件上传.
|
54
|
+
# 基于时间的策略表示: 每经过预设时间之后, 将文件上传.
|
55
|
+
# 默认: "size_and_time". 可选枚举值["size_and_time", "size", "time"].
|
56
|
+
rotation_strategy => "size_and_time"
|
57
|
+
|
58
|
+
# 配合"size_and_time", "size"的可选配置型, 单位byte
|
59
|
+
# 默认: 1024*1024*5 (byte) = 5 (MB)
|
60
|
+
size_file => 1024*1024*5
|
61
|
+
|
62
|
+
# 配合"size_and_time", "time"的可选配置型, 单位minute
|
63
|
+
# 默认: 15 (minutes)
|
64
|
+
time_file => 15
|
65
|
+
|
66
|
+
# 服务端文件加密, 支持AES256.
|
67
|
+
# 默认: "none". 可选枚举值: ["AES256", "none"]
|
68
|
+
server_side_encryption_algorithm => "AES256"
|
69
|
+
|
70
|
+
# 选用服务端文件加密时提供的秘钥, 秘钥要求32位/256bit
|
71
|
+
customer_key => "your_encryption_key"
|
72
|
+
|
73
|
+
# 宕机恢复, 启动logstash时, 自动上传目录下的遗留文件
|
74
|
+
# 默认: false
|
75
|
+
restore => true
|
76
|
+
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
```
|
81
|
+
|
82
|
+
## 2. 安装插件
|
83
|
+
|
84
|
+
#### 2.1 直接运行本地的插件
|
85
|
+
|
86
|
+
- 编辑Logstash目录下的Genfile, 添加插件的路径, 例如
|
87
|
+
```ruby
|
88
|
+
gem "logstash-output-qingstor", :path => "/your/local/logstash-output-qingstor"
|
89
|
+
```
|
90
|
+
- 安装插件
|
91
|
+
```sh
|
92
|
+
bin/logstash-plugin install --no-verify
|
93
|
+
```
|
94
|
+
- 使用插件运行
|
95
|
+
```sh
|
96
|
+
bin/logstash -e "output {qingstor {access_key_id => 'your_access_key_id'
|
97
|
+
secret_access_key => 'your_secret_access_key'
|
98
|
+
bucket => 'bucket_name' }}"
|
99
|
+
```
|
100
|
+
此时你对插件所做的任意的代码上的修改都会直接生效.
|
101
|
+
|
102
|
+
#### 2.2 安装一个本地插件然后运行
|
103
|
+
|
104
|
+
这一步你需要生成一个插件的gem包, 然后通过logstash来安装到logstash的插件目录下
|
105
|
+
- 在项目目录下生成gem
|
106
|
+
```sh
|
107
|
+
gem build logstash-output-qingstor.gemspec
|
108
|
+
```
|
109
|
+
- 在Logstash的目录下使用logstash-plugin安装
|
110
|
+
```sh
|
111
|
+
bin/logstash-plugin install /your/local/plugin/logstash-output-qingstor.gem
|
112
|
+
```
|
113
|
+
- 安装完毕之后, 就可以使用Logstash运行开始测试了.
|
114
|
+
|
115
|
+
|
116
|
+
# Logstash Output Plugin for Qingstor
|
117
|
+
|
118
|
+
As an output plugin, it can collect the outputs from logstash, and store them in [Qingstor](https://www.qingcloud.com/products/storage#qingstor), which is a remarkable object storage service provided by [Qingcloud](https://www.qingcloud.com/).
|
119
|
+
|
120
|
+
For now, We've submitted this plugin to rubygems.org. Use the following command to install.
|
121
|
+
```sh
|
122
|
+
bin/logstash-plugin install logstash-output-qingstor
|
123
|
+
```
|
124
|
+
If want to install the local code, please refer to the following guide to install it manually.
|
125
|
+
|
126
|
+
## 1. Configuration Guide
|
127
|
+
|
128
|
+
#### 1.1 Run in minimal Configuration Items
|
129
|
+
```sh
|
130
|
+
output {
|
131
|
+
qingstor {
|
132
|
+
access_key_id => 'your_access_key_id' #required
|
133
|
+
secret_access_key => 'your_secret_access_key' #required
|
134
|
+
bucket => 'bucket_name' #required
|
135
|
+
# region => "pek3a" #optional, default value "pek3a"
|
136
|
+
}
|
137
|
+
}
|
138
|
+
|
139
|
+
```
|
140
|
+
|
141
|
+
More configuration details please refer to [code part](lib/logstash/outputs/qingstor.rb).
|
142
|
+
|
143
|
+
## 2. Running your unpublished Plugin in Logstash
|
144
|
+
|
145
|
+
#### 2.1 Run in a local Logstash clone
|
146
|
+
|
147
|
+
- Edit Logstash `Gemfile` and add the local plugin path, for example:
|
148
|
+
```ruby
|
149
|
+
gem "logstash-output-qingstor", :path => "/your/local/logstash-output-qingstor"
|
150
|
+
```
|
151
|
+
- Install plugin
|
152
|
+
```sh
|
153
|
+
bin/logstash-plugin install --no-verify
|
154
|
+
```
|
155
|
+
- Run Logstash with your plugin
|
156
|
+
```sh
|
157
|
+
bin/logstash -e "output {qingstor {access_key_id => 'your_access_key_id'
|
158
|
+
secret_access_key => 'your_secret_access_key'
|
159
|
+
bucket => 'bucket_name' }}"
|
160
|
+
```
|
161
|
+
At this point any modifications to the plugin code will be applied to this local Logstash setup. After modifying the plugin, simply rerun Logstash.
|
162
|
+
|
163
|
+
#### 2.2 Run in an installed Logstash
|
164
|
+
|
165
|
+
You can use the same **2.1** method to run your plugin in an installed Logstash by editing its `Gemfile` and pointing the `:path` to your local plugin development directory or you can build the gem and install it using:
|
166
|
+
|
167
|
+
- Build your plugin gem
|
168
|
+
```sh
|
169
|
+
gem build logstash-output-qingstor.gemspec
|
170
|
+
```
|
171
|
+
- Install the plugin from the Logstash home
|
172
|
+
```sh
|
173
|
+
bin/logstash-plugin install /your/local/plugin/logstash-output-qingstor.gem
|
174
|
+
```
|
175
|
+
- Start Logstash and proceed to test the plugin
|
176
|
+
|
177
|
+
## Contributing
|
178
|
+
Please see [Contributing Guidelines](./CONTRIBUTING.md) of this project before submitting patches.
|
179
|
+
|
180
|
+
## LICENSE
|
181
|
+
The Apache License (Version 2.0, January 2004).
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "java"
|
3
|
+
require "concurrent"
|
4
|
+
require "concurrent/timer_task"
|
5
|
+
require "logstash/util"
|
6
|
+
|
7
|
+
ConcurrentHashMap = java.util.concurrent.ConcurrentHashMap
|
8
|
+
|
9
|
+
module LogStash
|
10
|
+
module Outputs
|
11
|
+
class Qingstor
|
12
|
+
class FileRepository
|
13
|
+
DEFAULT_STATE_SWEEPER_INTERVAL_SECS = 60
|
14
|
+
DEFAULT_STATE_TIME_SECS = 15 * 60
|
15
|
+
class PrefixedValue
|
16
|
+
def initialize(file_factory, stale_time)
|
17
|
+
@file_factory = file_factory
|
18
|
+
@lock = Mutex.new
|
19
|
+
@stale_time = stale_time
|
20
|
+
end
|
21
|
+
|
22
|
+
def with_lock
|
23
|
+
@lock.synchronize {
|
24
|
+
yield @file_factory
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def stale?
|
29
|
+
with_lock { |factory| factory.current.size == 0 && (Time.now - factory.current.ctime > @stale_time) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def apply(prefix)
|
33
|
+
return self
|
34
|
+
end
|
35
|
+
|
36
|
+
def delete!
|
37
|
+
with_lock { |factory| factory.current.delete! }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class FactoryInitializer
|
42
|
+
def initialize(tags, encoding, temporary_directory, stale_time)
|
43
|
+
@tags = tags
|
44
|
+
@encoding = encoding
|
45
|
+
@temporary_directory = temporary_directory
|
46
|
+
@stale_time = stale_time
|
47
|
+
end
|
48
|
+
|
49
|
+
def apply(prefix_key)
|
50
|
+
PrefixedValue.new(TemporaryFileFactory.new(prefix_key, @tags, @encoding, @temporary_directory), @stale_time)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def initialize(tags, encoding, temporary_directory,
|
55
|
+
stale_time = DEFAULT_STATE_TIME_SECS,
|
56
|
+
sweeper_interval = DEFAULT_STATE_SWEEPER_INTERVAL_SECS)
|
57
|
+
@prefixed_factories = ConcurrentHashMap.new
|
58
|
+
@sweeper_interval = sweeper_interval
|
59
|
+
@factoryinitializer = FactoryInitializer.new(tags, encoding, temporary_directory, stale_time)
|
60
|
+
|
61
|
+
start_stale_sweeper
|
62
|
+
end
|
63
|
+
|
64
|
+
def keys
|
65
|
+
@prefixed_factories.keySet
|
66
|
+
end
|
67
|
+
|
68
|
+
def each_files
|
69
|
+
@prefixed_factories.elements.each do |prefixed_file|
|
70
|
+
prefixed_file.with_lock { |factory| yield factory.current }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def get_factory(prefix_key)
|
75
|
+
@prefixed_factories.computeIfAbsent(prefix_key, @factoryinitializer).with_lock { |factory| yield factory }
|
76
|
+
end
|
77
|
+
|
78
|
+
def get_file(prefix_key)
|
79
|
+
get_factory(prefix_key) { |factory| yield factory.current }
|
80
|
+
end
|
81
|
+
|
82
|
+
def shutdown
|
83
|
+
stop_stale_sweeper
|
84
|
+
end
|
85
|
+
|
86
|
+
def size
|
87
|
+
@prefixed_factories.size
|
88
|
+
end
|
89
|
+
|
90
|
+
def remove_stale(k, v)
|
91
|
+
if v.stale?
|
92
|
+
@prefixed_factories.remove(k, v)
|
93
|
+
v.delete!
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def start_stale_sweeper
|
98
|
+
@stale_sweeper = Concurrent::TimerTask.new(:execution_interval => @sweeper_interval) do
|
99
|
+
LogStash::Util.set_thread_name("Qingstor, stale factory sweeper")
|
100
|
+
@prefixed_factories.forEach { |k, v| remove_stale(k, v) }
|
101
|
+
end
|
102
|
+
|
103
|
+
@stale_sweeper.execute
|
104
|
+
end
|
105
|
+
|
106
|
+
def stop_stale_sweeper
|
107
|
+
@stale_sweeper.shutdown
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "qingstor/sdk"
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
module LogStash
|
6
|
+
module Outputs
|
7
|
+
class Qingstor
|
8
|
+
class QingstorValidator
|
9
|
+
|
10
|
+
def self.bucket_valid?(bucket)
|
11
|
+
res = bucket.head
|
12
|
+
case res[:status_code]
|
13
|
+
when 401
|
14
|
+
raise LogStash::ConfigurationError, "Incorrect key id or access key."
|
15
|
+
when 404
|
16
|
+
raise LogStash::ConfigurationError, "Incorrect bucket/region name."
|
17
|
+
end
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.prefix_valid?(prefix)
|
22
|
+
if prefix.start_with?("/") || prefix.length >= 1024
|
23
|
+
raise LogStash::ConfigurationError, "Prefix must not start with '/' with length less than 1024 "
|
24
|
+
end
|
25
|
+
true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/outputs/qingstor/size_rotation_policy"
|
3
|
+
require "logstash/outputs/qingstor/time_rotation_policy"
|
4
|
+
|
5
|
+
module LogStash
|
6
|
+
module Outputs
|
7
|
+
class Qingstor
|
8
|
+
class SizeAndTimeRotationPolicy
|
9
|
+
def initialize(size_file, time_file)
|
10
|
+
@size_strategy = SizeRotationPolicy.new(size_file)
|
11
|
+
@time_strategy = TimeRotationPolicy.new(time_file)
|
12
|
+
end
|
13
|
+
|
14
|
+
def rotate?(file)
|
15
|
+
@size_strategy.rotate?(file) || @time_strategy.rotate?(file)
|
16
|
+
end
|
17
|
+
|
18
|
+
def needs_periodic?
|
19
|
+
true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module LogStash
|
3
|
+
module Outputs
|
4
|
+
class Qingstor
|
5
|
+
class SizeRotationPolicy
|
6
|
+
attr_reader :size_file
|
7
|
+
|
8
|
+
def initialize(size_file)
|
9
|
+
if size_file <= 0
|
10
|
+
raise LogStash::ConfigurationError, "'size_file' need to be greater than 0"
|
11
|
+
end
|
12
|
+
|
13
|
+
@size_file = size_file
|
14
|
+
end
|
15
|
+
|
16
|
+
def rotate?(file)
|
17
|
+
file.size >= size_file
|
18
|
+
end
|
19
|
+
|
20
|
+
def needs_periodic?
|
21
|
+
false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "thread"
|
3
|
+
require "forwardable"
|
4
|
+
require "fileutils"
|
5
|
+
require "pathname"
|
6
|
+
|
7
|
+
module LogStash
|
8
|
+
module Outputs
|
9
|
+
class Qingstor
|
10
|
+
class TemporaryFile
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
def_delegators :@fd, :path, :write, :close, :fsync
|
14
|
+
|
15
|
+
attr_reader :fd
|
16
|
+
|
17
|
+
def initialize(key, fd, tmp_path)
|
18
|
+
@key = key
|
19
|
+
@fd = fd
|
20
|
+
@tmp_path = tmp_path
|
21
|
+
@created_at = Time.now
|
22
|
+
end
|
23
|
+
|
24
|
+
def ctime
|
25
|
+
@created_at
|
26
|
+
end
|
27
|
+
|
28
|
+
def tmp_path
|
29
|
+
@tmp_path
|
30
|
+
end
|
31
|
+
|
32
|
+
def size
|
33
|
+
begin
|
34
|
+
@fd.size
|
35
|
+
rescue IOError
|
36
|
+
::File.size(path)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def key
|
41
|
+
@key.gsub(/^\//, "")
|
42
|
+
end
|
43
|
+
|
44
|
+
def delete!
|
45
|
+
@fd.close rescue IOError
|
46
|
+
FileUtils.rm_r(@tmp_path, :secure => true)
|
47
|
+
end
|
48
|
+
|
49
|
+
def empty?
|
50
|
+
size == 0
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.create_from_existing_file(file_path, tmp_folder)
|
54
|
+
key_parts = Pathname.new(file_path).relative_path_from(tmp_folder).to_s.split(::File::SEPARATOR)
|
55
|
+
TemporaryFile.new(key_parts.slice(1, key_parts.size).join("/"),
|
56
|
+
::File.open(file_path, "r"),
|
57
|
+
::File.join(tmp_folder, key_parts.slice(0, 1)))
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "socket"
|
3
|
+
require "securerandom"
|
4
|
+
require "fileutils"
|
5
|
+
require "zlib"
|
6
|
+
require "forwardable"
|
7
|
+
|
8
|
+
module LogStash
|
9
|
+
module Outputs
|
10
|
+
class Qingstor
|
11
|
+
class TemporaryFileFactory
|
12
|
+
FILE_MODE = "a"
|
13
|
+
GZIP_ENCODING = "gzip"
|
14
|
+
GZIP_EXTENSION = "log.gz"
|
15
|
+
TXT_EXTENSION = "log"
|
16
|
+
STRFTIME = "%Y-%m-%dT%H.%M"
|
17
|
+
|
18
|
+
attr_accessor :counter, :tags, :prefix, :encoding, :tmpdir, :current
|
19
|
+
|
20
|
+
def initialize(prefix, tags, encoding, tmpdir)
|
21
|
+
@counter = 0
|
22
|
+
@prefix = prefix
|
23
|
+
@tags = tags
|
24
|
+
@encoding = encoding
|
25
|
+
@tmpdir = tmpdir
|
26
|
+
@lock = Mutex.new
|
27
|
+
|
28
|
+
rotate!
|
29
|
+
end
|
30
|
+
|
31
|
+
def rotate!
|
32
|
+
@lock.synchronize {
|
33
|
+
@current = new_file
|
34
|
+
increment_counter
|
35
|
+
@current
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
def extension
|
41
|
+
gzip? ? GZIP_EXTENSION : TXT_EXTENSION
|
42
|
+
end
|
43
|
+
|
44
|
+
def gzip?
|
45
|
+
encoding == GZIP_ENCODING
|
46
|
+
end
|
47
|
+
|
48
|
+
def increment_counter
|
49
|
+
@counter += 1
|
50
|
+
end
|
51
|
+
|
52
|
+
def current_time
|
53
|
+
Time.new.strftime(STRFTIME)
|
54
|
+
end
|
55
|
+
|
56
|
+
def generate_name
|
57
|
+
filename = "ls.qingstor.#{SecureRandom.uuid}.#{current_time}"
|
58
|
+
|
59
|
+
if tags.size > 0
|
60
|
+
"#{filename}.tag_#{tags.join('.')}.part#{counter}.#{extension}"
|
61
|
+
else
|
62
|
+
"#{filename}.part#{counter}.#{extension}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def new_file
|
67
|
+
uuid = SecureRandom.uuid
|
68
|
+
name = generate_name
|
69
|
+
path = ::File.join(@tmpdir, uuid)
|
70
|
+
key = ::File.join(@prefix, name)
|
71
|
+
|
72
|
+
FileUtils.mkdir_p(::File.join(path, @prefix))
|
73
|
+
|
74
|
+
io = if gzip?
|
75
|
+
IOWrappedGzip.new(::File.open(::File.join(path, key), FILE_MODE))
|
76
|
+
else
|
77
|
+
::File.open(::File.join(path, key), FILE_MODE)
|
78
|
+
end
|
79
|
+
|
80
|
+
TemporaryFile.new(key, io, path)
|
81
|
+
end
|
82
|
+
|
83
|
+
class IOWrappedGzip
|
84
|
+
extend Forwardable
|
85
|
+
|
86
|
+
def_delegators :@gzip_writer, :write, :close
|
87
|
+
attr_accessor :file_io, :gzip_writer
|
88
|
+
|
89
|
+
def initialize(file_io)
|
90
|
+
@file_io = file_io
|
91
|
+
@gzip_writer = Zlib::GzipWriter.open(file_io)
|
92
|
+
end
|
93
|
+
|
94
|
+
def path
|
95
|
+
@gzip_writer.to_io.path
|
96
|
+
end
|
97
|
+
|
98
|
+
def size
|
99
|
+
@gzip_writer.flush
|
100
|
+
@gzip_writer.to_io.size
|
101
|
+
end
|
102
|
+
|
103
|
+
def fsync
|
104
|
+
@gzip_writer.to_io.fsync
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|