postfix-exporter 0.1.0 → 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 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: []