postfix-exporter 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: 4b3403736d6ac37fd2733f6db98f566a36e426de634fe971b96a332dd13527dc
4
- data.tar.gz: b20157262dd52237db592744c80713c260ea6eb224e2feb5eac7a60d5665ca40
2
+ SHA1:
3
+ metadata.gz: c3d12e780c74fb42d02f4760e59b04b92d2697f5
4
+ data.tar.gz: 01f82ea6eacc6058db15b568e04494ad6bbdbf96
5
5
  SHA512:
6
- metadata.gz: 8d99493844dfb43b7b15a3742430f50db2c70ec3506b0c1bd93557a4ca4aca45d22c4eca5a95010571eac486519740858e415f9bae2329ff2a630962056bd10d
7
- data.tar.gz: a10120c7f4b0b7a31f36b4fd215bb44ea14868e8281491c473e26812e8b2cfbad0352c87666437087875fb3e095c4036e8b0316461ae82b0077567e938040846
6
+ metadata.gz: 0be25eb7453abc769958db68daddddc64997cfd5d8a0ce5dd46f94b58f48b1c9bccf3c22a4e91c53f6e35f3b34e5f0f2f9cc2969073e43f7fd342b8647bb756b
7
+ data.tar.gz: 7178052ca7f931d8194a26d187e7d24541712e07f9f085f6d5e8d82ac642087f6009adf6786bb35bf6fad0d20fdf3a077dc149152954d55709700e2c76ef2a6f
data/Dockerfile ADDED
@@ -0,0 +1,9 @@
1
+ FROM ruby:2.3-alpine
2
+
3
+ RUN apk update \
4
+ && apk add build-base \
5
+ && gem install postfix-exporter \
6
+ && apk del build-base \
7
+ && rm -f /var/cache/apk/*
8
+
9
+ ENTRYPOINT ["/usr/local/bundle/bin/postgres-exporter"]
data/README.md CHANGED
@@ -7,18 +7,8 @@ server.
7
7
  * Examines mail queue periodically and exports `postfix_mail_queue_size`;
8
8
 
9
9
  * Reads syslog entries as they happen, and exports disposition status
10
- counters (`postfix_delivery_delays_count`) per DSN, as well as delay summaries per
11
- DSN (`postfix_delivery_delays{quantile="..."}`);
12
-
13
- * Total number of SMTP connections (`postfix_smtpd_connections_total`) and
14
- currently-active connections (`postfix_smtpd_active_connections`);
15
-
16
- * Count how many delivery attempts were received
17
- (`postfix_incoming_delivery_attempts_total`), split out by whether we
18
- accepted or rejected the message (`status`) and the exact DSN provided to
19
- the client (`dsn`);
20
-
21
- * Whether or not the Postfix `master` process is running (`postfix_up`);
10
+ counters (`postfix_disposition`) per DSN, as well as delay summaries per
11
+ DSN;
22
12
 
23
13
  * Drinks from the syslog stream directly.
24
14
 
@@ -31,11 +21,6 @@ available in the container as `/var/spool/postfix`), and some env
31
21
  vars. You can also run it directly (via the gem), with the same env vars,
32
22
  and with the expectation that `/var/spool/postfix` is in the usual place.
33
23
 
34
- If you configure the postfix-exporter to run in the same PID namespace as
35
- whatever it is that's running Postfix itself (either `--pid=host` or in the
36
- same NS namespace, a la k8s pods), then the `postfix_up` metric will be
37
- valid, otherwise it'll be random (but unlikely to be correct).
38
-
39
24
 
40
25
  ## Environment Variables
41
26
 
data/bin/postfix-exporter CHANGED
@@ -1,19 +1,15 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'rack'
4
- require 'prometheus/middleware/exporter'
3
+ require 'prometheus/client/rack/exporter'
5
4
  require 'socket'
5
+ require 'docker'
6
+ require 'rack'
6
7
  require 'rack/handler/webrick'
7
8
  require 'logger'
8
9
 
9
10
  prometheus = Prometheus::Client.registry
10
11
 
11
- prometheus.gauge(:postfix_exporter_start_time_seconds, "When this process started up").set({}, Time.now.to_f)
12
-
13
- oldest = prometheus.gauge(:postfix_oldest_message_timestamp_seconds, "Queue time of the oldest message")
14
- mailq = prometheus.gauge(:postfix_queue_size, "Number of messages in the mail queue")
15
- q_err = prometheus.counter(:postfix_queue_processing_error_total, "Exceptions raised whilst scanning the Postfix queue")
16
- up = prometheus.gauge(:postfix_up, "Whether the master process is running or not")
12
+ mailq = prometheus.gauge(:postfix_queue_size, "Number of messages in the mail queue")
17
13
 
18
14
  Thread.abort_on_exception = true
19
15
 
@@ -27,84 +23,19 @@ Thread.new do
27
23
  # deferred is special, because it's often hueg it gets sharded into
28
24
  # multiple subdirectories
29
25
  mailq.set({ queue: 'deferred' }, Dir["/var/spool/postfix/deferred/*/*"].size)
