betterlog 0.20.2 → 1.0.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/.github/workflows/codeql-analysis.yml +72 -0
- data/.semaphore/semaphore.yml +0 -18
- data/.tool-versions +2 -3
- data/Rakefile +1 -3
- data/VERSION +1 -1
- data/betterlog.gemspec +10 -14
- data/bin/betterlog +6 -0
- data/lib/betterlog/version.rb +1 -1
- data/lib/betterlog.rb +1 -1
- data/spec/betterlog/global_metadata_spec.rb +9 -7
- data/spec/betterlog/version_spec.rb +7 -0
- data/spec/spec_helper.rb +0 -6
- metadata +8 -52
- data/Dockerfile +0 -44
- data/Makefile +0 -71
- data/TODO.md +0 -1
- data/betterlog/config.go +0 -11
- data/betterlog/healthz.go +0 -71
- data/betterlog/redis_cert_cache.go +0 -83
- data/bin/betterlog_pusher +0 -51
- data/bin/betterlog_sink +0 -47
- data/cloudbuild.yaml +0 -7
- data/cmd/betterlog-server/LICENSE +0 -13
- data/cmd/betterlog-server/main.go +0 -83
- data/go.mod +0 -16
- data/go.sum +0 -136
- data/lib/betterlog/logger.rb +0 -143
- data/spec/betterlog/logger_spec.rb +0 -96
@@ -1,83 +0,0 @@
|
|
1
|
-
package betterlog
|
2
|
-
|
3
|
-
import (
|
4
|
-
"context"
|
5
|
-
"strings"
|
6
|
-
|
7
|
-
"github.com/go-redis/redis"
|
8
|
-
"golang.org/x/crypto/acme/autocert"
|
9
|
-
)
|
10
|
-
|
11
|
-
type RedisCertCache struct {
|
12
|
-
Redis *redis.Client
|
13
|
-
PREFIX string
|
14
|
-
}
|
15
|
-
|
16
|
-
// Get reads certificate data from the specified key name.
|
17
|
-
func (cache RedisCertCache) Get(ctx context.Context, name string) ([]byte, error) {
|
18
|
-
name = strings.Join([]string{cache.PREFIX, name}, "/")
|
19
|
-
done := make(chan struct{})
|
20
|
-
var (
|
21
|
-
err error
|
22
|
-
data string
|
23
|
-
)
|
24
|
-
go func() {
|
25
|
-
defer close(done)
|
26
|
-
result := cache.Redis.Get(name)
|
27
|
-
err = result.Err()
|
28
|
-
if err == nil {
|
29
|
-
data, err = result.Result()
|
30
|
-
}
|
31
|
-
}()
|
32
|
-
select {
|
33
|
-
case <-ctx.Done():
|
34
|
-
return nil, ctx.Err()
|
35
|
-
case <-done:
|
36
|
-
}
|
37
|
-
if err == redis.Nil {
|
38
|
-
return nil, autocert.ErrCacheMiss
|
39
|
-
}
|
40
|
-
return []byte(data), err
|
41
|
-
}
|
42
|
-
|
43
|
-
// Put writes the certificate data to the specified redis key name.
|
44
|
-
func (cache RedisCertCache) Put(ctx context.Context, name string, data []byte) error {
|
45
|
-
name = strings.Join([]string{cache.PREFIX, name}, "/")
|
46
|
-
done := make(chan struct{})
|
47
|
-
var err error
|
48
|
-
go func() {
|
49
|
-
defer close(done)
|
50
|
-
select {
|
51
|
-
case <-ctx.Done():
|
52
|
-
// Don't overwrite the key if the context was canceled.
|
53
|
-
default:
|
54
|
-
result := cache.Redis.Set(name, string(data), 0)
|
55
|
-
err = result.Err()
|
56
|
-
}
|
57
|
-
}()
|
58
|
-
select {
|
59
|
-
case <-ctx.Done():
|
60
|
-
return ctx.Err()
|
61
|
-
case <-done:
|
62
|
-
}
|
63
|
-
return err
|
64
|
-
}
|
65
|
-
|
66
|
-
// Delete removes the specified key name.
|
67
|
-
func (cache RedisCertCache) Delete(ctx context.Context, name string) error {
|
68
|
-
name = strings.Join([]string{cache.PREFIX, name}, "/")
|
69
|
-
var (
|
70
|
-
err error
|
71
|
-
done = make(chan struct{})
|
72
|
-
)
|
73
|
-
go func() {
|
74
|
-
defer close(done)
|
75
|
-
err = cache.Redis.Del(name).Err()
|
76
|
-
}()
|
77
|
-
select {
|
78
|
-
case <-ctx.Done():
|
79
|
-
return ctx.Err()
|
80
|
-
case <-done:
|
81
|
-
}
|
82
|
-
return err
|
83
|
-
}
|
data/bin/betterlog_pusher
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# vim: set ft=ruby et sw=2 ts=2:
|
3
|
-
|
4
|
-
require 'betterlog'
|
5
|
-
require 'excon'
|
6
|
-
require 'redlock'
|
7
|
-
|
8
|
-
lines = Integer(ENV.fetch('BETTERLOG_LINES', 1_000))
|
9
|
-
lock_time = Integer(ENV.fetch('BETTERLOG_LOCK_TIME', 60_000))
|
10
|
-
url = ENV.fetch('BETTERLOG_SERVER_URL')
|
11
|
-
name = ENV['BETTERLOG_NAME']
|
12
|
-
redis_url = ENV.fetch('REDIS_URL')
|
13
|
-
redis =
|
14
|
-
if redis_sentinels = ENV['REDIS_SENTINELS']&.split(?,)
|
15
|
-
redis_sentinels.map! { |s|
|
16
|
-
h, p = s.split(?:, 2)
|
17
|
-
{ host: h, port: (p || 26379).to_i }
|
18
|
-
}
|
19
|
-
Redis.new(url: redis_url, sentinels: redis_sentinels, role: :master)
|
20
|
-
else
|
21
|
-
Redis.new(url: redis_url)
|
22
|
-
end
|
23
|
-
lm = Redlock::Client.new([ redis_url ])
|
24
|
-
logger = Betterlog::Logger.new(redis, name: name)
|
25
|
-
|
26
|
-
quit = false
|
27
|
-
[ :TERM, :INT, :QUIT ].each { |s| trap(s) { quit = true } }
|
28
|
-
|
29
|
-
STDOUT.sync = true
|
30
|
-
loop do
|
31
|
-
count = 0
|
32
|
-
lm.lock!(File.basename($0), lock_time) do
|
33
|
-
print ?…
|
34
|
-
logger.each_slice(lines).with_index do |batch, i|
|
35
|
-
count.zero? and print ?○
|
36
|
-
count += batch.sum(&:size)
|
37
|
-
attempt(attempts: 10, sleep: -60, reraise: true) do
|
38
|
-
print ?┄
|
39
|
-
Excon.post(url, body: batch.join)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
quit and exit
|
44
|
-
if count.zero?
|
45
|
-
sleep 1
|
46
|
-
else
|
47
|
-
print "→ %s sent.\n" % Tins::Unit.format(count, format: '%.2f %U', prefix: 1024, unit: ?b)
|
48
|
-
end
|
49
|
-
rescue Redlock::LockError => e
|
50
|
-
STDERR.puts "Caught #{e.class}: #{e} => Retrying!"
|
51
|
-
end
|
data/bin/betterlog_sink
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# vim: set ft=ruby et sw=2 ts=2:
|
3
|
-
|
4
|
-
require 'betterlog'
|
5
|
-
require 'complex_config/rude'
|
6
|
-
require 'json'
|
7
|
-
|
8
|
-
LOG_ENV = ENV.fetch('LOG_ENV', 'production')
|
9
|
-
|
10
|
-
@config = complex_config.log(LOG_ENV).sink
|
11
|
-
|
12
|
-
ENV['KUBECONFIG'] = File.join(
|
13
|
-
ENV.fetch('HOME'),
|
14
|
-
'.kube',
|
15
|
-
@config.cluster.name
|
16
|
-
)
|
17
|
-
|
18
|
-
def authenticate
|
19
|
-
return if File.exist?(ENV['KUBECONFIG'])
|
20
|
-
context = [
|
21
|
-
'gke',
|
22
|
-
@config.cluster.project,
|
23
|
-
@config.cluster.zone,
|
24
|
-
@config.cluster.name,
|
25
|
-
] * ?_
|
26
|
-
system(%{
|
27
|
-
gcloud --no-user-output-enabled container clusters get-credentials \
|
28
|
-
#{@config.cluster.name.inspect} \
|
29
|
-
--zone #{@config.cluster.zone.inspect} \
|
30
|
-
--project #{@config.cluster.project.inspect}
|
31
|
-
}) or fail "failed to authenticate for context #{context.inspect}"
|
32
|
-
end
|
33
|
-
|
34
|
-
authenticate
|
35
|
-
|
36
|
-
pods = JSON(
|
37
|
-
%x{ kubectl -n #{@config.namespace} -ojson get pods },
|
38
|
-
object_class: JSON::GenericObject
|
39
|
-
)
|
40
|
-
|
41
|
-
if pod = pods.items.find { |i| i.metadata.labels.app == @config.source }
|
42
|
-
system %{
|
43
|
-
kubectl logs -n #{@config.namespace} #{ARGV.join(' ')} #{pod.metadata.name}
|
44
|
-
}
|
45
|
-
else
|
46
|
-
exit 1
|
47
|
-
end
|
data/cloudbuild.yaml
DELETED
@@ -1,7 +0,0 @@
|
|
1
|
-
steps:
|
2
|
-
- name: eu.gcr.io/betterplace-183212/better-builder
|
3
|
-
args: ['build', '-t', 'eu.gcr.io/$PROJECT_ID/${_IMAGE_NAME}:$SHORT_SHA', '-t', 'eu.gcr.io/$PROJECT_ID/${_IMAGE_NAME}', '.']
|
4
|
-
images:
|
5
|
-
- 'eu.gcr.io/$PROJECT_ID/${_IMAGE_NAME}:$SHORT_SHA'
|
6
|
-
- 'eu.gcr.io/$PROJECT_ID/${_IMAGE_NAME}'
|
7
|
-
timeout: 600s
|
@@ -1,13 +0,0 @@
|
|
1
|
-
Copyright 2018 Florian Frank
|
2
|
-
|
3
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
you may not use this file except in compliance with the License.
|
5
|
-
You may obtain a copy of the License at
|
6
|
-
|
7
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
|
9
|
-
Unless required by applicable law or agreed to in writing, software
|
10
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
-
See the License for the specific language governing permissions and
|
13
|
-
limitations under the License.
|
@@ -1,83 +0,0 @@
|
|
1
|
-
package main
|
2
|
-
|
3
|
-
import (
|
4
|
-
"fmt"
|
5
|
-
"io/ioutil"
|
6
|
-
"log"
|
7
|
-
"net/http"
|
8
|
-
"os"
|
9
|
-
"strings"
|
10
|
-
|
11
|
-
betterlog "github.com/betterplace/betterlog/betterlog"
|
12
|
-
"github.com/go-redis/redis"
|
13
|
-
"github.com/kelseyhightower/envconfig"
|
14
|
-
"github.com/labstack/echo/v4"
|
15
|
-
"github.com/labstack/echo/v4/middleware"
|
16
|
-
)
|
17
|
-
|
18
|
-
func postLogHandler(c echo.Context) error {
|
19
|
-
body := c.Request().Body
|
20
|
-
data, err := ioutil.ReadAll(body)
|
21
|
-
if err == nil {
|
22
|
-
defer body.Close()
|
23
|
-
os.Stdout.Write(data)
|
24
|
-
return c.NoContent(http.StatusOK)
|
25
|
-
} else {
|
26
|
-
return c.String(http.StatusInternalServerError, err.Error())
|
27
|
-
}
|
28
|
-
}
|
29
|
-
|
30
|
-
func basicAuthConfig(config betterlog.Config) middleware.BasicAuthConfig {
|
31
|
-
return middleware.BasicAuthConfig{
|
32
|
-
Realm: config.HTTP_REALM,
|
33
|
-
Validator: func(username, password string, c echo.Context) (bool, error) {
|
34
|
-
httpAuth := strings.Split(config.HTTP_AUTH, ":")
|
35
|
-
if username == httpAuth[0] && password == httpAuth[1] {
|
36
|
-
return true, nil
|
37
|
-
}
|
38
|
-
return false, nil
|
39
|
-
},
|
40
|
-
}
|
41
|
-
}
|
42
|
-
|
43
|
-
func initializeRedis(config betterlog.Config) *redis.Client {
|
44
|
-
options, err := redis.ParseURL(config.REDIS_URL)
|
45
|
-
if err != nil {
|
46
|
-
log.Panic(err)
|
47
|
-
}
|
48
|
-
options.MaxRetries = 3
|
49
|
-
return redis.NewClient(options)
|
50
|
-
}
|
51
|
-
|
52
|
-
func main() {
|
53
|
-
var config betterlog.Config
|
54
|
-
err := envconfig.Process("", &config)
|
55
|
-
if err != nil {
|
56
|
-
log.Fatal(err)
|
57
|
-
}
|
58
|
-
e := echo.New()
|
59
|
-
|
60
|
-
if !config.SSL {
|
61
|
-
e.Use(betterlog.Health{}.ZPage)
|
62
|
-
}
|
63
|
-
if config.HTTP_AUTH != "" {
|
64
|
-
fmt.Println("info: Configuring HTTP Auth access control")
|
65
|
-
e.Use(middleware.BasicAuthWithConfig(basicAuthConfig(config)))
|
66
|
-
}
|
67
|
-
e.POST("/log", postLogHandler)
|
68
|
-
if config.SSL {
|
69
|
-
log.Println("Starting SSL AutoTLS service.")
|
70
|
-
redis := initializeRedis(config)
|
71
|
-
e.AutoTLSManager.Cache = betterlog.RedisCertCache{
|
72
|
-
Redis: redis,
|
73
|
-
PREFIX: config.REDIS_PREFIX,
|
74
|
-
}
|
75
|
-
go betterlog.StartHealthzEcho(
|
76
|
-
betterlog.Health{
|
77
|
-
PORT: config.HEALTHZ_PORT,
|
78
|
-
})
|
79
|
-
e.Logger.Fatal(e.StartAutoTLS(fmt.Sprintf(":%d", config.PORT)))
|
80
|
-
} else {
|
81
|
-
e.Logger.Fatal(e.Start(fmt.Sprintf(":%d", config.PORT)))
|
82
|
-
}
|
83
|
-
}
|
data/go.mod
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
module github.com/betterplace/betterlog
|
2
|
-
|
3
|
-
go 1.16
|
4
|
-
|
5
|
-
require (
|
6
|
-
github.com/go-redis/redis v6.15.9+incompatible
|
7
|
-
github.com/kelseyhightower/envconfig v1.4.0
|
8
|
-
github.com/labstack/echo v3.3.10+incompatible
|
9
|
-
github.com/labstack/echo/v4 v4.5.0
|
10
|
-
github.com/mattn/go-isatty v0.0.13 // indirect
|
11
|
-
github.com/onsi/gomega v1.16.0 // indirect
|
12
|
-
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
|
13
|
-
golang.org/x/net v0.0.0-20210825183410-e898025ed96a // indirect
|
14
|
-
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect
|
15
|
-
golang.org/x/text v0.3.7 // indirect
|
16
|
-
)
|
data/go.sum
DELETED
@@ -1,136 +0,0 @@
|
|
1
|
-
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
2
|
-
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
3
|
-
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
4
|
-
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
5
|
-
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
6
|
-
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
7
|
-
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
|
8
|
-
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
9
|
-
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
10
|
-
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
11
|
-
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
12
|
-
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
13
|
-
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
14
|
-
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
15
|
-
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
16
|
-
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
17
|
-
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
18
|
-
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
19
|
-
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
20
|
-
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
21
|
-
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
22
|
-
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
23
|
-
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
24
|
-
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
25
|
-
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
26
|
-
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
|
27
|
-
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
28
|
-
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
|
29
|
-
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
|
30
|
-
github.com/labstack/echo/v4 v4.5.0 h1:JXk6H5PAw9I3GwizqUHhYyS4f45iyGebR/c1xNCeOCY=
|
31
|
-
github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y=
|
32
|
-
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
|
33
|
-
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
34
|
-
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
35
|
-
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
36
|
-
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
37
|
-
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
38
|
-
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
39
|
-
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
40
|
-
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
|
41
|
-
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
42
|
-
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
43
|
-
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
44
|
-
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
45
|
-
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
46
|
-
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
47
|
-
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
48
|
-
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
49
|
-
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
50
|
-
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
51
|
-
github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c=
|
52
|
-
github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
53
|
-
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
54
|
-
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
55
|
-
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
56
|
-
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
57
|
-
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
58
|
-
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
59
|
-
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
60
|
-
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
61
|
-
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
62
|
-
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
|
63
|
-
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
64
|
-
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
65
|
-
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
66
|
-
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
67
|
-
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
68
|
-
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
69
|
-
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
|
70
|
-
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
71
|
-
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
72
|
-
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
73
|
-
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
74
|
-
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
75
|
-
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
76
|
-
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
77
|
-
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
78
|
-
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
79
|
-
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
80
|
-
golang.org/x/net v0.0.0-20210825183410-e898025ed96a h1:bRuuGXV8wwSdGTB+CtJf+FjgO1APK1CoO39T4BN/XBw=
|
81
|
-
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
82
|
-
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
83
|
-
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
84
|
-
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
85
|
-
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
86
|
-
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
87
|
-
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
88
|
-
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
89
|
-
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
90
|
-
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
91
|
-
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
92
|
-
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
93
|
-
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
94
|
-
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
95
|
-
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
96
|
-
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
97
|
-
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
98
|
-
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
99
|
-
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
100
|
-
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
101
|
-
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
102
|
-
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
103
|
-
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k=
|
104
|
-
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
105
|
-
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
106
|
-
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
107
|
-
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
108
|
-
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
109
|
-
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
110
|
-
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
111
|
-
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE=
|
112
|
-
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
113
|
-
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
114
|
-
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
115
|
-
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
116
|
-
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
117
|
-
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
118
|
-
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
119
|
-
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
120
|
-
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
121
|
-
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
122
|
-
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
123
|
-
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
124
|
-
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
125
|
-
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
126
|
-
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
127
|
-
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
128
|
-
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
129
|
-
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
130
|
-
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
131
|
-
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
132
|
-
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
133
|
-
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
134
|
-
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
135
|
-
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
136
|
-
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
data/lib/betterlog/logger.rb
DELETED
@@ -1,143 +0,0 @@
|
|
1
|
-
begin
|
2
|
-
require 'redis' unless defined?(Redis)
|
3
|
-
rescue LoadError
|
4
|
-
end
|
5
|
-
require 'logger'
|
6
|
-
|
7
|
-
module Betterlog
|
8
|
-
class Logger < ::Logger
|
9
|
-
include ComplexConfig::Provider::Shortcuts
|
10
|
-
|
11
|
-
def initialize(redis, shift_age = 0, shift_size = 1048576, name: nil, buffer_size: nil, **opts)
|
12
|
-
@redis = redis
|
13
|
-
@fallback = ::Logger.new(STDERR)
|
14
|
-
if level = cc.log.level?
|
15
|
-
self.level = level
|
16
|
-
@fallback.level = level
|
17
|
-
end
|
18
|
-
@name = name || self.class.name
|
19
|
-
@buffer_size = determine_buffer_size(buffer_size)
|
20
|
-
super(nil, shift_age, shift_size, **opts)
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.for_redis_url(url, shift_age = 0, shift_size = 1048576, **opts)
|
24
|
-
redis = Redis.new(url: url)
|
25
|
-
redis.ping
|
26
|
-
new(redis, shift_age, shift_size, **opts)
|
27
|
-
rescue Redis::CannotConnectError
|
28
|
-
end
|
29
|
-
|
30
|
-
private def determine_buffer_size(buffer_size)
|
31
|
-
if buffer_size.nil? || buffer_size < 1 * 1024 ** 2
|
32
|
-
1 * 1024 ** 2 # Default to very small buffer
|
33
|
-
elsif buffer_size > 511 * 1024 ** 2
|
34
|
-
511 * 1024 ** 2 # Stay well below redis' 512Mb upper limit for strings
|
35
|
-
else
|
36
|
-
buffer_size
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
private def redis_write(msg)
|
41
|
-
# Stop before reaching configured buffer_size limit, after warning a lot.
|
42
|
-
if @redis.strlen(@name) > (@buffer_size * 96) / 100
|
43
|
-
@fallback.error("Redis memory limit will soon be reached =>"\
|
44
|
-
" Log output to redis stops now unless log data is pushed away!")
|
45
|
-
return nil
|
46
|
-
end
|
47
|
-
@redis.append @name, msg
|
48
|
-
self
|
49
|
-
end
|
50
|
-
|
51
|
-
|
52
|
-
def add(severity, message = nil, progname = nil)
|
53
|
-
severity ||= UNKNOWN
|
54
|
-
if severity < @level
|
55
|
-
return true
|
56
|
-
end
|
57
|
-
if progname.nil?
|
58
|
-
progname = @progname
|
59
|
-
end
|
60
|
-
if message.nil?
|
61
|
-
if block_given?
|
62
|
-
message = yield
|
63
|
-
else
|
64
|
-
message = progname
|
65
|
-
progname = @progname
|
66
|
-
end
|
67
|
-
end
|
68
|
-
redis_write(
|
69
|
-
format_message(format_severity(severity), Time.now, progname, message))
|
70
|
-
true
|
71
|
-
rescue Redis::BaseConnectionError
|
72
|
-
@fallback.add(severity, message, progname)
|
73
|
-
end
|
74
|
-
|
75
|
-
def <<(msg)
|
76
|
-
redis_write(msg)
|
77
|
-
rescue Redis::BaseConnectionError
|
78
|
-
@fallback << msg
|
79
|
-
end
|
80
|
-
|
81
|
-
def clear
|
82
|
-
@redis.del @name
|
83
|
-
self
|
84
|
-
end
|
85
|
-
|
86
|
-
def each_chunk(chunk_size: 100 * 1024, &block)
|
87
|
-
chunk_size > 0 or raise ArgumentError, 'chunk_size > 0 required'
|
88
|
-
|
89
|
-
# Delete any remaining temporary keys if we were interrtupted earlier
|
90
|
-
# (or in some other process.)
|
91
|
-
@redis.scan_each(match: "#{@name}_*") do |key|
|
92
|
-
@redis.del key
|
93
|
-
rescue Redis::BaseConnectionError
|
94
|
-
end
|
95
|
-
|
96
|
-
@redis.exists?(@name) or return Enumerator.new {}
|
97
|
-
|
98
|
-
Enumerator.new do |y|
|
99
|
-
name_tmp = "#{@name}_#{rand}"
|
100
|
-
@redis.rename @name, name_tmp
|
101
|
-
|
102
|
-
s = 0
|
103
|
-
e = @redis.strlen(name_tmp) - 1
|
104
|
-
until s > e
|
105
|
-
range = @redis.getrange(name_tmp, s, s + chunk_size - 1)
|
106
|
-
range.force_encoding 'ASCII-8BIT'
|
107
|
-
y.yield range
|
108
|
-
s += chunk_size
|
109
|
-
end
|
110
|
-
|
111
|
-
ensure
|
112
|
-
begin
|
113
|
-
@redis.del name_tmp
|
114
|
-
rescue Redis::BaseConnectionError
|
115
|
-
# We have to delete this later if del command failed here,
|
116
|
-
# see the beginning of this method.
|
117
|
-
end
|
118
|
-
end.each(&block)
|
119
|
-
|
120
|
-
rescue Redis::BaseConnectionError => e
|
121
|
-
# Maybe it works again later, just log the error…
|
122
|
-
@fallback.error(e)
|
123
|
-
Enumerator.new {}
|
124
|
-
end
|
125
|
-
|
126
|
-
def each(chunk_size: 100 * 1024, &block)
|
127
|
-
chunk_size > 0 or raise ArgumentError, 'chunk_size > 0 required'
|
128
|
-
Enumerator.new do |y|
|
129
|
-
buffer = ''
|
130
|
-
buffer.encode! 'ASCII-8BIT'
|
131
|
-
each_chunk(chunk_size: chunk_size) do |chunk|
|
132
|
-
buffer << chunk
|
133
|
-
buffer.gsub!(/\A(.*?#$/)/n) do |line|
|
134
|
-
y.yield(line)
|
135
|
-
''
|
136
|
-
end
|
137
|
-
end
|
138
|
-
buffer.length > 0 and y.yield(buffer)
|
139
|
-
end.each(&block)
|
140
|
-
end
|
141
|
-
include Enumerable
|
142
|
-
end
|
143
|
-
end
|