30
- rescue StandardError => ex
31
- $stderr.puts "Error while monitoring queue sizes: #{ex.message} (#{ex.class})"
32
- $stderr.puts ex.backtrace.map { |l| " #{l}" }.join("\n")
33
- q_err.increment(class: ex.class.to_s, phase: "scan")
34
- end
35
-
36
- begin
37
- master_pid = File.read("/var/spool/postfix/pid/master.pid").to_i
38
26
 
39
- if master_pid > 1
40
- Process.kill(0, master_pid)
41
- # If we get here, then the process exists, and
42
- # that'll do for our purposes
43
- up.set({}, 1)
44
- else
45
- up.set({}, 0)
46
- end
47
- rescue Errno::ENOENT, Errno::ESRCH, Errno::EACCES
48
- up.set({}, 0)
49
- rescue Errno::EPERM
50
- # Ironically, we don't need to be able to *actually*
51
- # signal the process; EPERM means it exists and is running
52
- # as someone more privileged than us, which is enough
53
- # for our purposes
54
- up.set({}, 1)
27
+ sleep 5
55
28
  rescue StandardError => ex
56
- $stderr.puts "Error while checking master process: #{ex.message} (#{ex.class})"
57
- $stderr.puts ex.backtrace.map { |l| " #{l}" }.join("\n")
58
- q_err.increment(class: ex.class.to_s, phase: "up")
59
- end
60
-
61
- sleep 5
62
-
63
- end
64
- end
65
-
66
- Thread.new do
67
- earliest_ctime = ->(glob) do
68
- # There is seemingly no way to unset or remove a gauge metric in the Ruby
69
- # implementation of the prom exporter. As a hack, we return the current
70
- # time in cases where there is nothing to sample.
71
- now = Time.now.to_i
72
-
73
- Dir[glob].lazy.map do |n|
74
- begin
75
- File.stat(n).ctime.to_i
76
- rescue Errno::ENOENT
77
- now
78
- end
79
- end.min || now
80
- end
81
-
82
- loop do
83
- begin
84
- %w{incoming active corrupt hold}.each do |q|
85
- oldest.set({ queue: q }, earliest_ctime["/var/spool/postfix/#{q}/*"])
86
- end
87
- oldest.set({ queue: 'deferred' }, earliest_ctime["/var/spool/postfix/deferred/*/*"])
88
- rescue StandardError => ex
89
- $stderr.puts "Error while sampling message ages: #{ex.message} (#{ex.class})"
29
+ $stderr.puts "Error while monitoring queue sizes: #{ex.message} (#{ex.class})"
90
30
  $stderr.puts ex.backtrace.map { |l| " #{l}" }.join("\n")
91
- q_err.increment(class: ex.class.to_s, phase: "stat")
31
+ sleep 1
92
32
  end
93
-
94
- # stat()ing all the files in a large queue could potentially be quite
95
- # expensive, so we sample this data less frequently.
96
- sleep 60
97
-
98
33
  end
99
34
  end
100
35
 
101
36
  if ENV["SYSLOG_SOCKET"]
102
- delays = prometheus.summary(:postfix_delivery_delays, "Distribution of time taken to deliver (or bounce) messages")
103
- connects = prometheus.counter(:postfix_smtpd_connections_total, "Connections to smtpd")
104
- active = prometheus.gauge(:postfix_smtpd_active_connections, "Current connections to smtpd")
105
- incoming = prometheus.counter(:postfix_incoming_delivery_attempts_total, "Delivery attempts, labelled by dsn and status")
106
- messages = prometheus.counter(:postfix_log_messages_total, "Syslog messages received, labelled by how it was handled")
107
- log_errors = prometheus.counter(:postfix_log_processing_error_total, "Exceptions raised whilst processing log messages")
37
+ delays = prometheus.summary(:postfix_delivery_delays, "Distribution of time taken to deliver (or bounce) messages")
38
+ statuses = prometheus.counter(:postfix_deliveries, "How many messages have been delivered (or bounced)")
108
39
 
109
40
  Thread.new do
110
41
  begin
@@ -118,44 +49,19 @@ if ENV["SYSLOG_SOCKET"]
118
49
  loop do
119
50
  begin
120
51
  msg = s.recvmsg.first
121
- if msg =~ %r{postfix/.* delay=(\d+(\.\d+)?), .* dsn=(\d+\.\d+\.\d+), status=(\w+)}
52
+ if msg =~ %r{postfix/smtp.* delay=(\d+(\.\d+)?), .* dsn=(\d+\.\d+\.\d+), status=(\w+)}
122
53
  delay = $1.to_f
123
54
  dsn = $3
124
55
  status = $4
125
56
 
126
57
  if status == "bounced" or status == "sent"
127
- delays.observe({dsn: dsn, status: status}, delay)
128
- end
129
-
130
- messages.increment(type: "delay")
131
- elsif msg =~ %r{postfix/smtpd\[\d+\]: connect from }
132
- connects.increment({})
133
- active.send(:synchronize) { active.set({}, active.get({}) || 0 + 1) }
134
- messages.increment(type: "connect")
135
- elsif msg =~ %r{postfix/smtpd\[\d+\]: disconnect from }
136
- active.send(:synchronize) do
137
- new = (active.get({}) || 0) - 1
138
- # If we start running mid-stream,
139
- # we might end up seeing more
140
- # disconnects than connections,
141
- # which would be confusing
142
- new = 0 if new < 0
143
- active.set({}, new)
58
+ statuses.increment(dsn: dsn, status: status)
59
+ delays.add({dsn: dsn, status: status}, delay)
144
60
  end
145
- messages.increment(type: "disconnect")
146
- elsif msg =~ %r{postfix/smtpd\[\d+\]: [A-F0-9]+: client=}
147
- incoming.increment(dsn: "2.0.0", status: "queued")
148
- messages.increment(type: "queued")
149
- elsif msg =~ %r{postfix/smtpd\[\d+\]: NOQUEUE: reject: RCPT from \S+: \d{3} (\d+\.\d+\.\d+) }
150
- incoming.increment(dsn: $1, status: "rejected")
151
- messages.increment(type: "noqueue")
152
- else
153
- messages.increment(type: "ignored")
154
61
  end
155
62
  rescue StandardError => ex
156
63
  $stderr.puts "Error while receiving postfix logs: #{ex.message} (#{ex.class})"
157
64
  $stderr.puts ex.backtrace.map { |l| " #{l}" }.join("\n")
158
- log_errors.increment(class: ex.class.to_s)
159
65
  sleep 1
160
66
  end
161
67
  end
@@ -163,21 +69,11 @@ if ENV["SYSLOG_SOCKET"]
163
69
  end
164
70
 
165
71
  app = Rack::Builder.new
166
- app.use Rack::Deflater, if: ->(_, _, _, body) { body.any? && body[0].length > 512 }
167
- app.use Prometheus::Middleware::Exporter
72
+ app.use Prometheus::Client::Rack::Exporter
168
73
  app.run ->(env) { [404, {'Content-Type' => 'text/plain'}, ['NOPE NOPE NOPE NOPE']] }
169
74
 
170
75
  logger = Logger.new($stderr)
171
76
  logger.level = Logger::INFO
172
77
  logger.formatter = proc { |s, t, p, m| "WEBrick: #{m}\n" }
173
78
 
174
- # This is the only way to get the Rack-mediated webrick to listen on both
175
- # INADDR_ANY and IN6ADDR_ANY on libcs that don't support getaddrinfo("*")
176
- # (ie musl-libc). Setting `Host: '*'` barfs on the above-mentioned buggy(?)
177
- # libcs, `Host: '::'` fails on newer rubies (because they use
178
- # setsockopt(V6ONLY) by default), and with RACK_ENV at its default of
179
- # "development", it only listens on localhost. And even *this* only works
180
- # on Rack 2, because before that the non-development default listen address
181
- # was "0.0.0.0"!
182
- ENV['RACK_ENV'] = "none"
183
- Rack::Handler::WEBrick.run app, Port: 9154, Logger: logger, AccessLog: []
79
+ Rack::Handler::WEBrick.run app, Host: '::', Port: 9154, Logger: logger, AccessLog: []
@@ -18,15 +18,15 @@ Gem::Specification.new do |s|
18
18
  s.email = ["matt.palmer@discourse.org"]
19
19
  s.homepage = "https://github.com/discourse/postfix-exporter"
20
20
 
21
- s.files = `git ls-files -z`.split("\0").reject { |f| f =~ /^(G|spec|Dockerfile|Rakefile)/ }
21
+ s.files = `git ls-files -z`.split("\0").reject { |f| f =~ /^(G|spec|Rakefile)/ }
22
22
  s.executables = ["postfix-exporter"]
23
23
 
24
24
  s.required_ruby_version = ">= 2.1.0"
25
25
 
26
- s.add_runtime_dependency 'prometheus-client', '~> 0.7', '< 0.10'
27
- s.add_runtime_dependency 'rack', '~> 2.0'
26
+ s.add_runtime_dependency 'prometheus-client'
27
+ s.add_runtime_dependency 'rack'
28
28
 
29
29
  s.add_development_dependency 'bundler'
30
30
  s.add_development_dependency 'github-release'
31
- s.add_development_dependency 'rake'
31
+ s.add_development_dependency 'rake', '~> 10.4', '>= 10.4.2'
32
32
  end
metadata CHANGED
@@ -1,49 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: postfix-exporter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Palmer
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-07 00:00:00.000000000 Z
11
+ date: 2016-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: prometheus-client
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '0.7'
20
- - - "<"
17
+ - - ">="
21
18
  - !ruby/object:Gem::Version
22
- version: '0.10'
19
+ version: '0'
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: '0.7'
30
- - - "<"
24
+ - - ">="
31
25
  - !ruby/object:Gem::Version
32
- version: '0.10'
26
+ version: '0'
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: rack
35
29
  requirement: !ruby/object:Gem::Requirement
36
30
  requirements:
37
- - - "~>"
31
+ - - ">="
38
32
  - !ruby/object:Gem::Version
39
- version: '2.0'
33
+ version: '0'
40
34
  type: :runtime
41
35
  prerelease: false
42
36
  version_requirements: !ruby/object:Gem::Requirement
43
37
  requirements:
44
- - - "~>"
38
+ - - ">="
45
39
  - !ruby/object:Gem::Version
46
- version: '2.0'
40
+ version: '0'
47
41
  - !ruby/object:Gem::Dependency
48
42
  name: bundler
49
43
  requirement: !ruby/object:Gem::Requirement
@@ -76,17 +70,23 @@ dependencies:
76
70
  name: rake
77
71
  requirement: !ruby/object:Gem::Requirement
78
72
  requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.4'
79
76
  - - ">="
80
77
  - !ruby/object:Gem::Version
81
- version: '0'
78
+ version: 10.4.2
82
79
  type: :development
83
80
  prerelease: false
84
81
  version_requirements: !ruby/object:Gem::Requirement
85
82
  requirements:
83
+ - - "~>"
84
+ - !ruby/object:Gem::Version
85
+ version: '10.4'
86
86
  - - ">="
87
87
  - !ruby/object:Gem::Version
88
- version: '0'
89
- description:
88
+ version: 10.4.2
89
+ description:
90
90
  email:
91
91
  - matt.palmer@discourse.org
92
92
  executables:
@@ -95,13 +95,14 @@ extensions: []
95
95
  extra_rdoc_files: []
96
96
  files:
97
97
  - ".gitignore"
98
+ - Dockerfile
98
99
  - README.md
99
100
  - bin/postfix-exporter
100
101
  - postfix-exporter.gemspec
101
102
  homepage: https://github.com/discourse/postfix-exporter
102
103
  licenses: []
103
104
  metadata: {}
104
- post_install_message:
105
+ post_install_message:
105
106
  rdoc_options: []
106
107
  require_paths:
107
108
  - lib
@@ -116,8 +117,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
116
117
  - !ruby/object:Gem::Version
117
118
  version: '0'
118
119
  requirements: []
119
- rubygems_version: 3.0.3
120
- signing_key:
120
+ rubyforge_project:
121
+ rubygems_version: 2.2.2
122
+ signing_key:
121
123
  specification_version: 4
122
124
  summary: Export Prometheus statistics for a Postfix server
123
125
  test_files: []